@@ -87,45 +87,107 @@ function closeUserFromUserSocket(username) {
8787function isPrivateIp ( ip ) {
8888 if ( ! ip ) return false ;
8989
90- // Handle IPv6 localhost
91- if ( ip === '::1' || ip === 'localhost' ) return true ;
90+ // Clean up Node's IPv6-mapped IPv4 prefix (::ffff:)
91+ const cleanIp = ip . replace ( / ^ : : f f f f : / , '' ) ;
9292
93- // Split the IP into its 4 parts (octets)
94- const parts = ip . split ( '.' ) ;
95- if ( parts . length !== 4 ) return false ; // Not a standard IPv4 address
93+ if ( cleanIp === '::1' || cleanIp === 'localhost' ) return true ;
94+
95+ const parts = cleanIp . split ( '.' ) ;
96+ if ( parts . length !== 4 ) return false ;
9697
9798 const first = parseInt ( parts [ 0 ] , 10 ) ;
9899 const second = parseInt ( parts [ 1 ] , 10 ) ;
99100
100- // 127.x.x.x (Localhost)
101- if ( first === 127 ) return true ;
101+ // Standard Private Ranges
102+ if ( first === 127 || first === 10 ) return true ;
103+ if ( first === 192 && second === 168 ) return true ;
104+ if ( first === 172 && second >= 16 && second <= 31 ) return true ;
102105
103- // 10.x.x.x (Private network)
104- if ( first === 10 ) return true ;
106+ return false ;
107+ }
105108
106- // 192.168.x.x (Private network)
107- if ( first === 192 && second === 168 ) return true ;
109+ function getIPFromRequest ( req ) {
110+ // 1. Always check Cloudflare/Render verified header first
111+ if ( req . headers [ 'cf-connecting-ip' ] ) {
112+ return req . headers [ 'cf-connecting-ip' ] ;
113+ }
114+
115+ const ipListHeader = req . headers [ 'x-forwarded-for' ] ;
116+ if ( ipListHeader ) {
117+ const IPs = ipListHeader . split ( "," ) . map ( ( ip ) => ip . trim ( ) ) ;
118+
119+ // Start from the right (the most recent proxy)
120+ for ( let i = IPs . length - 1 ; i >= 0 ; i -- ) {
121+ const curIp = IPs [ i ] ;
122+ // If we hit an IP that is NOT private, check if it's the last one left
123+ // or if it's a known public proxy (like Render's Azure IPs).
124+ if ( ! isPrivateIp ( curIp ) ) {
125+ // If we are at the very first IP (index 0), it's definitely the user.
126+ if ( i === 0 ) return curIp ;
127+
128+ // If this isn't the first IP, it might be Render's public proxy.
129+ // We keep looping until we hit index 0.
130+ continue ;
131+ }
132+ }
133+ // If the loop finished or we want the most likely candidate:
134+ return IPs [ 0 ] ;
135+ }
136+
137+ // Fallback to socket address, cleaning the prefix if it exists
138+ return ( req . socket . remoteAddress || "" ) . replace ( / ^ : : f f f f : / , '' ) ;
139+ } function isPrivateIp ( ip ) {
140+ if ( ! ip ) return false ;
108141
109- // 172.16.x.x through 172.31.x.x (Private network)
142+ // Clean up Node's IPv6-mapped IPv4 prefix (::ffff:)
143+ const cleanIp = ip . replace ( / ^ : : f f f f : / , '' ) ;
144+
145+ if ( cleanIp === '::1' || cleanIp === 'localhost' ) return true ;
146+
147+ const parts = cleanIp . split ( '.' ) ;
148+ if ( parts . length !== 4 ) return false ;
149+
150+ const first = parseInt ( parts [ 0 ] , 10 ) ;
151+ const second = parseInt ( parts [ 1 ] , 10 ) ;
152+
153+ // Standard Private Ranges
154+ if ( first === 127 || first === 10 ) return true ;
155+ if ( first === 192 && second === 168 ) return true ;
110156 if ( first === 172 && second >= 16 && second <= 31 ) return true ;
111157
112158 return false ;
113159}
160+
114161function getIPFromRequest ( req ) {
115- var ipListHeader = req . headers [ 'x-forwarded-for' ] ;
162+ // 1. Always check Cloudflare/Render verified header first
163+ if ( req . headers [ 'cf-connecting-ip' ] ) {
164+ return req . headers [ 'cf-connecting-ip' ] ;
165+ }
166+
167+ const ipListHeader = req . headers [ 'x-forwarded-for' ] ;
116168 if ( ipListHeader ) {
117- var IPString = "" + ipListHeader ;
118- var IPs = IPString . split ( "," ) . map ( ( ip ) => ip . trim ( ) ) ;
119- var i = IPs . length - 1 ;
120- while ( i > 0 ) {
121- var curIp = IPs [ i ] ;
122- if ( ! isPrivateIp ( curIp ) ) {
123- return curIp ;
124- }
125- i -= 1 ;
126- }
169+ const IPs = ipListHeader . split ( "," ) . map ( ( ip ) => ip . trim ( ) ) ;
170+
171+ // Start from the right (the most recent proxy)
172+ for ( let i = IPs . length - 1 ; i >= 0 ; i -- ) {
173+ const curIp = IPs [ i ] ;
174+ // If we hit an IP that is NOT private, check if it's the last one left
175+ // or if it's a known public proxy (like Render's Azure IPs).
176+ if ( ! isPrivateIp ( curIp ) ) {
177+ // If we are at the very first IP (index 0), it's definitely the user.
178+ if ( i === 0 ) return curIp ;
179+
180+ // If this isn't the first IP, it might be Render's public proxy.
181+ // We keep looping until we hit index 0.
182+ continue ;
183+ }
184+ }
185+ // If the loop finished or we want the most likely candidate:
186+ return IPs [ 0 ] ;
127187 }
128- return req . socket . remoteAddress ;
188+
189+ // Fallback to socket address, cleaning the prefix if it exists
190+ return ( req . socket . remoteAddress || "" ) . replace ( / ^ : : f f f f : / , '' ) ;
129191}
130192
131193var ipBanReasons = {
0 commit comments