Skip to content

Commit 8cbb879

Browse files
Thread safety (#12)
* begin thread safety * dockerfile for debugging * rename
1 parent def1529 commit 8cbb879

File tree

2 files changed

+108
-13
lines changed

2 files changed

+108
-13
lines changed

docker-compose.yaml

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
version: '3.3'
2+
services:
3+
ssh-sync-server:
4+
restart: always
5+
environment:
6+
- PORT=3000
7+
- NO_DOTENV=1
8+
- DATABASE_USERNAME=sshsync
9+
- DATABASE_PASSWORD=sshsync
10+
- DATABASE_NAME=sshsync
11+
- DATABASE_HOST=ssh-sync-db:5432
12+
logging:
13+
driver: json-file
14+
options:
15+
max-size: 10m
16+
ports:
17+
- '3000:3000'
18+
image: fd49561087a8563fece3be7eff59f6ff728c3e749fbd4ebfaee96a6ee3982b7d
19+
container_name: ssh-sync-server
20+
ssh-sync-db:
21+
image: therealpaulgg/ssh-sync-db:latest
22+
container_name: ssh-sync-db
23+
environment:
24+
- POSTGRES_USER=sshsync
25+
- POSTGRES_PASSWORD=sshsync
26+
- POSTGRES_DB=sshsync
27+
restart: always
28+
ssh-sync:
29+
image: 46204e8109ce
30+
container_name: ssh-sync
31+
stdin_open: true # Allows Docker container to keep STDIN open
32+
tty: true # Allocates a pseudo-TTY
33+
ssh-sync-2:
34+
image: 46204e8109ce
35+
container_name: ssh-sync-2
36+
stdin_open: true # Allows Docker container to keep STDIN open
37+
tty: true # Allocates a pseudo-TTY
38+
ssh-sync-3:
39+
image: 46204e8109ce
40+
container_name: ssh-sync-3
41+
stdin_open: true # Allows Docker container to keep STDIN open
42+
tty: true # Allocates a pseudo-TTY

pkg/web/live/main.go

+66-13
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net"
77
"net/http"
88
"strings"
9+
"sync"
910
"time"
1011

1112
"github.com/gobwas/ws"
@@ -40,8 +41,30 @@ type Something struct {
4041
ResponderChannel chan []byte
4142
}
4243

44+
type SafeChallengeResponseDict struct {
45+
mux sync.Mutex
46+
dict map[string]Something
47+
}
48+
49+
// Utility method for safely writing to the dict
50+
func (c *SafeChallengeResponseDict) WriteChallenge(challengePhrase string, data Something) {
51+
c.mux.Lock()
52+
c.dict[challengePhrase] = data
53+
c.mux.Unlock()
54+
}
55+
56+
// Utility method for safely reading from the dict
57+
func (c *SafeChallengeResponseDict) ReadChallenge(challengePhrase string) (Something, bool) {
58+
c.mux.Lock()
59+
data, exists := c.dict[challengePhrase]
60+
c.mux.Unlock()
61+
return data, exists
62+
}
63+
4364
var ChallengeResponseChannel = make(chan ChallengeResponse)
44-
var ChallengeResponseDict = make(map[string]Something)
65+
var ChallengeResponseDict = SafeChallengeResponseDict{
66+
dict: make(map[string]Something),
67+
}
4568

4669
func MachineChallengeResponse(i *do.Injector, r *http.Request, w http.ResponseWriter) error {
4770
conn, _, _, err := ws.UpgradeHTTP(r, w)
@@ -65,7 +88,7 @@ func MachineChallengeResponseHandler(i *do.Injector, r *http.Request, w http.Res
6588
log.Err(err).Msg("Error reading client message")
6689
return
6790
}
68-
chalChan, ok := ChallengeResponseDict[foo.Data.Challenge]
91+
chalChan, ok := ChallengeResponseDict.ReadChallenge(foo.Data.Challenge)
6992
if !ok {
7093
log.Warn().Msg("Could not find challenge in dict")
7194
if err := utils.WriteServerError[dto.ChallengeSuccessEncryptedKeyDto](&conn, "Invalid challenge response."); err != nil {
@@ -164,34 +187,63 @@ func NewMachineChallengeHandler(i *do.Injector, r *http.Request, w http.Response
164187
// Computer A will receive the public key, decrypt the master key, encrypt the master key with the public key, and send it back to the server.
165188
// At this point Computer B will be able to communicate freely.
166189

167-
ChallengeResponseDict[challengePhrase] = Something{
190+
ChallengeResponseDict.WriteChallenge(challengePhrase, Something{
168191
Username: user.Username,
169192
ChallengeAccepted: make(chan bool),
170193
ChallengerChannel: make(chan []byte),
171194
ResponderChannel: make(chan []byte),
172-
}
195+
})
173196
defer func() {
174-
close(ChallengeResponseDict[challengePhrase].ChallengeAccepted)
175-
close(ChallengeResponseDict[challengePhrase].ChallengerChannel)
176-
close(ChallengeResponseDict[challengePhrase].ResponderChannel)
177-
delete(ChallengeResponseDict, challengePhrase)
197+
ChallengeResponseDict.mux.Lock()
198+
defer ChallengeResponseDict.mux.Unlock()
199+
item, exists := ChallengeResponseDict.dict[challengePhrase]
200+
if exists {
201+
close(item.ChallengeAccepted)
202+
close(item.ChallengerChannel)
203+
close(item.ResponderChannel)
204+
delete(ChallengeResponseDict.dict, challengePhrase)
205+
}
178206
}()
179207
timer := time.NewTimer(30 * time.Second)
180208
go func() {
209+
var challengeAcceptedChan chan bool
210+
211+
// Lock before accessing ChallengeResponseDict
212+
ChallengeResponseDict.mux.Lock()
213+
if item, exists := ChallengeResponseDict.dict[challengePhrase]; exists {
214+
challengeAcceptedChan = item.ChallengeAccepted
215+
}
216+
ChallengeResponseDict.mux.Unlock()
217+
218+
// If the channel does not exist, return to avoid a nil channel operation
219+
if challengeAcceptedChan == nil {
220+
return
221+
}
222+
181223
for {
182224
select {
183225
case <-timer.C:
184-
ChallengeResponseDict[challengePhrase].ChallengeAccepted <- false
226+
ChallengeResponseDict.mux.Lock()
227+
// Check if the challenge still exists before sending to the channel
228+
if _, exists := ChallengeResponseDict.dict[challengePhrase]; exists {
229+
ChallengeResponseDict.dict[challengePhrase].ChallengeAccepted <- false
230+
}
231+
ChallengeResponseDict.mux.Unlock()
185232
return
186-
case chalWon := <-ChallengeResponseDict[challengePhrase].ChallengeAccepted:
233+
case chalWon := <-challengeAcceptedChan:
187234
if chalWon {
188235
timer.Stop()
189236
}
190237
return
191238
}
192239
}
193240
}()
194-
challengeResult := <-ChallengeResponseDict[challengePhrase].ChallengeAccepted
241+
cha, ok := ChallengeResponseDict.ReadChallenge(challengePhrase)
242+
if !ok {
243+
log.Err(err).Msg("Error getting challenge from dict")
244+
return
245+
}
246+
challengeResult := <-cha.ChallengeAccepted
195247

196248
if !challengeResult {
197249
if err := utils.WriteServerError[dto.MessageDto](&conn, "Challenge timed out"); err != nil {
@@ -208,8 +260,9 @@ func NewMachineChallengeHandler(i *do.Injector, r *http.Request, w http.Response
208260
log.Err(err).Msg("Error reading client message")
209261
return
210262
}
211-
ChallengeResponseDict[challengePhrase].ChallengerChannel <- pubkey.Data.PublicKey
212-
encryptedMasterKey := <-ChallengeResponseDict[challengePhrase].ResponderChannel
263+
264+
cha.ChallengerChannel <- pubkey.Data.PublicKey
265+
encryptedMasterKey := <-cha.ResponderChannel
213266
machine.PublicKey = pubkey.Data.PublicKey
214267
if _, err = machineRepo.CreateMachine(machine); err != nil {
215268
log.Err(err).Msg("Error creating machine")

0 commit comments

Comments
 (0)