diff --git a/ipn/ipnlocal/peerapi.go b/ipn/ipnlocal/peerapi.go index 5631789a1..4919365c1 100644 --- a/ipn/ipnlocal/peerapi.go +++ b/ipn/ipnlocal/peerapi.go @@ -401,6 +401,10 @@ func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.handlePeerPut(w, r) return } + if r.URL.Path == "/v0/goroutines" { + h.handleServeGoroutines(w, r) + return + } who := h.peerUser.DisplayName fmt.Fprintf(w, ` @@ -582,3 +586,19 @@ func approxSize(n int64) string { } return fmt.Sprintf("~%dMB", n>>20) } + +func (h *peerAPIHandler) handleServeGoroutines(w http.ResponseWriter, r *http.Request) { + if !h.isSelf { + http.Error(w, "not owner", http.StatusForbidden) + return + } + var buf []byte + for size := 4 << 10; size <= 2<<20; size *= 2 { + buf = make([]byte, size) + buf = buf[:runtime.Stack(buf, true)] + if len(buf) < size { + break + } + } + w.Write(buf) +} diff --git a/ipn/ipnlocal/peerapi_test.go b/ipn/ipnlocal/peerapi_test.go index f1333d6a1..2af09e4d0 100644 --- a/ipn/ipnlocal/peerapi_test.go +++ b/ipn/ipnlocal/peerapi_test.go @@ -103,7 +103,7 @@ func hexAll(v string) string { return sb.String() } -func TestHandlePeerPut(t *testing.T) { +func TestHandlePeerAPI(t *testing.T) { tests := []struct { name string isSelf bool // the peer sending the request is owned by us @@ -134,6 +134,21 @@ func TestHandlePeerPut(t *testing.T) { bodyNotContains("You are the owner of this node."), ), }, + { + name: "peer_api_goroutines_deny", + isSelf: false, + req: httptest.NewRequest("GET", "/v0/goroutines", nil), + checks: checks(httpStatus(403)), + }, + { + name: "peer_api_goroutines", + isSelf: true, + req: httptest.NewRequest("GET", "/v0/goroutines", nil), + checks: checks( + httpStatus(200), + bodyContains("ServeHTTP"), + ), + }, { name: "reject_non_owner_put", isSelf: false,