Skip to content

Commit 3677815

Browse files
authored
Refactor IP address handling in server.js
1 parent c7167c6 commit 3677815

1 file changed

Lines changed: 86 additions & 24 deletions

File tree

server-src/server.js

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -87,45 +87,107 @@ function closeUserFromUserSocket(username) {
8787
function 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(/^::ffff:/, '');
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(/^::ffff:/, '');
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(/^::ffff:/, '');
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+
114161
function 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(/^::ffff:/, '');
129191
}
130192

131193
var ipBanReasons = {

0 commit comments

Comments
 (0)