util.go 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package server
  2. import (
  3. "heckel.io/ntfy/log"
  4. "heckel.io/ntfy/util"
  5. "io"
  6. "net/http"
  7. "net/netip"
  8. "strings"
  9. )
  10. func readBoolParam(r *http.Request, defaultValue bool, names ...string) bool {
  11. value := strings.ToLower(readParam(r, names...))
  12. if value == "" {
  13. return defaultValue
  14. }
  15. return value == "1" || value == "yes" || value == "true"
  16. }
  17. func readParam(r *http.Request, names ...string) string {
  18. value := readHeaderParam(r, names...)
  19. if value != "" {
  20. return value
  21. }
  22. return readQueryParam(r, names...)
  23. }
  24. func readHeaderParam(r *http.Request, names ...string) string {
  25. for _, name := range names {
  26. value := r.Header.Get(name)
  27. if value != "" {
  28. return strings.TrimSpace(value)
  29. }
  30. }
  31. return ""
  32. }
  33. func readQueryParam(r *http.Request, names ...string) string {
  34. for _, name := range names {
  35. value := r.URL.Query().Get(strings.ToLower(name))
  36. if value != "" {
  37. return strings.TrimSpace(value)
  38. }
  39. }
  40. return ""
  41. }
  42. func extractIPAddress(r *http.Request, behindProxy bool) netip.Addr {
  43. remoteAddr := r.RemoteAddr
  44. addrPort, err := netip.ParseAddrPort(remoteAddr)
  45. ip := addrPort.Addr()
  46. if err != nil {
  47. // This should not happen in real life; only in tests. So, using falling back to 0.0.0.0 if address unspecified
  48. ip, err = netip.ParseAddr(remoteAddr)
  49. if err != nil {
  50. ip = netip.IPv4Unspecified()
  51. if remoteAddr != "@" || !behindProxy { // RemoteAddr is @ when unix socket is used
  52. log.Warn("unable to parse IP (%s), new visitor with unspecified IP (0.0.0.0) created %s", remoteAddr, err)
  53. }
  54. }
  55. }
  56. if behindProxy && strings.TrimSpace(r.Header.Get("X-Forwarded-For")) != "" {
  57. // X-Forwarded-For can contain multiple addresses (see #328). If we are behind a proxy,
  58. // only the right-most address can be trusted (as this is the one added by our proxy server).
  59. // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For for details.
  60. ips := util.SplitNoEmpty(r.Header.Get("X-Forwarded-For"), ",")
  61. realIP, err := netip.ParseAddr(strings.TrimSpace(util.LastString(ips, remoteAddr)))
  62. if err != nil {
  63. log.Error("invalid IP address %s received in X-Forwarded-For header: %s", ip, err.Error())
  64. // Fall back to regular remote address if X-Forwarded-For is damaged
  65. } else {
  66. ip = realIP
  67. }
  68. }
  69. return ip
  70. }
  71. func readJSONWithLimit[T any](r io.ReadCloser, limit int, allowEmpty bool) (*T, error) {
  72. obj, err := util.UnmarshalJSONWithLimit[T](r, limit, allowEmpty)
  73. if err == util.ErrUnmarshalJSON {
  74. return nil, errHTTPBadRequestJSONInvalid
  75. } else if err == util.ErrTooLargeJSON {
  76. return nil, errHTTPEntityTooLargeJSONBody
  77. } else if err != nil {
  78. return nil, err
  79. }
  80. return obj, nil
  81. }