mirror of https://github.com/schollz/croc.git
add api and divide into relay+server
This commit is contained in:
parent
5ede3186e5
commit
ce8135002c
2
main.go
2
main.go
|
@ -3,7 +3,7 @@ package main
|
||||||
import croc "github.com/schollz/croc/src"
|
import croc "github.com/schollz/croc/src"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
err := croc.RunRelay("8002")
|
err := croc.Relay([]string{"27001", "27002", "27003", "27004"}, "8002")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package croc
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Croc struct {
|
||||||
|
tcpPorts []string
|
||||||
|
serverPort string
|
||||||
|
timeout time.Duration
|
||||||
|
useEncryption bool
|
||||||
|
useCompression bool
|
||||||
|
curveType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init will initialize the croc relay
|
||||||
|
func Init() (c *Croc) {
|
||||||
|
c = new(Croc)
|
||||||
|
c.tcpPorts = []string{"27001", "27002", "27003", "27004"}
|
||||||
|
c.serverPort = "8003"
|
||||||
|
c.timeout = 10 * time.Minute
|
||||||
|
c.useEncryption = true
|
||||||
|
c.useCompression = true
|
||||||
|
c.curveType = "p521"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// OptionTimeout
|
||||||
|
// OptionCurve
|
||||||
|
// OptionUseEncryption
|
||||||
|
// OptionUseCompression
|
||||||
|
|
||||||
|
// Relay initiates a relay
|
||||||
|
func (c *Croc) Relay() error {
|
||||||
|
// start relay
|
||||||
|
go startRelay(c.tcpPorts)
|
||||||
|
|
||||||
|
// start server
|
||||||
|
return startServer(c.tcpPorts, c.serverPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send will take an existing file or folder and send it through the croc relay
|
||||||
|
func (c *Croc) Send(fname string) (err error) {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive will receive something through the croc relay
|
||||||
|
func (c *Croc) Receive() (err error) {
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -1,23 +1,48 @@
|
||||||
package croc
|
package croc
|
||||||
|
|
||||||
import "crypto/elliptic"
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// maximum buffer size for initial TCP communication
|
||||||
|
bufferSize = 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// availableStates are the states available to the parties involved
|
||||||
|
availableStates = []string{"curve", "h_k", "hh_k", "x", "y"}
|
||||||
|
)
|
||||||
|
|
||||||
type channelData struct {
|
type channelData struct {
|
||||||
// public
|
// Public
|
||||||
|
// Name is the name of the channel
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
State map[string][]byte
|
// State contains state variables that are public to both parties
|
||||||
|
State map[string][]byte `json:"state"`
|
||||||
|
// TransferReady is set by the relaying when both parties have connected
|
||||||
|
// with their credentials
|
||||||
|
TransferReady bool `json:"transfer_ready"`
|
||||||
|
// Ports returns which TCP ports to connect to
|
||||||
|
Ports []string `json:"ports"`
|
||||||
|
|
||||||
// private
|
// Private
|
||||||
stapled bool
|
// isopen determine whether or not the channel has been opened
|
||||||
|
isopen bool
|
||||||
|
// store a UUID of the parties to prevent other parties from joining
|
||||||
uuids [2]string // 0 is sender, 1 is recipient
|
uuids [2]string // 0 is sender, 1 is recipient
|
||||||
|
// curve is the type of elliptic curve used for PAKE
|
||||||
curve elliptic.Curve
|
curve elliptic.Curve
|
||||||
|
// connection information is stored when the clients do connect over TCP
|
||||||
|
connection [2]net.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
type response struct {
|
type response struct {
|
||||||
// various responses
|
// various responses
|
||||||
Channel string `json:"channel,omitempty"`
|
Channel string `json:"channel,omitempty"`
|
||||||
UUID string `json:"uuid,omitempty"`
|
UUID string `json:"uuid,omitempty"`
|
||||||
State map[string][]byte `json:"state,omitempty"`
|
Data *channelData `json:"data,omitempty"`
|
||||||
|
|
||||||
// constant responses
|
// constant responses
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
|
@ -25,8 +50,12 @@ type response struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type payloadOpen struct {
|
type payloadOpen struct {
|
||||||
|
// Channel is used to designate the channel of interest
|
||||||
Channel string `json:"channel"`
|
Channel string `json:"channel"`
|
||||||
|
// Role designates which role the person will take;
|
||||||
|
// 0 for sender and 1 for recipient.
|
||||||
Role int `json:"role"`
|
Role int `json:"role"`
|
||||||
|
// Curve is the curve to be used.
|
||||||
Curve string `json:"curve"`
|
Curve string `json:"curve"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,12 +70,8 @@ func newChannelData(name string) (cd *channelData) {
|
||||||
cd = new(channelData)
|
cd = new(channelData)
|
||||||
cd.Name = name
|
cd.Name = name
|
||||||
cd.State = make(map[string][]byte)
|
cd.State = make(map[string][]byte)
|
||||||
cd.State["x"] = []byte{}
|
for _, state := range availableStates {
|
||||||
cd.State["curve"] = []byte{}
|
cd.State[state] = []byte{}
|
||||||
cd.State["y"] = []byte{}
|
}
|
||||||
cd.State["hh_k"] = []byte{}
|
|
||||||
cd.State["sender_ready"] = []byte{0}
|
|
||||||
cd.State["recipient_ready"] = []byte{0}
|
|
||||||
cd.State["is_open"] = []byte{0}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
package croc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/cihub/seelog"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func startRelay(ports []string) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(len(ports))
|
||||||
|
for _, port := range ports {
|
||||||
|
go func(port string, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
log.Debugf("listening on port %s", port)
|
||||||
|
if err := listener(port); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(port, &wg)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func listener(port string) (err error) {
|
||||||
|
server, err := net.Listen("tcp", "0.0.0.0:"+port)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Error listening on :"+port)
|
||||||
|
}
|
||||||
|
defer server.Close()
|
||||||
|
// spawn a new goroutine whenever a client connects
|
||||||
|
for {
|
||||||
|
connection, err := server.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "problem accepting connection")
|
||||||
|
}
|
||||||
|
log.Debugf("client %s connected", connection.RemoteAddr().String())
|
||||||
|
go func(port string, connection net.Conn) {
|
||||||
|
errCommunication := clientCommuncation(port, connection)
|
||||||
|
if errCommunication != nil {
|
||||||
|
log.Warnf("relay-%s: %s", connection.RemoteAddr().String(), errCommunication.Error())
|
||||||
|
}
|
||||||
|
}(port, connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientCommuncation(port string, connection net.Conn) (err error) {
|
||||||
|
var con1, con2 net.Conn
|
||||||
|
|
||||||
|
// get the channel and UUID from the client
|
||||||
|
err = sendMessage("channel and uuid?", connection)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := receiveMessage(connection)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uuid, err := receiveMessage(connection)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("%s connected with channel %s and uuid %s", connection.RemoteAddr().String(), channel, uuid)
|
||||||
|
|
||||||
|
// validate channel and UUID
|
||||||
|
rs.Lock()
|
||||||
|
if _, ok := rs.channel[channel]; !ok {
|
||||||
|
rs.Unlock()
|
||||||
|
err = errors.Errorf("channel %s does not exist", channel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if uuid != rs.channel[channel].uuids[0] &&
|
||||||
|
uuid != rs.channel[channel].uuids[1] {
|
||||||
|
rs.Unlock()
|
||||||
|
err = errors.Errorf("uuid '%s' is invalid", uuid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
role := 0
|
||||||
|
if uuid == rs.channel[channel].uuids[1] {
|
||||||
|
role = 1
|
||||||
|
}
|
||||||
|
rs.channel[channel].connection[role] = connection
|
||||||
|
|
||||||
|
con1 = rs.channel[channel].connection[0]
|
||||||
|
con2 = rs.channel[channel].connection[1]
|
||||||
|
rs.Unlock()
|
||||||
|
|
||||||
|
if con1 != nil && con2 != nil {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(2)
|
||||||
|
// first start piping
|
||||||
|
go func(con1 net.Conn, con2 net.Conn, wg *sync.WaitGroup) {
|
||||||
|
pipe(con1, con2)
|
||||||
|
wg.Done()
|
||||||
|
}(con1, con2, &wg)
|
||||||
|
// then set transfer ready
|
||||||
|
go func(channel string, wg *sync.WaitGroup) {
|
||||||
|
// set the channels to ready
|
||||||
|
rs.Lock()
|
||||||
|
rs.channel[channel].TransferReady = true
|
||||||
|
rs.Unlock()
|
||||||
|
wg.Done()
|
||||||
|
}(channel, &wg)
|
||||||
|
wg.Wait()
|
||||||
|
log.Debugf("finished transfer")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendMessage(message string, connection net.Conn) (err error) {
|
||||||
|
message = fillString(message, bufferSize)
|
||||||
|
_, err = connection.Write([]byte(message))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveMessage(connection net.Conn) (s string, err error) {
|
||||||
|
messageByte := make([]byte, bufferSize)
|
||||||
|
err = connection.SetReadDeadline(time.Now().Add(60 * time.Minute))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = connection.SetDeadline(time.Now().Add(60 * time.Minute))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = connection.SetWriteDeadline(time.Now().Add(60 * time.Minute))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = connection.Read(messageByte)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s = strings.TrimRight(string(messageByte), ":")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillString(returnString string, toLength int) string {
|
||||||
|
for {
|
||||||
|
lengthString := len(returnString)
|
||||||
|
if lengthString < toLength {
|
||||||
|
returnString = returnString + ":"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return returnString
|
||||||
|
}
|
||||||
|
|
||||||
|
// chanFromConn creates a channel from a Conn object, and sends everything it
|
||||||
|
// Read()s from the socket to the channel.
|
||||||
|
func chanFromConn(conn net.Conn) chan []byte {
|
||||||
|
c := make(chan []byte)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
b := make([]byte, bufferSize)
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := conn.Read(b)
|
||||||
|
if n > 0 {
|
||||||
|
res := make([]byte, n)
|
||||||
|
// Copy the buffer so it doesn't get changed while read by the recipient.
|
||||||
|
copy(res, b[:n])
|
||||||
|
c <- res
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
c <- nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// pipe creates a full-duplex pipe between the two sockets and
|
||||||
|
// transfers data from one to the other.
|
||||||
|
func pipe(conn1 net.Conn, conn2 net.Conn) {
|
||||||
|
chan1 := chanFromConn(conn1)
|
||||||
|
chan2 := chanFromConn(conn2)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case b1 := <-chan1:
|
||||||
|
if b1 == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn2.Write(b1)
|
||||||
|
|
||||||
|
case b2 := <-chan2:
|
||||||
|
if b2 == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
conn1.Write(b2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package croc
|
package croc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -27,17 +26,8 @@ func init() {
|
||||||
rs.Unlock()
|
rs.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
func startServer(tcpPorts []string, port string) (err error) {
|
||||||
state_curve = "curve"
|
// start server
|
||||||
state_hh_k = "hh_k"
|
|
||||||
state_is_open = "is_open"
|
|
||||||
state_recipient_ready = "recipient_ready"
|
|
||||||
state_sender_ready = "sender_ready"
|
|
||||||
state_x = "x"
|
|
||||||
state_y = "y"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RunRelay(port string) (err error) {
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
r.Use(middleWareHandler(), gin.Recovery())
|
r.Use(middleWareHandler(), gin.Recovery())
|
||||||
|
@ -88,15 +78,13 @@ func RunRelay(port string) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the current state
|
// return the current state
|
||||||
r.State = make(map[string][]byte)
|
r.Data = rs.channel[p.Channel]
|
||||||
for key := range rs.channel[p.Channel].State {
|
|
||||||
r.State[key] = rs.channel[p.Channel].State[key]
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Message = fmt.Sprintf("assigned %d keys: %v", len(assignedKeys), assignedKeys)
|
r.Message = fmt.Sprintf("assigned %d keys: %v", len(assignedKeys), assignedKeys)
|
||||||
return
|
return
|
||||||
}(c)
|
}(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debugf("bad /channel: %s", err.Error())
|
||||||
r.Message = err.Error()
|
r.Message = err.Error()
|
||||||
r.Success = false
|
r.Success = false
|
||||||
}
|
}
|
||||||
|
@ -132,7 +120,7 @@ func RunRelay(port string) (err error) {
|
||||||
if _, ok := rs.channel[p.Channel]; ok {
|
if _, ok := rs.channel[p.Channel]; ok {
|
||||||
// channel is not empty
|
// channel is not empty
|
||||||
if rs.channel[p.Channel].uuids[p.Role] != "" {
|
if rs.channel[p.Channel].uuids[p.Role] != "" {
|
||||||
err = errors.Errorf("channel is already occupied by role %d", p.Role)
|
err = errors.Errorf("channel '%s' already occupied by role %d", p.Channel, p.Role)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,9 +132,10 @@ func RunRelay(port string) (err error) {
|
||||||
// assign UUID for the role in the channel
|
// assign UUID for the role in the channel
|
||||||
rs.channel[r.Channel].uuids[p.Role] = uuid4.New().String()
|
rs.channel[r.Channel].uuids[p.Role] = uuid4.New().String()
|
||||||
r.UUID = rs.channel[r.Channel].uuids[p.Role]
|
r.UUID = rs.channel[r.Channel].uuids[p.Role]
|
||||||
|
log.Debugf("(%s) %s has joined as role %d", r.Channel, r.UUID, p.Role)
|
||||||
|
|
||||||
// if channel is not open, determine curve
|
// if channel is not open, set curve
|
||||||
if bytes.Equal(rs.channel[r.Channel].State[state_is_open], []byte{0}) {
|
if !rs.channel[r.Channel].isopen {
|
||||||
switch curve := p.Curve; curve {
|
switch curve := p.Curve; curve {
|
||||||
case "p224":
|
case "p224":
|
||||||
rs.channel[r.Channel].curve = elliptic.P224()
|
rs.channel[r.Channel].curve = elliptic.P224()
|
||||||
|
@ -162,14 +151,17 @@ func RunRelay(port string) (err error) {
|
||||||
p.Curve = "p256"
|
p.Curve = "p256"
|
||||||
rs.channel[r.Channel].curve = elliptic.P256()
|
rs.channel[r.Channel].curve = elliptic.P256()
|
||||||
}
|
}
|
||||||
rs.channel[r.Channel].State[state_curve] = []byte(p.Curve)
|
log.Debugf("(%s) using curve '%s'", r.Channel, p.Curve)
|
||||||
rs.channel[r.Channel].State[state_is_open] = []byte{1}
|
rs.channel[r.Channel].State["curve"] = []byte(p.Curve)
|
||||||
|
rs.channel[r.Channel].Ports = tcpPorts
|
||||||
|
rs.channel[r.Channel].isopen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Message = fmt.Sprintf("assigned role %d in channel '%s'", p.Role, r.Channel)
|
r.Message = fmt.Sprintf("assigned role %d in channel '%s'", p.Role, r.Channel)
|
||||||
return
|
return
|
||||||
}(c)
|
}(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debugf("bad /join: %s", err.Error())
|
||||||
r.Message = err.Error()
|
r.Message = err.Error()
|
||||||
r.Success = false
|
r.Success = false
|
||||||
}
|
}
|
||||||
|
@ -184,8 +176,6 @@ func RunRelay(port string) (err error) {
|
||||||
func middleWareHandler() gin.HandlerFunc {
|
func middleWareHandler() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
// Add base headers
|
|
||||||
// addCORS(c)
|
|
||||||
// Run next function
|
// Run next function
|
||||||
c.Next()
|
c.Next()
|
||||||
// Log request
|
// Log request
|
||||||
|
|
Loading…
Reference in New Issue