Fix broker race condition

This commit is contained in:
itchyonion 2022-05-26 23:26:38 -07:00
parent c983c13a84
commit 03b2b56f87
2 changed files with 23 additions and 20 deletions

View file

@ -154,8 +154,8 @@ func (ctx *BrokerContext) AddSnowflake(id string, proxyType string, natType stri
heap.Push(ctx.restrictedSnowflakes, snowflake) heap.Push(ctx.restrictedSnowflakes, snowflake)
} }
ctx.metrics.promMetrics.AvailableProxies.With(prometheus.Labels{"nat": natType, "type": proxyType}).Inc() ctx.metrics.promMetrics.AvailableProxies.With(prometheus.Labels{"nat": natType, "type": proxyType}).Inc()
ctx.snowflakeLock.Unlock()
ctx.idToSnowflake[id] = snowflake ctx.idToSnowflake[id] = snowflake
ctx.snowflakeLock.Unlock()
return snowflake return snowflake
} }

View file

@ -190,19 +190,10 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
offer.fingerprint = BridgeFingerprint.ToBytes() offer.fingerprint = BridgeFingerprint.ToBytes()
// Only hand out known restricted snowflakes to unrestricted clients snowflake := i.matchSnowflake(offer.natType)
var snowflakeHeap *SnowflakeHeap if snowflake != nil {
if offer.natType == NATUnrestricted { snowflake.offerChannel <- offer
snowflakeHeap = i.ctx.restrictedSnowflakes
} else { } else {
snowflakeHeap = i.ctx.snowflakes
}
// Immediately fail if there are no snowflakes available.
i.ctx.snowflakeLock.Lock()
numSnowflakes := snowflakeHeap.Len()
i.ctx.snowflakeLock.Unlock()
if numSnowflakes <= 0 {
i.ctx.metrics.lock.Lock() i.ctx.metrics.lock.Lock()
i.ctx.metrics.clientDeniedCount++ i.ctx.metrics.clientDeniedCount++
i.ctx.metrics.promMetrics.ClientPollTotal.With(prometheus.Labels{"nat": offer.natType, "status": "denied"}).Inc() i.ctx.metrics.promMetrics.ClientPollTotal.With(prometheus.Labels{"nat": offer.natType, "status": "denied"}).Inc()
@ -216,13 +207,6 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
return sendClientResponse(resp, response) return sendClientResponse(resp, response)
} }
// Otherwise, find the most available snowflake proxy, and pass the offer to it.
// Delete must be deferred in order to correctly process answer request later.
i.ctx.snowflakeLock.Lock()
snowflake := heap.Pop(snowflakeHeap).(*Snowflake)
i.ctx.snowflakeLock.Unlock()
snowflake.offerChannel <- offer
// Wait for the answer to be returned on the channel or timeout. // Wait for the answer to be returned on the channel or timeout.
select { select {
case answer := <-snowflake.answerChannel: case answer := <-snowflake.answerChannel:
@ -248,6 +232,25 @@ func (i *IPC) ClientOffers(arg messages.Arg, response *[]byte) error {
return err return err
} }
func (i *IPC) matchSnowflake(natType string) *Snowflake {
// Only hand out known restricted snowflakes to unrestricted clients
var snowflakeHeap *SnowflakeHeap
if natType == NATUnrestricted {
snowflakeHeap = i.ctx.restrictedSnowflakes
} else {
snowflakeHeap = i.ctx.snowflakes
}
i.ctx.snowflakeLock.Lock()
defer i.ctx.snowflakeLock.Unlock()
if snowflakeHeap.Len() > 0 {
return heap.Pop(snowflakeHeap).(*Snowflake)
} else {
return nil
}
}
func (i *IPC) ProxyAnswers(arg messages.Arg, response *[]byte) error { func (i *IPC) ProxyAnswers(arg messages.Arg, response *[]byte) error {
answer, id, err := messages.DecodeAnswerRequest(arg.Body) answer, id, err := messages.DecodeAnswerRequest(arg.Body)
if err != nil || answer == "" { if err != nil || answer == "" {