83769ba715
This commits removes the locks used to guard data integrity for the database and replaces them with Transactions, turns out that SQL had a way to deal with this all along. This reduces the complexity we had with multiple locks that might stack or recurse (database, nofitifer, mapper). All notifications and state updates are now triggered _after_ a database change. Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
96 lines
2.4 KiB
Go
96 lines
2.4 KiB
Go
package hscontrol
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
"gorm.io/gorm"
|
|
"tailscale.com/tailcfg"
|
|
"tailscale.com/types/key"
|
|
)
|
|
|
|
const (
|
|
MinimumCapVersion tailcfg.CapabilityVersion = 58
|
|
)
|
|
|
|
// NoisePollNetMapHandler takes care of /machine/:id/map using the Noise protocol
|
|
//
|
|
// This is the busiest endpoint, as it keeps the HTTP long poll that updates
|
|
// the clients when something in the network changes.
|
|
//
|
|
// The clients POST stuff like HostInfo and their Endpoints here, but
|
|
// only after their first request (marked with the ReadOnly field).
|
|
//
|
|
// At this moment the updates are sent in a quite horrendous way, but they kinda work.
|
|
func (ns *noiseServer) NoisePollNetMapHandler(
|
|
writer http.ResponseWriter,
|
|
req *http.Request,
|
|
) {
|
|
log.Trace().
|
|
Str("handler", "NoisePollNetMap").
|
|
Msg("PollNetMapHandler called")
|
|
|
|
log.Trace().
|
|
Any("headers", req.Header).
|
|
Caller().
|
|
Msg("Headers")
|
|
|
|
body, _ := io.ReadAll(req.Body)
|
|
|
|
mapRequest := tailcfg.MapRequest{}
|
|
if err := json.Unmarshal(body, &mapRequest); err != nil {
|
|
log.Error().
|
|
Caller().
|
|
Err(err).
|
|
Msg("Cannot parse MapRequest")
|
|
http.Error(writer, "Internal error", http.StatusInternalServerError)
|
|
|
|
return
|
|
}
|
|
|
|
// Reject unsupported versions
|
|
if mapRequest.Version < MinimumCapVersion {
|
|
log.Info().
|
|
Caller().
|
|
Int("min_version", int(MinimumCapVersion)).
|
|
Int("client_version", int(mapRequest.Version)).
|
|
Msg("unsupported client connected")
|
|
http.Error(writer, "Internal error", http.StatusBadRequest)
|
|
|
|
return
|
|
}
|
|
|
|
ns.nodeKey = mapRequest.NodeKey
|
|
|
|
node, err := ns.headscale.db.GetNodeByAnyKey(
|
|
ns.conn.Peer(),
|
|
mapRequest.NodeKey,
|
|
key.NodePublic{},
|
|
)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
log.Warn().
|
|
Str("handler", "NoisePollNetMap").
|
|
Msgf("Ignoring request, cannot find node with key %s", mapRequest.NodeKey.String())
|
|
http.Error(writer, "Internal error", http.StatusNotFound)
|
|
|
|
return
|
|
}
|
|
log.Error().
|
|
Str("handler", "NoisePollNetMap").
|
|
Msgf("Failed to fetch node from the database with node key: %s", mapRequest.NodeKey.String())
|
|
http.Error(writer, "Internal error", http.StatusInternalServerError)
|
|
|
|
return
|
|
}
|
|
log.Debug().
|
|
Str("handler", "NoisePollNetMap").
|
|
Str("node", node.Hostname).
|
|
Int("cap_ver", int(mapRequest.Version)).
|
|
Msg("A node sending a MapRequest with Noise protocol")
|
|
|
|
ns.headscale.handlePoll(writer, req.Context(), node, mapRequest)
|
|
}
|