Golang Rewrite
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
.idea/
 | 
			
		||||
opensend
 | 
			
		||||
opensend-arm
 | 
			
		||||
							
								
								
									
										113
									
								
								config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								config.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/pkg/browser"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Create config type to store action type and data
 | 
			
		||||
type Config struct {
 | 
			
		||||
	ActionType string
 | 
			
		||||
	ActionData string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Instantiate and return a new Config struct
 | 
			
		||||
func NewConfig(actionType string, actionData string) *Config {
 | 
			
		||||
	return &Config{ActionType: actionType, ActionData: actionData}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create config file
 | 
			
		||||
func (config *Config) CreateFile(dir string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// 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") }
 | 
			
		||||
	// 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") }
 | 
			
		||||
	// 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") }
 | 
			
		||||
	// Log bytes written
 | 
			
		||||
	log.Info().Str("file", "config.json").Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Collect all required files into given directory
 | 
			
		||||
func (config *Config) CollectFiles(dir string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// If action type is file
 | 
			
		||||
	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") }
 | 
			
		||||
		// 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") }
 | 
			
		||||
		// 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") }
 | 
			
		||||
		// Replace file path in config.ActionData with file name
 | 
			
		||||
		config.ActionData = filepath.Base(config.ActionData)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read config file at given file path
 | 
			
		||||
func (config *Config) ReadFile(filePath string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Read file at filePath
 | 
			
		||||
	fileData, err := ioutil.ReadFile(filePath)
 | 
			
		||||
	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") }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute action specified in config
 | 
			
		||||
func (config *Config) ExecuteAction(srcDir string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// If action is file
 | 
			
		||||
	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") }
 | 
			
		||||
		// Close source file at the end of this function
 | 
			
		||||
		defer src.Close()
 | 
			
		||||
		// Get user's home directory
 | 
			
		||||
		homeDir, err := os.UserHomeDir()
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error getting home directory") }
 | 
			
		||||
		// Create file in user's Downloads directory
 | 
			
		||||
		dst, err := os.Create(homeDir + "/Downloads/" + config.ActionData)
 | 
			
		||||
		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
 | 
			
		||||
	} else if config.ActionType == "url" {
 | 
			
		||||
		// Attempt to open URL in browser
 | 
			
		||||
		err := browser.OpenURL(config.ActionData)
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error opening browser") }
 | 
			
		||||
	// Catchall
 | 
			
		||||
	} else {
 | 
			
		||||
		// Log unknown action type
 | 
			
		||||
		log.Fatal().Msg("Unknown action type " + config.ActionType)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								deviceDiscovery.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								deviceDiscovery.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"github.com/grandcat/zeroconf"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Discover opensend receivers on the network
 | 
			
		||||
func DiscoverReceivers() ([]string, []string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Create zeroconf resolver
 | 
			
		||||
	resolver, err := zeroconf.NewResolver(nil)
 | 
			
		||||
	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
 | 
			
		||||
	var discoveredReceivers []string
 | 
			
		||||
	// Create slice to store IPs of discovered receivers
 | 
			
		||||
	var discoveredReceiverIPs []string
 | 
			
		||||
	// Concurrently run mDNS query
 | 
			
		||||
	go func(results <-chan *zeroconf.ServiceEntry) {
 | 
			
		||||
		// For each entry
 | 
			
		||||
		for entry := range results {
 | 
			
		||||
			// Append hostname to discoveredReceivers
 | 
			
		||||
			discoveredReceivers = append(discoveredReceivers, entry.HostName)
 | 
			
		||||
			// Append IP to discoveredReceiverIPs
 | 
			
		||||
			discoveredReceiverIPs = append(discoveredReceiverIPs, entry.AddrIPv4[0].String())
 | 
			
		||||
		}
 | 
			
		||||
	}(entries)
 | 
			
		||||
 | 
			
		||||
	// Create context with 4 second timeout
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
 | 
			
		||||
	// Cancel context at the end of this function
 | 
			
		||||
	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") }
 | 
			
		||||
 | 
			
		||||
	// Send Done signal to context
 | 
			
		||||
	<-ctx.Done()
 | 
			
		||||
	// Return discovered receiver slices
 | 
			
		||||
	return discoveredReceivers, discoveredReceiverIPs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register opensend zeroconf service on the network
 | 
			
		||||
func RegisterService() func() {
 | 
			
		||||
	// Get computer hostname
 | 
			
		||||
	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") }
 | 
			
		||||
	// Return server.Shutdown() function to allow for shutdown in main()
 | 
			
		||||
	return server.Shutdown
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										127
									
								
								fileCrypto.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								fileCrypto.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/aes"
 | 
			
		||||
	"crypto/cipher"
 | 
			
		||||
	"crypto/md5"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Encrypt given file using the shared key
 | 
			
		||||
func EncryptFile(filePath string, newFilePath string, sharedKey string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Read data from file
 | 
			
		||||
	data, err := ioutil.ReadFile(filePath)
 | 
			
		||||
	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))
 | 
			
		||||
	// Encode md5 hash bytes into hexadecimal
 | 
			
		||||
	hashedKey := hex.EncodeToString(md5Hash.Sum(nil))
 | 
			
		||||
	// Create new AES cipher
 | 
			
		||||
	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") }
 | 
			
		||||
	// 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") }
 | 
			
		||||
	// 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") }
 | 
			
		||||
	// Defer file close
 | 
			
		||||
	defer newFile.Close()
 | 
			
		||||
	// Write ciphertext to new file
 | 
			
		||||
	bytesWritten, err := newFile.Write(ciphertext)
 | 
			
		||||
	// Log bytes written and to which file
 | 
			
		||||
	log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decrypt given file using the shared key
 | 
			
		||||
func DecryptFile(filePath string, newFilePath string, sharedKey string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Read data from file
 | 
			
		||||
	data, err := ioutil.ReadFile(filePath)
 | 
			
		||||
	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))
 | 
			
		||||
	hashedKey := hex.EncodeToString(md5Hash.Sum(nil))
 | 
			
		||||
	// Create new AES cipher
 | 
			
		||||
	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") }
 | 
			
		||||
	// 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") }
 | 
			
		||||
	// Create new file
 | 
			
		||||
	newFile, err := os.Create(newFilePath)
 | 
			
		||||
	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(plaintext)
 | 
			
		||||
	// Log bytes written and to which file
 | 
			
		||||
	log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encrypt files in given directory using shared key
 | 
			
		||||
func EncryptFiles(dir string, sharedKey string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// 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 file is not a directory and is not the key
 | 
			
		||||
		if !info.IsDir() && !strings.Contains(path, "aesKey"){
 | 
			
		||||
			// Encrypt the file using shared key, appending .enc
 | 
			
		||||
			EncryptFile(path, path + ".enc", sharedKey)
 | 
			
		||||
			// Remove unencrypted file
 | 
			
		||||
			err := os.Remove(path)
 | 
			
		||||
			if err != nil {return err}
 | 
			
		||||
		}
 | 
			
		||||
		// Return nil if no error occurs
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil { log.Fatal().Err(err).Msg("Error encrypting files") }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decrypt files in given directory using shared key
 | 
			
		||||
func DecryptFiles(dir string, sharedKey string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// 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 file is not a directory and is encrypted
 | 
			
		||||
		if !info.IsDir() && strings.Contains(path, ".enc") {
 | 
			
		||||
			// Decrypt the file using the shared key, removing .enc
 | 
			
		||||
			DecryptFile(path, strings.TrimSuffix(path, ".enc"), sharedKey)
 | 
			
		||||
		}
 | 
			
		||||
		// Return nil if no errors occurred
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil { log.Fatal().Err(err).Msg("Error decrypting files") }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										166
									
								
								files.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								files.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Save encrypted key to file
 | 
			
		||||
func SaveEncryptedKey(encryptedKey []byte, filePath string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Create file at given file path
 | 
			
		||||
	keyFile, err := os.Create(filePath)
 | 
			
		||||
	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") }
 | 
			
		||||
	// Log bytes written
 | 
			
		||||
	log.Info().Str("file", filepath.Base(filePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create HTTP server to transmit files
 | 
			
		||||
func SendFiles(dir string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Instantiate http.Server struct
 | 
			
		||||
	srv := &http.Server{}
 | 
			
		||||
	// Listen on all ipv4 addresses on port 9898
 | 
			
		||||
	listener, err := net.Listen("tcp4", ":9898")
 | 
			
		||||
	if err != nil { log.Fatal().Err(err).Msg("Error starting listener") }
 | 
			
		||||
 | 
			
		||||
	// If client connects to /:filePath
 | 
			
		||||
	http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		// Set file to first path components of URL, excluding first /
 | 
			
		||||
		file := req.URL.Path[1:]
 | 
			
		||||
		// Read file at specified location
 | 
			
		||||
		fileData, err := ioutil.ReadFile(dir + "/" + file)
 | 
			
		||||
		// If there was an error reading
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			// Warn user of error
 | 
			
		||||
			log.Warn().Err(err).Msg("Error reading file")
 | 
			
		||||
		// Otherwise
 | 
			
		||||
		} else {
 | 
			
		||||
			// Inform user client has requested a file
 | 
			
		||||
			log.Info().Str("file", file).Msg("GET File")
 | 
			
		||||
		}
 | 
			
		||||
		// Write file to ResponseWriter
 | 
			
		||||
		_, err = fmt.Fprint(res, string(fileData))
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error writing response") }
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// If client connects to /index
 | 
			
		||||
	http.HandleFunc("/index", func(res http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		// Inform user a client has requested the file index
 | 
			
		||||
		log.Info().Msg("GET Index")
 | 
			
		||||
		// Get directory listing
 | 
			
		||||
		dirListing, err := ioutil.ReadDir(dir)
 | 
			
		||||
		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
 | 
			
		||||
		for _, file := range dirListing {
 | 
			
		||||
			// If the file is not the key
 | 
			
		||||
			if !strings.Contains(file.Name(), "savedKey.aesKey") {
 | 
			
		||||
				// Append the file path to indexSlice
 | 
			
		||||
				indexSlice = append(indexSlice, dir + "/" + file.Name())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// Join index slice into string
 | 
			
		||||
		indexStr := strings.Join(indexSlice, ";")
 | 
			
		||||
		// Write index to ResponseWriter
 | 
			
		||||
		_, err = fmt.Fprint(res, indexStr)
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error writing response") }
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// If client connects to /key
 | 
			
		||||
	http.HandleFunc("/key", func(res http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		// Inform user a client has requested the key
 | 
			
		||||
		log.Info().Msg("GET Key")
 | 
			
		||||
		// Read saved key
 | 
			
		||||
		key, err := ioutil.ReadFile(dir + "/savedKey.aesKey")
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error reading key") }
 | 
			
		||||
		// Write saved key to ResponseWriter
 | 
			
		||||
		_, err = fmt.Fprint(res, string(key))
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error writing response") }
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// If client connects to /stop
 | 
			
		||||
	http.HandleFunc("/stop", func(res http.ResponseWriter, req *http.Request) {
 | 
			
		||||
		// Inform user a client has requested server shutdown
 | 
			
		||||
		log.Info().Msg("GET Stop")
 | 
			
		||||
		log.Info().Msg("Shutdown signal received")
 | 
			
		||||
		// Shutdown server and send to empty context
 | 
			
		||||
		err := srv.Shutdown(context.TODO())
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error stopping server") }
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Start HTTP Server
 | 
			
		||||
	_ = srv.Serve(listener)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get files from sender
 | 
			
		||||
func RecvFiles(dir string, senderAddr string) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Get server address by getting the IP without the port, prepending http:// and appending :9898
 | 
			
		||||
	serverAddr := "http://" + strings.Split(senderAddr, ":")[0] + ":9898"
 | 
			
		||||
	// GET /index on sender's HTTP server
 | 
			
		||||
	response, err := http.Get(serverAddr + "/index")
 | 
			
		||||
	if err != nil { log.Fatal().Err(err).Msg("Error getting index") }
 | 
			
		||||
	// Close response body at the end of this function
 | 
			
		||||
	defer response.Body.Close()
 | 
			
		||||
	// Create index slice for storage of file index
 | 
			
		||||
	var index []string
 | 
			
		||||
	// If server responded with 200 OK
 | 
			
		||||
	if response.StatusCode == http.StatusOK {
 | 
			
		||||
		// Read response body
 | 
			
		||||
		body, err := ioutil.ReadAll(response.Body)
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error reading HTTP response") }
 | 
			
		||||
		// Get string from body
 | 
			
		||||
		bodyStr := string(body)
 | 
			
		||||
		// Split string to form index
 | 
			
		||||
		index = strings.Split(bodyStr, ";")
 | 
			
		||||
	}
 | 
			
		||||
	// For each file in the index
 | 
			
		||||
	for _, file := range index {
 | 
			
		||||
		// GET current file in index
 | 
			
		||||
		response, err := http.Get(serverAddr + "/" + filepath.Base(file))
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error getting file") }
 | 
			
		||||
		// If server responded with 200 OK
 | 
			
		||||
		if response.StatusCode == http.StatusOK {
 | 
			
		||||
			// Create new file at index filepath
 | 
			
		||||
			newFile, err := os.Create(file)
 | 
			
		||||
			if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
 | 
			
		||||
			// Copy response body to new file
 | 
			
		||||
			bytesWritten, err := io.Copy(newFile, response.Body)
 | 
			
		||||
			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
 | 
			
		||||
			newFile.Close()
 | 
			
		||||
		}
 | 
			
		||||
		// Close response body
 | 
			
		||||
		response.Body.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Send stop signal to sender's HTTP server
 | 
			
		||||
func SendSrvStopSignal(senderAddr string) {
 | 
			
		||||
	// Get server address by getting the IP without the port, prepending http:// and appending :9898
 | 
			
		||||
	serverAddr := "http://" + strings.Split(senderAddr, ":")[0] + ":9898"
 | 
			
		||||
	// GET /stop on sender's HTTP servers ignoring any errors
 | 
			
		||||
	_, _ = http.Get(serverAddr + "/stop")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
module opensend
 | 
			
		||||
 | 
			
		||||
go 1.15
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/grandcat/zeroconf v1.0.0
 | 
			
		||||
	github.com/pkg/browser v0.0.0-20201112035734-206646e67786
 | 
			
		||||
	github.com/rs/zerolog v1.20.0
 | 
			
		||||
)
 | 
			
		||||
							
								
								
									
										36
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
 | 
			
		||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/grandcat/zeroconf v1.0.0 h1:uHhahLBKqwWBV6WZUDAT71044vwOTL+McW0mBJvo6kE=
 | 
			
		||||
github.com/grandcat/zeroconf v1.0.0/go.mod h1:lTKmG1zh86XyCoUeIHSA4FJMBwCJiQmGfcP2PdzytEs=
 | 
			
		||||
github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM=
 | 
			
		||||
github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
 | 
			
		||||
github.com/pkg/browser v0.0.0-20201112035734-206646e67786 h1:4Gk0Dsp90g2YwfsxDOjvkEIgKGh+2R9FlvormRycveA=
 | 
			
		||||
github.com/pkg/browser v0.0.0-20201112035734-206646e67786/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
 | 
			
		||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
			
		||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 | 
			
		||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
 | 
			
		||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
 | 
			
		||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
							
								
								
									
										75
									
								
								keyCrypto.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								keyCrypto.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"crypto/sha256"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Generate RSA keypair
 | 
			
		||||
func GenerateRSAKeypair() (*rsa.PrivateKey, *rsa.PublicKey) {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Generate private/public RSA keypair
 | 
			
		||||
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
 | 
			
		||||
	if err != nil { log.Fatal().Err(err).Msg("Error generating RSA keypair") }
 | 
			
		||||
	// Get public key
 | 
			
		||||
	publicKey := privateKey.PublicKey
 | 
			
		||||
	// Return keypair
 | 
			
		||||
	return privateKey, &publicKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get public key from sender
 | 
			
		||||
func GetKey(senderAddr string) []byte {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Get server address by getting the IP without the port, prepending http:// and appending :9898
 | 
			
		||||
	serverAddr := "http://" + strings.Split(senderAddr, ":")[0] + ":9898"
 | 
			
		||||
	// GET /key on the sender's HTTP server
 | 
			
		||||
	response, err := http.Get(serverAddr + "/key")
 | 
			
		||||
	if err != nil { log.Fatal().Err(err).Msg("Error getting key") }
 | 
			
		||||
	// Close response body at the end of this function
 | 
			
		||||
	defer response.Body.Close()
 | 
			
		||||
	// If server responded with 200 OK
 | 
			
		||||
	if response.StatusCode == http.StatusOK {
 | 
			
		||||
		// Read response body into key
 | 
			
		||||
		key, err := ioutil.ReadAll(response.Body)
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error reading HTTP response") }
 | 
			
		||||
		// Return key
 | 
			
		||||
		return key
 | 
			
		||||
	// Otherwise
 | 
			
		||||
	} else {
 | 
			
		||||
		// Fatally log status code
 | 
			
		||||
		if err != nil { log.Fatal().Int("code", response.StatusCode).Msg("HTTP Error Response Code Received") }
 | 
			
		||||
	}
 | 
			
		||||
	// Return nil if all else fails
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Encrypt shared key with received public key
 | 
			
		||||
func EncryptKey(sharedKey string, recvPubKey *rsa.PublicKey) []byte {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// 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") }
 | 
			
		||||
	// Return encrypted key
 | 
			
		||||
	return encryptedSharedKey
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decrypt shared key using private RSA key
 | 
			
		||||
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") }
 | 
			
		||||
	// Get string of decrypted key
 | 
			
		||||
	sharedKey := string(decryptedKey)
 | 
			
		||||
	// Return shared key
 | 
			
		||||
	return sharedKey
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								keyExchange.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								keyExchange.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"encoding/gob"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Exchange keys with sender
 | 
			
		||||
func ReceiverKeyExchange(key *rsa.PublicKey) string {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// Create TCP listener on port 9797
 | 
			
		||||
	listener, err := net.Listen("tcp", ":9797")
 | 
			
		||||
	if err != nil { log.Fatal().Err(err).Msg("Error starting listener") }
 | 
			
		||||
	// Create string for sender address
 | 
			
		||||
	var senderAddr string
 | 
			
		||||
	// Create channel to send break signal
 | 
			
		||||
	breakChannel := make(chan bool)
 | 
			
		||||
	for {
 | 
			
		||||
		// Accept connection on listener
 | 
			
		||||
		connection, err := listener.Accept()
 | 
			
		||||
		// Get sender address and store it in senderAddr
 | 
			
		||||
		senderAddr = connection.RemoteAddr().String()
 | 
			
		||||
		if err != nil { log.Fatal().Err(err).Msg("Error accepting connections") }
 | 
			
		||||
		// Concurrently handle connection
 | 
			
		||||
		go func(conn net.Conn) {
 | 
			
		||||
			// Create gob encoder with connection as io.Writer
 | 
			
		||||
			encoder := gob.NewEncoder(conn)
 | 
			
		||||
			// Encode key into connection
 | 
			
		||||
			err := encoder.Encode(key)
 | 
			
		||||
			if err != nil { log.Fatal().Err(err).Msg("Error encoding key") }
 | 
			
		||||
			// Send signal to breakChannel
 | 
			
		||||
			breakChannel <- true
 | 
			
		||||
		}(connection)
 | 
			
		||||
		// Wait for break signal
 | 
			
		||||
		select {
 | 
			
		||||
		// When break signal arrives
 | 
			
		||||
		case _ = <-breakChannel:
 | 
			
		||||
			// Return sender address
 | 
			
		||||
			return senderAddr
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Exchange keys with receiver
 | 
			
		||||
func SenderKeyExchange(receiverIP string) *rsa.PublicKey {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
	// 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") }
 | 
			
		||||
	// 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") }
 | 
			
		||||
	// Return received key
 | 
			
		||||
	return recvPubKey
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										137
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/signal"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	// Use ConsoleWriter logger
 | 
			
		||||
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
 | 
			
		||||
 | 
			
		||||
	// Create 32 byte buffer
 | 
			
		||||
	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") }
 | 
			
		||||
	// Encode random bytes to hexadecimal
 | 
			
		||||
	sharedKey := hex.EncodeToString(sharedKeyBytes)
 | 
			
		||||
	// Get user's home directory
 | 
			
		||||
	homeDir, err := os.UserHomeDir()
 | 
			
		||||
	if err != nil { log.Fatal().Err(err).Msg("Error getting home directory") }
 | 
			
		||||
	// Define opensend directory as ~/.opensend
 | 
			
		||||
	opensendDir := homeDir + "/.opensend"
 | 
			
		||||
 | 
			
		||||
	// Create channel for signals
 | 
			
		||||
	sig := make(chan os.Signal, 1)
 | 
			
		||||
	// Send message on channel upon reception of SIGINT or SIGTERM
 | 
			
		||||
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
 | 
			
		||||
	// Intercept signal
 | 
			
		||||
	go func() {
 | 
			
		||||
		select {
 | 
			
		||||
		// Wait for sig to be written
 | 
			
		||||
		case <-sig:
 | 
			
		||||
			// Warn user that a signal has been received and that opensend is shutting down
 | 
			
		||||
			log.Warn().Msg("Signal received. Shutting down.")
 | 
			
		||||
			// Remove opensend directory to avoid future conflicts
 | 
			
		||||
			_ = os.RemoveAll(opensendDir)
 | 
			
		||||
			// Exit with code 0
 | 
			
		||||
			os.Exit(0)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// Create -t flag for type
 | 
			
		||||
	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
 | 
			
		||||
	sendFlag := flag.Bool("s", false, "Send data")
 | 
			
		||||
	// Create -r flag for receiving
 | 
			
		||||
	recvFlag := flag.Bool("r", false, "Receive data")
 | 
			
		||||
	// Parse flags
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
 | 
			
		||||
	// Create opensend dir ignoring errors
 | 
			
		||||
	_ = os.Mkdir(opensendDir, 0755)
 | 
			
		||||
	// If -s given
 | 
			
		||||
	if *sendFlag {
 | 
			
		||||
		// 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))
 | 
			
		||||
		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
 | 
			
		||||
		choiceIP := discoveredIPs[choiceIndex]
 | 
			
		||||
		// Exchange RSA keys with receiver
 | 
			
		||||
		rawKey := SenderKeyExchange(choiceIP)
 | 
			
		||||
		// Encrypt shared key using RSA public key
 | 
			
		||||
		key := EncryptKey(sharedKey, rawKey)
 | 
			
		||||
		// Save encrypted key in opensend directory as savedKey.aesKey
 | 
			
		||||
		SaveEncryptedKey(key, opensendDir + "/savedKey.aesKey")
 | 
			
		||||
		// Instantiate Config object
 | 
			
		||||
		config := NewConfig(*actionType, *actionData)
 | 
			
		||||
		// Collect any files that may be required for transaction into opensend directory
 | 
			
		||||
		config.CollectFiles(opensendDir)
 | 
			
		||||
		// Create config file in opensend directory
 | 
			
		||||
		config.CreateFile(opensendDir)
 | 
			
		||||
		// Encrypt all files in opensend directory using shared key
 | 
			
		||||
		EncryptFiles(opensendDir, sharedKey)
 | 
			
		||||
		// Send all files in opensend directory using an HTTP server on port 9898
 | 
			
		||||
		SendFiles(opensendDir)
 | 
			
		||||
	// If -r given
 | 
			
		||||
	} else if *recvFlag {
 | 
			
		||||
		// Register {hostname}._opensend._tcp.local. mDNS service and pass shutdown function
 | 
			
		||||
		zeroconfShutdown := RegisterService()
 | 
			
		||||
		// Shutdown zeroconf server at the end of main()
 | 
			
		||||
		defer zeroconfShutdown()
 | 
			
		||||
		// Generate keypair
 | 
			
		||||
		privateKey, publicKey := GenerateRSAKeypair()
 | 
			
		||||
		// Exchange keys with sender
 | 
			
		||||
		senderIP := ReceiverKeyExchange(publicKey)
 | 
			
		||||
		// Sleep 300ms to allow sender time to start HTTP server
 | 
			
		||||
		time.Sleep(300*time.Millisecond)
 | 
			
		||||
		// Get files from sender and place them into the opensend directory
 | 
			
		||||
		RecvFiles(opensendDir, senderIP)
 | 
			
		||||
		// Get encrypted shared key from sender
 | 
			
		||||
		encryptedKey := GetKey(senderIP)
 | 
			
		||||
		// Send stop signal to sender's HTTP server
 | 
			
		||||
		SendSrvStopSignal(senderIP)
 | 
			
		||||
		// Decrypt shared key
 | 
			
		||||
		sharedKey := DecryptKey(encryptedKey, privateKey)
 | 
			
		||||
		// Decrypt all files in opensend directory using shared key
 | 
			
		||||
		DecryptFiles(opensendDir, sharedKey)
 | 
			
		||||
		// Instantiate Config
 | 
			
		||||
		config := &Config{}
 | 
			
		||||
		// Read config file in opensend directory
 | 
			
		||||
		config.ReadFile(opensendDir + "/config.json")
 | 
			
		||||
		// Execute JSON action using files within opensend directory
 | 
			
		||||
		config.ExecuteAction(opensendDir)
 | 
			
		||||
	}
 | 
			
		||||
	// Remove opensend directory
 | 
			
		||||
	err = os.RemoveAll(opensendDir)
 | 
			
		||||
	if err != nil { log.Fatal().Err(err).Msg("Error remove opensend dir") }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user