mirror of
https://github.com/schollz/croc.git
synced 2025-10-11 13:21:00 +02:00
first attempt
This commit is contained in:
parent
84f732b662
commit
5ede3186e5
5 changed files with 318 additions and 120 deletions
43
src/logging.go
Normal file
43
src/logging.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package croc
|
||||
|
||||
import (
|
||||
log "github.com/cihub/seelog"
|
||||
)
|
||||
|
||||
// SetLogLevel determines the log level
|
||||
func SetLogLevel(level string) (err error) {
|
||||
|
||||
// https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit
|
||||
// https://github.com/cihub/seelog/wiki/Log-levels
|
||||
appConfig := `
|
||||
<seelog minlevel="` + level + `">
|
||||
<outputs formatid="stdout">
|
||||
<filter levels="debug,trace">
|
||||
<console formatid="debug"/>
|
||||
</filter>
|
||||
<filter levels="info">
|
||||
<console formatid="info"/>
|
||||
</filter>
|
||||
<filter levels="critical,error">
|
||||
<console formatid="error"/>
|
||||
</filter>
|
||||
<filter levels="warn">
|
||||
<console formatid="warn"/>
|
||||
</filter>
|
||||
</outputs>
|
||||
<formats>
|
||||
<format id="stdout" format="%Date %Time [%LEVEL] %File %FuncShort:%Line %Msg %n" />
|
||||
<format id="debug" format="%Date %Time %EscM(37)[%LEVEL]%EscM(0) %File %FuncShort:%Line %Msg %n" />
|
||||
<format id="info" format="%Date %Time %EscM(36)[%LEVEL]%EscM(0) %File %FuncShort:%Line %Msg %n" />
|
||||
<format id="warn" format="%Date %Time %EscM(33)[%LEVEL]%EscM(0) %File %FuncShort:%Line %Msg %n" />
|
||||
<format id="error" format="%Date %Time %EscM(31)[%LEVEL]%EscM(0) %File %FuncShort:%Line %Msg %n" />
|
||||
</formats>
|
||||
</seelog>
|
||||
`
|
||||
logger, err := log.LoggerFromConfigAsBytes([]byte(appConfig))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.ReplaceLogger(logger)
|
||||
return
|
||||
}
|
|
@ -1 +1,52 @@
|
|||
package croc
|
||||
|
||||
import "crypto/elliptic"
|
||||
|
||||
type channelData struct {
|
||||
// public
|
||||
Name string `json:"name,omitempty"`
|
||||
State map[string][]byte
|
||||
|
||||
// private
|
||||
stapled bool
|
||||
uuids [2]string // 0 is sender, 1 is recipient
|
||||
curve elliptic.Curve
|
||||
}
|
||||
|
||||
type response struct {
|
||||
// various responses
|
||||
Channel string `json:"channel,omitempty"`
|
||||
UUID string `json:"uuid,omitempty"`
|
||||
State map[string][]byte `json:"state,omitempty"`
|
||||
|
||||
// constant responses
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type payloadOpen struct {
|
||||
Channel string `json:"channel"`
|
||||
Role int `json:"role"`
|
||||
Curve string `json:"curve"`
|
||||
}
|
||||
|
||||
type payloadChannel struct {
|
||||
Channel string `json:"channel" binding:"required"`
|
||||
UUID string `json:"uuid" binding:"required"`
|
||||
State map[string][]byte `json:"state"`
|
||||
Close bool `json:"close"`
|
||||
}
|
||||
|
||||
func newChannelData(name string) (cd *channelData) {
|
||||
cd = new(channelData)
|
||||
cd.Name = name
|
||||
cd.State = make(map[string][]byte)
|
||||
cd.State["x"] = []byte{}
|
||||
cd.State["curve"] = []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
|
||||
}
|
||||
|
|
194
src/server.go
Normal file
194
src/server.go
Normal file
|
@ -0,0 +1,194 @@
|
|||
package croc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/elliptic"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/cihub/seelog"
|
||||
"github.com/frankenbeanies/uuid4"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type relayState struct {
|
||||
channel map[string]*channelData
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
var rs relayState
|
||||
|
||||
func init() {
|
||||
rs.Lock()
|
||||
rs.channel = make(map[string]*channelData)
|
||||
rs.Unlock()
|
||||
}
|
||||
|
||||
const (
|
||||
state_curve = "curve"
|
||||
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)
|
||||
r := gin.New()
|
||||
r.Use(middleWareHandler(), gin.Recovery())
|
||||
r.POST("/channel", func(c *gin.Context) {
|
||||
r, err := func(c *gin.Context) (r response, err error) {
|
||||
rs.Lock()
|
||||
defer rs.Unlock()
|
||||
r.Success = true
|
||||
var p payloadChannel
|
||||
err = c.ShouldBindJSON(&p)
|
||||
if err != nil {
|
||||
log.Errorf("failed on payload %+v", p)
|
||||
err = errors.Wrap(err, "problem parsing /channel")
|
||||
return
|
||||
}
|
||||
|
||||
// determine if channel is invalid
|
||||
if _, ok := rs.channel[p.Channel]; !ok {
|
||||
err = errors.Errorf("channel '%s' does not exist", p.Channel)
|
||||
return
|
||||
}
|
||||
|
||||
// determine if UUID is invalid for channel
|
||||
if p.UUID != rs.channel[p.Channel].uuids[0] &&
|
||||
p.UUID != rs.channel[p.Channel].uuids[1] {
|
||||
err = errors.Errorf("uuid '%s' is invalid", p.UUID)
|
||||
return
|
||||
}
|
||||
|
||||
// check if the action is to close the channel
|
||||
if p.Close {
|
||||
delete(rs.channel, p.Channel)
|
||||
r.Message = "deleted " + p.Channel
|
||||
return
|
||||
}
|
||||
|
||||
// assign each key provided
|
||||
assignedKeys := []string{}
|
||||
for key := range p.State {
|
||||
// TODO:
|
||||
// add a check that the value of key is not enormous
|
||||
|
||||
// add only if it is a valid key
|
||||
if _, ok := rs.channel[p.Channel].State[key]; ok {
|
||||
assignedKeys = append(assignedKeys, key)
|
||||
rs.channel[p.Channel].State[key] = p.State[key]
|
||||
}
|
||||
}
|
||||
|
||||
// return the current state
|
||||
r.State = make(map[string][]byte)
|
||||
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)
|
||||
return
|
||||
}(c)
|
||||
if err != nil {
|
||||
r.Message = err.Error()
|
||||
r.Success = false
|
||||
}
|
||||
bR, _ := json.Marshal(r)
|
||||
c.Data(200, "application/json", bR)
|
||||
})
|
||||
r.POST("/join", func(c *gin.Context) {
|
||||
r, err := func(c *gin.Context) (r response, err error) {
|
||||
rs.Lock()
|
||||
defer rs.Unlock()
|
||||
r.Success = true
|
||||
|
||||
var p payloadOpen
|
||||
err = c.ShouldBindJSON(&p)
|
||||
if err != nil {
|
||||
log.Errorf("failed on payload %+v", p)
|
||||
err = errors.Wrap(err, "problem parsing")
|
||||
return
|
||||
}
|
||||
|
||||
// determine if sender or recipient
|
||||
if p.Role != 0 && p.Role != 1 {
|
||||
err = errors.Errorf("no such role of %d", p.Role)
|
||||
return
|
||||
}
|
||||
|
||||
// determine channel
|
||||
if p.Channel == "" {
|
||||
// TODO:
|
||||
// find an empty channel
|
||||
p.Channel = "chou"
|
||||
}
|
||||
if _, ok := rs.channel[p.Channel]; ok {
|
||||
// channel is not empty
|
||||
if rs.channel[p.Channel].uuids[p.Role] != "" {
|
||||
err = errors.Errorf("channel is already occupied by role %d", p.Role)
|
||||
return
|
||||
}
|
||||
}
|
||||
r.Channel = p.Channel
|
||||
if _, ok := rs.channel[r.Channel]; !ok {
|
||||
rs.channel[r.Channel] = newChannelData(r.Channel)
|
||||
}
|
||||
|
||||
// assign UUID for the role in the channel
|
||||
rs.channel[r.Channel].uuids[p.Role] = uuid4.New().String()
|
||||
r.UUID = rs.channel[r.Channel].uuids[p.Role]
|
||||
|
||||
// if channel is not open, determine curve
|
||||
if bytes.Equal(rs.channel[r.Channel].State[state_is_open], []byte{0}) {
|
||||
switch curve := p.Curve; curve {
|
||||
case "p224":
|
||||
rs.channel[r.Channel].curve = elliptic.P224()
|
||||
case "p256":
|
||||
rs.channel[r.Channel].curve = elliptic.P256()
|
||||
case "p384":
|
||||
rs.channel[r.Channel].curve = elliptic.P384()
|
||||
case "p521":
|
||||
rs.channel[r.Channel].curve = elliptic.P521()
|
||||
default:
|
||||
// TODO:
|
||||
// add SIEC
|
||||
p.Curve = "p256"
|
||||
rs.channel[r.Channel].curve = elliptic.P256()
|
||||
}
|
||||
rs.channel[r.Channel].State[state_curve] = []byte(p.Curve)
|
||||
rs.channel[r.Channel].State[state_is_open] = []byte{1}
|
||||
}
|
||||
|
||||
r.Message = fmt.Sprintf("assigned role %d in channel '%s'", p.Role, r.Channel)
|
||||
return
|
||||
}(c)
|
||||
if err != nil {
|
||||
r.Message = err.Error()
|
||||
r.Success = false
|
||||
}
|
||||
bR, _ := json.Marshal(r)
|
||||
c.Data(200, "application/json", bR)
|
||||
})
|
||||
log.Infof("Running at http://0.0.0.0:" + port)
|
||||
err = r.Run(":" + port)
|
||||
return
|
||||
}
|
||||
|
||||
func middleWareHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
t := time.Now()
|
||||
// Add base headers
|
||||
// addCORS(c)
|
||||
// Run next function
|
||||
c.Next()
|
||||
// Log request
|
||||
log.Infof("%v %v %v %s", c.Request.RemoteAddr, c.Request.Method, c.Request.URL, time.Since(t))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue