Skip to content

Commit

Permalink
HTML is safely escaped in CMsgMsg messages
Browse files Browse the repository at this point in the history
Also includes a refactor of a good portion of message handling code. The
message server and location server both take the same approach to
processing messages. This has revealed striking symmetries between the
two servers which must be dealt with in the future.
  • Loading branch information
fmstephe committed Jun 22, 2012
1 parent 00ef141 commit 5fd32ff
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 76 deletions.
10 changes: 5 additions & 5 deletions exec/example/example.go
Expand Up @@ -3,15 +3,15 @@ package main
import ( import (
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"encoding/json" "encoding/json"
"location_server/locserver" "flag"
"location_server/msgserver" "fmt"
"github.com/fmstephe/simpleid" "github.com/fmstephe/simpleid"
"location_server/locserver"
"location_server/logutil" "location_server/logutil"
"location_server/msgserver"
"location_server/msgutil/msgdef" "location_server/msgutil/msgdef"
"net/http" "net/http"
"os" "os"
"flag"
"fmt"
) )


var port = flag.Int("port", 80, "Sets the port the server will attach to") var port = flag.Int("port", 80, "Sets the port the server will attach to")
Expand All @@ -22,7 +22,7 @@ var idMaker = simpleid.NewIdMaker()
func idProvider(w http.ResponseWriter, r *http.Request) { func idProvider(w http.ResponseWriter, r *http.Request) {
id := idMaker.NewId() id := idMaker.NewId()
idMsg := msgdef.SIdMsg{Op: msgdef.SIdOp, Id: id} idMsg := msgdef.SIdMsg{Op: msgdef.SIdOp, Id: id}
if buf, err := json.MarshalForHTML(idMsg); err != nil { if buf, err := json.Marshal(idMsg); err != nil {
println(err.Error()) println(err.Error())
} else { } else {
w.Write(buf) w.Write(buf)
Expand Down
54 changes: 19 additions & 35 deletions locserver/connmanager.go
Expand Up @@ -2,22 +2,22 @@ package locserver


import ( import (
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"encoding/json"
"errors" "errors"
"github.com/fmstephe/simpleid" "github.com/fmstephe/simpleid"
"location_server/logutil" "location_server/logutil"
"location_server/msgutil/jsonutil"
"location_server/msgutil/msgdef" "location_server/msgutil/msgdef"
"location_server/user" "location_server/user"
) )


var iOpErr = errors.New("Illegal Message Op. Operation unrecognised or provided in illegal order.") var iOpErr = errors.New("Illegal Message Op. Operation unrecognised or provided in illegal order.")
var idSet = simpleid.NewIdMap() var idMap = simpleid.NewIdMap()


// Represents a task for the tree manager. // Represents a task for the tree manager.
type task struct { type task struct {
tId uint // The transaction id for this task tId uint // The transaction id for this task
op msgdef.ClientOp // The operation to perform for this task op msgdef.ClientOp // The operation to perform for this task
usr *user.U // The state of the user for this task usr *user.U // The state of the user for this task
} }


// Safely creates a new task struct, in particular duplicating usr // Safely creates a new task struct, in particular duplicating usr
Expand All @@ -27,7 +27,7 @@ func newTask(tId uint, op msgdef.ClientOp, usr *user.U) *task {


// This is the websocket connection handling function // This is the websocket connection handling function
// The following messages are required in this order // The following messages are required in this order
// 1: User registration message (user id added to idSet) // 1: User registration message (user id added to idMap)
// 2: Initial location message // 2: Initial location message
// 3: Move message // 3: Move message
// //
Expand All @@ -37,27 +37,22 @@ func newTask(tId uint, op msgdef.ClientOp, usr *user.U) *task {
// Any error will result in these actions // Any error will result in these actions
// 1: The user will be sent a server-error message // 1: The user will be sent a server-error message
// 2: The connection will be closed // 2: The connection will be closed
// 3: The user id will be removed from the idSet // 3: The user id will be removed from the idMap
// 4: The user will be removed from the treemanager // 4: The user will be removed from the treemanager
func HandleLocationService(ws *websocket.Conn) { func HandleLocationService(ws *websocket.Conn) {
var tId uint var tId uint
usr := user.New(ws) usr := user.New(ws)
idMsg := &msgdef.CIdMsg{} idMsg := &msgdef.CIdMsg{}
procReg := processReg(idMsg, usr) procReg := processReg(tId, idMsg, usr)
if err := unmarshalAndProcess(tId, usr.Id, ws, idMsg, procReg); err != nil { if err := jsonutil.UnmarshalAndProcess(tId, usr.Id, ws, idMsg, procReg); err != nil {
usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error()) usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error())
return return
} }
if err := idSet.Add(usr.Id, usr); err != nil {
usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error())
return
}
logutil.Registered(tId, usr.Id)
defer removeId(&tId, usr) defer removeId(&tId, usr)
tId++ tId++
initLocMsg := msgdef.EmptyCLocMsg() initLocMsg := msgdef.EmptyCLocMsg()
procInit := processInitLoc(tId, initLocMsg, usr) procInit := processInitLoc(tId, initLocMsg, usr)
if err := unmarshalAndProcess(tId, usr.Id, ws, initLocMsg, procInit); err != nil { if err := jsonutil.UnmarshalAndProcess(tId, usr.Id, ws, initLocMsg, procInit); err != nil {
usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error()) usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error())
return return
} }
Expand All @@ -66,33 +61,18 @@ func HandleLocationService(ws *websocket.Conn) {
tId++ tId++
locMsg := msgdef.EmptyCLocMsg() locMsg := msgdef.EmptyCLocMsg()
procReq := processMove(tId, locMsg, usr) procReq := processMove(tId, locMsg, usr)
if err := unmarshalAndProcess(tId, usr.Id, ws, locMsg, procReq); err != nil { if err := jsonutil.UnmarshalAndProcess(tId, usr.Id, ws, locMsg, procReq); err != nil {
usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error()) usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error())
return return
} }
} }
} }


