Fix prefix URL routing for encoded CIDR notation
Change route from wildcard /prefix/* to explicit /prefix/{prefix}/{len}
to properly handle URL-encoded IPv6 addresses with CIDR notation.
- Separate prefix and length into individual path parameters
- Add prefixURL template function for generating correct links
- Remove url.QueryUnescape from handlers (chi handles decoding)
This commit is contained in:
parent
27909e021f
commit
45810e3fc8
@ -7,7 +7,6 @@ import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
@ -736,21 +735,18 @@ func (s *Server) handleASDetailJSON() http.HandlerFunc {
|
||||
// handlePrefixDetailJSON returns prefix details as JSON
|
||||
func (s *Server) handlePrefixDetailJSON() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Get wildcard parameter (everything after /prefix/)
|
||||
prefixParam := chi.URLParam(r, "*")
|
||||
if prefixParam == "" {
|
||||
writeJSONError(w, http.StatusBadRequest, "Prefix parameter is required")
|
||||
// Get prefix and length from URL params
|
||||
prefixParam := chi.URLParam(r, "prefix")
|
||||
lenParam := chi.URLParam(r, "len")
|
||||
|
||||
if prefixParam == "" || lenParam == "" {
|
||||
writeJSONError(w, http.StatusBadRequest, "Prefix and length parameters are required")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// URL decode the prefix parameter
|
||||
prefix, err := url.QueryUnescape(prefixParam)
|
||||
if err != nil {
|
||||
writeJSONError(w, http.StatusBadRequest, "Invalid prefix parameter")
|
||||
|
||||
return
|
||||
}
|
||||
// Combine prefix and length into CIDR notation
|
||||
prefix := prefixParam + "/" + lenParam
|
||||
|
||||
routes, err := s.db.GetPrefixDetailsContext(r.Context(), prefix)
|
||||
if err != nil {
|
||||
@ -903,21 +899,18 @@ func (s *Server) handleASDetail() http.HandlerFunc {
|
||||
// handlePrefixDetail returns a handler that serves the prefix detail HTML page
|
||||
func (s *Server) handlePrefixDetail() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Get wildcard parameter (everything after /prefix/)
|
||||
prefixParam := chi.URLParam(r, "*")
|
||||
if prefixParam == "" {
|
||||
http.Error(w, "Prefix parameter is required", http.StatusBadRequest)
|
||||
// Get prefix and length from URL params
|
||||
prefixParam := chi.URLParam(r, "prefix")
|
||||
lenParam := chi.URLParam(r, "len")
|
||||
|
||||
if prefixParam == "" || lenParam == "" {
|
||||
http.Error(w, "Prefix and length parameters are required", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// URL decode the prefix parameter
|
||||
prefix, err := url.QueryUnescape(prefixParam)
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid prefix parameter", http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
// Combine prefix and length into CIDR notation
|
||||
prefix := prefixParam + "/" + lenParam
|
||||
|
||||
routes, err := s.db.GetPrefixDetailsContext(r.Context(), prefix)
|
||||
if err != nil {
|
||||
|
||||
@ -28,7 +28,7 @@ func (s *Server) setupRoutes() {
|
||||
|
||||
// AS and prefix detail pages
|
||||
r.Get("/as/{asn}", s.handleASDetail())
|
||||
r.Get("/prefix/*", s.handlePrefixDetail())
|
||||
r.Get("/prefix/{prefix}/{len}", s.handlePrefixDetail())
|
||||
r.Get("/prefixlength/{length}", s.handlePrefixLength())
|
||||
r.Get("/prefixlength6/{length}", s.handlePrefixLength6())
|
||||
|
||||
@ -45,7 +45,7 @@ func (s *Server) setupRoutes() {
|
||||
r.Get("/stats", s.handleStats())
|
||||
r.Get("/ip/{ip}", s.handleIPLookup())
|
||||
r.Get("/as/{asn}", s.handleASDetailJSON())
|
||||
r.Get("/prefix/*", s.handlePrefixDetailJSON())
|
||||
r.Get("/prefix/{prefix}/{len}", s.handlePrefixDetailJSON())
|
||||
})
|
||||
|
||||
s.router = r
|
||||
|
||||
@ -182,7 +182,7 @@
|
||||
<tbody>
|
||||
{{range .IPv4Prefixes}}
|
||||
<tr>
|
||||
<td><a href="/prefix/{{.Prefix | urlEncode}}" class="prefix-link">{{.Prefix}}</a></td>
|
||||
<td><a href="{{.Prefix | prefixURL}}" class="prefix-link">{{.Prefix}}</a></td>
|
||||
<td>/{{.MaskLength}}</td>
|
||||
<td>{{.LastUpdated.Format "2006-01-02 15:04:05"}}</td>
|
||||
<td class="age">{{.LastUpdated | timeSince}}</td>
|
||||
@ -211,7 +211,7 @@
|
||||
<tbody>
|
||||
{{range .IPv6Prefixes}}
|
||||
<tr>
|
||||
<td><a href="/prefix/{{.Prefix | urlEncode}}" class="prefix-link">{{.Prefix}}</a></td>
|
||||
<td><a href="{{.Prefix | prefixURL}}" class="prefix-link">{{.Prefix}}</a></td>
|
||||
<td>/{{.MaskLength}}</td>
|
||||
<td>{{.LastUpdated.Format "2006-01-02 15:04:05"}}</td>
|
||||
<td class="age">{{.LastUpdated | timeSince}}</td>
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
<tbody>
|
||||
{{ range .Prefixes }}
|
||||
<tr>
|
||||
<td><a href="/prefix/{{ .Prefix | urlEncode }}" class="prefix-link">{{ .Prefix }}</a></td>
|
||||
<td><a href="{{ .Prefix | prefixURL }}" class="prefix-link">{{ .Prefix }}</a></td>
|
||||
<td class="age">{{ .Age }}</td>
|
||||
<td>
|
||||
<a href="/as/{{ .OriginASN }}" class="as-link">
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
_ "embed"
|
||||
"html/template"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -43,8 +44,9 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
hoursPerDay = 24
|
||||
daysPerMonth = 30
|
||||
hoursPerDay = 24
|
||||
daysPerMonth = 30
|
||||
cidrPartCount = 2 // A CIDR has two parts: prefix and length
|
||||
)
|
||||
|
||||
// timeSince returns a human-readable duration since the given time
|
||||
@ -80,6 +82,20 @@ func timeSince(t time.Time) string {
|
||||
return t.Format("2006-01-02")
|
||||
}
|
||||
|
||||
// prefixURL generates a URL path for a prefix in CIDR notation.
|
||||
// Takes a prefix like "192.168.1.0/24" and returns "/prefix/192.168.1.0/24"
|
||||
// with the prefix part URL-encoded to handle IPv6 colons.
|
||||
func prefixURL(cidr string) string {
|
||||
// Split CIDR into prefix and length
|
||||
parts := strings.SplitN(cidr, "/", cidrPartCount)
|
||||
if len(parts) != cidrPartCount {
|
||||
// Fallback if no slash found
|
||||
return "/prefix/" + url.PathEscape(cidr) + "/0"
|
||||
}
|
||||
|
||||
return "/prefix/" + url.PathEscape(parts[0]) + "/" + parts[1]
|
||||
}
|
||||
|
||||
// initTemplates parses all embedded templates
|
||||
func initTemplates() {
|
||||
var err error
|
||||
@ -90,6 +106,7 @@ func initTemplates() {
|
||||
funcs := template.FuncMap{
|
||||
"timeSince": timeSince,
|
||||
"urlEncode": url.QueryEscape,
|
||||
"prefixURL": prefixURL,
|
||||
"appName": func() string { return version.Name },
|
||||
"appAuthor": func() string { return version.Author },
|
||||
"appAuthorURL": func() string { return version.AuthorURL },
|
||||
|
||||
Loading…
Reference in New Issue
Block a user