| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- package controller
- import (
- "fmt"
- "net"
- "net/http"
- "net/netip"
- "strings"
- "github.com/mhsanaei/3x-ui/v3/logger"
- "github.com/mhsanaei/3x-ui/v3/web/entity"
- "github.com/mhsanaei/3x-ui/v3/web/service"
- "github.com/gin-gonic/gin"
- )
- // getRemoteIp extracts the real IP address from the request headers or remote address.
- func getRemoteIp(c *gin.Context) string {
- remoteIP, ok := extractTrustedIP(c.Request.RemoteAddr)
- if !ok {
- return "unknown"
- }
- if isTrustedProxy(remoteIP) {
- if ip, ok := extractTrustedIP(c.GetHeader("X-Real-IP")); ok {
- return ip
- }
- if xff := c.GetHeader("X-Forwarded-For"); xff != "" {
- for _, part := range strings.Split(xff, ",") {
- if ip, ok := extractTrustedIP(part); ok {
- return ip
- }
- }
- }
- }
- return remoteIP
- }
- func isTrustedForwardedRequest(c *gin.Context) bool {
- remoteIP, ok := extractTrustedIP(c.Request.RemoteAddr)
- return ok && isTrustedProxy(remoteIP)
- }
- func isTrustedProxy(ip string) bool {
- addr, err := netip.ParseAddr(ip)
- if err != nil {
- return false
- }
- trusted := trustedProxyCIDRs()
- for _, value := range strings.Split(trusted, ",") {
- value = strings.TrimSpace(value)
- if value == "" {
- continue
- }
- if prefix, err := netip.ParsePrefix(value); err == nil {
- if prefix.Contains(addr) {
- return true
- }
- continue
- }
- if proxyIP, err := netip.ParseAddr(value); err == nil && proxyIP.Unmap() == addr.Unmap() {
- return true
- }
- }
- return false
- }
- func trustedProxyCIDRs() (trusted string) {
- trusted = "127.0.0.1/32,::1/128"
- defer func() {
- _ = recover()
- }()
- settingService := service.SettingService{}
- if value, err := settingService.GetTrustedProxyCIDRs(); err == nil && strings.TrimSpace(value) != "" {
- trusted = value
- }
- return trusted
- }
- func extractTrustedIP(value string) (string, bool) {
- candidate := strings.TrimSpace(value)
- if candidate == "" {
- return "", false
- }
- if ip, ok := parseIPCandidate(candidate); ok {
- return ip.String(), true
- }
- if host, _, err := net.SplitHostPort(candidate); err == nil {
- if ip, ok := parseIPCandidate(host); ok {
- return ip.String(), true
- }
- }
- if strings.Count(candidate, ":") == 1 {
- if host, _, err := net.SplitHostPort(fmt.Sprintf("[%s]", candidate)); err == nil {
- if ip, ok := parseIPCandidate(host); ok {
- return ip.String(), true
- }
- }
- }
- return "", false
- }
- func parseIPCandidate(value string) (netip.Addr, bool) {
- ip, err := netip.ParseAddr(strings.TrimSpace(value))
- if err != nil {
- return netip.Addr{}, false
- }
- return ip.Unmap(), true
- }
- // jsonMsg sends a JSON response with a message and error status.
- func jsonMsg(c *gin.Context, msg string, err error) {
- jsonMsgObj(c, msg, nil, err)
- }
- // jsonObj sends a JSON response with an object and error status.
- func jsonObj(c *gin.Context, obj any, err error) {
- jsonMsgObj(c, "", obj, err)
- }
- // jsonMsgObj sends a JSON response with a message, object, and error status.
- func jsonMsgObj(c *gin.Context, msg string, obj any, err error) {
- m := entity.Msg{
- Obj: obj,
- }
- if err == nil {
- m.Success = true
- if msg != "" {
- m.Msg = msg
- }
- } else {
- m.Success = false
- errStr := err.Error()
- if errStr != "" {
- m.Msg = msg + " (" + errStr + ")"
- logger.Warning(msg+" "+I18nWeb(c, "fail")+": ", err)
- } else if msg != "" {
- m.Msg = msg
- logger.Warning(msg + " " + I18nWeb(c, "fail"))
- } else {
- m.Msg = I18nWeb(c, "somethingWentWrong")
- logger.Warning(I18nWeb(c, "somethingWentWrong") + " " + I18nWeb(c, "fail"))
- }
- }
- c.JSON(http.StatusOK, m)
- }
- // pureJsonMsg sends a pure JSON message response with custom status code.
- func pureJsonMsg(c *gin.Context, statusCode int, success bool, msg string) {
- c.JSON(statusCode, entity.Msg{
- Success: success,
- Msg: msg,
- })
- }
- // isAjax checks if the request is an AJAX request.
- func isAjax(c *gin.Context) bool {
- return c.GetHeader("X-Requested-With") == "XMLHttpRequest"
- }
|