// Unmarshals a message as a string from the websocket connection // Removes this user's id from idMap and logs the action
// Unmarshals that string into msg
// Calls processFunc provided for arbitrary handling
func unmarshalAndProcess(tId uint, uId string, ws *websocket.Conn, msg interface{}, processFunc func() error) error {
var data string
if err := websocket.Message.Receive(ws, &data); err != nil {
return err
}
logutil.Log(tId, uId, data)
if err := json.Unmarshal([]byte(data), msg); err != nil {
return err
}
return processFunc()
}

// Removes this user's id from idSet and logs the action
func removeId(tId *uint, usr *user.U) { func removeId(tId *uint, usr *user.U) {
(*tId)++ (*tId)++
logutil.Deregistered(*tId, usr.Id) logutil.Deregistered(*tId, usr.Id)
idSet.Remove(usr.Id) idMap.Remove(usr.Id)
} }


// Sends a remove message to the tree manager // Sends a remove message to the tree manager
Expand All @@ -104,15 +84,19 @@ func removeFromTree(tId *uint, usr *user.U) {


// Handle registration message // Handle registration message
// Success will leave usr with initialised Id field // Success will leave usr with initialised Id field
func processReg(idMsg *msgdef.CIdMsg, usr *user.U) func() error { func processReg(tId uint, idMsg *msgdef.CIdMsg, usr *user.U) func() error {
return func() error { return func() error {
if idMsg.Op != msgdef.CAddOp {
return errors.New("Incorrect op-code for id registration: " + string(idMsg.Op))
}
if err := idMsg.Validate(); err != nil { if err := idMsg.Validate(); err != nil {
return err return err
} }
if idMsg.Op != msgdef.CAddOp {
return iOpErr
}
usr.Id = idMsg.Id usr.Id = idMsg.Id
if err := idMap.Add(usr.Id, usr); err != nil {
return err
}
logutil.Registered(tId, usr.Id)
return nil return nil
} }
} }
Expand Down
55 changes: 32 additions & 23 deletions msgserver/msgserver.go
Expand Up @@ -17,35 +17,52 @@ func HandleMessageService(ws *websocket.Conn) {
var tId uint var tId uint
usr := user.New(ws) usr := user.New(ws)
idMsg := &msgdef.CIdMsg{} idMsg := &msgdef.CIdMsg{}
if err := jsonutil.JSONCodec.Receive(ws, idMsg); err != nil { procReg := processReg(tId, idMsg, usr)
if err := jsonutil.UnmarshalAndProcess(tId, usr.Id, ws, idMsg, procReg); err != nil {
usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error()) usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error())
return return
} }
if err := idMsg.Validate(); err != nil {
usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error())
return
}
processReg(idMsg, usr)
if err := idMap.Add(usr.Id, usr); err != nil {
usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error())
return
}
logutil.Registered(tId, usr.Id)
defer removeUser(&tId, usr.Id) defer removeUser(&tId, usr.Id)
for { for {
tId++ tId++
msg := &msgdef.CMsgMsg{} msg := &msgdef.CMsgMsg{}
if err := jsonutil.JSONCodec.Receive(ws, msg); err != nil { procMsg := processMsg(tId, msg, usr)
if err := jsonutil.UnmarshalAndProcess(tId, usr.Id, ws, msg, procMsg); err != nil {
usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error()) usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error())
return return
} }
}
}

func processReg(tId uint, idMsg *msgdef.CIdMsg, usr *user.U) func() error {
return func() error {
if idMsg.Op != msgdef.CAddOp {
return errors.New("Incorrect op-code for id registration: " + string(idMsg.Op))
}
if err := idMsg.Validate(); err != nil {
return err
}
usr.Id = idMsg.Id
if err := idMap.Add(usr.Id, usr); err != nil {
return err
}
logutil.Registered(tId, usr.Id)
return nil
}
}

func processMsg(tId uint, msg *msgdef.CMsgMsg, usr *user.U) func() error {
return func() error {
if msg.Op != msgdef.CMsgOp {
return errors.New("Incorrect op-code for msg: " + string(msg.Op))
}
if err := msg.Validate(); err != nil { if err := msg.Validate(); err != nil {
usr.MsgWriter.ErrorAndClose(tId, usr.Id, err.Error()) return err
return
} }
if idMap.Contains(msg.To) { if idMap.Contains(msg.To) {
forUser := idMap.Get(msg.To).(*user.U) forUser := idMap.Get(msg.To).(*user.U)
msgMsg := &msgdef.SMsgMsg{Op: msgdef.SMsgOp, From: usr.Id, Content: msg.Content} safeContent := jsonutil.SanitiseJSON(msg.Content)
msgMsg := &msgdef.SMsgMsg{Op: msgdef.SMsgOp, From: usr.Id, Content: safeContent}
sMsg := &msgdef.ServerMsg{Msg: msgMsg, TId: tId, UId: usr.Id} sMsg := &msgdef.ServerMsg{Msg: msgMsg, TId: tId, UId: usr.Id}
forUser.MsgWriter.WriteMsg(sMsg) forUser.MsgWriter.WriteMsg(sMsg)
logutil.Log(tId, usr.Id, fmt.Sprintf("Content: '%s' sent to: '%s'", msg.Content, msg.To)) logutil.Log(tId, usr.Id, fmt.Sprintf("Content: '%s' sent to: '%s'", msg.Content, msg.To))
Expand All @@ -54,16 +71,8 @@ func HandleMessageService(ws *websocket.Conn) {
sMsg := &msgdef.ServerMsg{Msg: nuMsg, TId: tId, UId: usr.Id} sMsg := &msgdef.ServerMsg{Msg: nuMsg, TId: tId, UId: usr.Id}
usr.MsgWriter.WriteMsg(sMsg) usr.MsgWriter.WriteMsg(sMsg)
} }
}
}

func processReg(idMsg *msgdef.CIdMsg, usr *user.U) error {
switch idMsg.Op {
case msgdef.CAddOp:
usr.Id = idMsg.Id
return nil return nil
} }
return errors.New("Incorrect op-code for id registration: " + string(idMsg.Op))
} }


func removeUser(tId *uint, uId string) { func removeUser(tId *uint, uId string) {
Expand Down
48 changes: 42 additions & 6 deletions msgutil/jsonutil/jsonutil.go
Expand Up @@ -3,15 +3,51 @@ package jsonutil
import ( import (
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"encoding/json" "encoding/json"
"html"
"location_server/logutil"
) )


func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { // Unmarshals a websocket message into msgi as JSON.
msg, err = json.MarshalForHTML(v) func UnmarshalAndLog(tId uint, uId string, ws *websocket.Conn, msg interface{}) error {
return msg, websocket.TextFrame, err var data string
if err := websocket.Message.Receive(ws, &data); err != nil {
return err
}
logutil.Log(tId, uId, data)
if err := json.Unmarshal([]byte(data), msg); err != nil {
return err
}
return nil
} }


func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { func UnmarshalAndProcess(tId uint, uId string, ws *websocket.Conn, msg interface{}, processFunc func() error) error {
return json.Unmarshal(msg, v) UnmarshalAndLog(tId, uId, ws, msg)
return processFunc()
} }


var JSONCodec = websocket.Codec{jsonMarshal, jsonUnmarshal} //
func SanitiseJSON(v interface{}) interface{} {
if v == nil {
return nil
}
if s, ok := v.(string); ok {
return html.EscapeString(s)
}
sanitiseJSON(v)
return v
}

func sanitiseJSON(parent interface{}) {
switch parent.(type) {
case map[string]interface{}:
parentMap := parent.(map[string]interface{})
for k, child := range parentMap {
switch child.(type) {
case string:
parentMap[k] = html.EscapeString(child.(string))
case map[string]interface{}:
SanitiseJSON(child)
}
}
}
}
12 changes: 6 additions & 6 deletions msgutil/msgdef/msgmsg.go
Expand Up @@ -8,9 +8,9 @@ import (
const CMsgOp = ClientOp("cMsg") const CMsgOp = ClientOp("cMsg")


type CMsgMsg struct { type CMsgMsg struct {
Op ClientOp `json:"op"` Op ClientOp `json:"op"`
To string `json:"to"` To string `json:"to"`
Content interface{} `json:"content"` Content interface{} `json:"content"`
} }


func (msg *CMsgMsg) Validate() error { func (msg *CMsgMsg) Validate() error {
Expand All @@ -30,9 +30,9 @@ func (msg *CMsgMsg) Validate() error {
const SMsgOp = ServerOp("sMsg") const SMsgOp = ServerOp("sMsg")


type SMsgMsg struct { type SMsgMsg struct {
Op ServerOp `json:"op"` Op ServerOp `json:"op"`
From string `json:"from"` From string `json:"from"`
Content interface{} `json:"content"` Content interface{} `json:"content"`
} }


// Indicates that UserId is not registered on the msg_server // Indicates that UserId is not registered on the msg_server
Expand Down
2 changes: 1 addition & 1 deletion user/user.go
@@ -1,8 +1,8 @@
package user package user


import ( import (
"location_server/msgutil/msgwriter"
"code.google.com/p/go.net/websocket" "code.google.com/p/go.net/websocket"
"location_server/msgutil/msgwriter"
) )


// Identifies a user identifed with a lat/lng location currently registered with this service // Identifies a user identifed with a lat/lng location currently registered with this service
Expand Down

0 comments on commit 5fd32ff

Please sign in to comment.