Commits (9)
......@@ -136,6 +136,11 @@ function UsingSecondRLoginName() {
return (FBBSOptions.indexOf("USE_2ND_RLOGIN") !== -1);
}
function UsingHAProxy() {
GetSBBSIniValues();
return (FBBSOptions.indexOf("HAPROXY_PROTO") !== -1);
}
function GetInterface(i) {
if (['', '0.0.0.0', '::'].indexOf(i) > -1) return 'localhost';
return i;
......
......@@ -47,6 +47,11 @@ var FWebSocketState = WEBSOCKET_NEED_PACKET_START;
try {
// Parse and respond to the WebSocket handshake request
if (ShakeHands()) {
if (UsingHAProxy() && FWebSocketHeader['X-Forwarded-For'] === undefined) {
throw new Error('BBS is using HAProxy, but no X-Forwarded-For header present.');
}
SendToWebSocketClient(StringToBytes("Redirecting to server...\r\n"));
// Default to localhost on the telnet port
......@@ -87,6 +92,20 @@ try {
var ClientData = [];
var ServerData = [];
if (UsingHAProxy()) {
var hapstr = '\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x21';
if (client.socket.family === PF_INET) {
hapstr += '\x11\x00\x0C';
} else if (client.socket.family === PF_INET6) {
hapstr += '\x21\x00\x24';
}
hapstr += inet_pton(FWebSocketHeader['X-Forwarded-For']);
hapstr += inet_pton(FServerSocket.remote_ip_address);
hapstr += client.port.toString(16);
hapstr += TargetPort.toString(16);
FServerSocket.send(hapstr);
}
// Loop while we're still connected on both ends
while ((client.socket.is_connected) && (FServerSocket.is_connected)) {
// Should we yield or not (default true, but disable if we had line activity)
......@@ -449,51 +468,53 @@ function ShakeHands() {
break;
}
break;
} else if (InLine.indexOf("Connection:") === 0) {
} else if (InLine.search(/^Connection:/i) === 0) {
// Example: "Connection: Upgrade"
FWebSocketHeader['Connection'] = InLine.replace(/Connection:\s?/i, "");
} else if (InLine.indexOf("GET") === 0) {
} else if (InLine.search(/^GET/i) === 0) {
// Example: "GET /demo HTTP/1.1"
var GET = InLine.split(" ");
FWebSocketHeader['Path'] = GET[1];
} else if (InLine.indexOf("Host:") === 0) {
} else if (InLine.search(/^Host:/i) === 0) {
// Example: "Host: example.com"
FWebSocketHeader['Host'] = InLine.replace(/Host:\s?/i, "");
} else if (InLine.indexOf("Origin:") === 0) {
} else if (InLine.search(/^Origin:/i) === 0) {
// Example: "Origin: http://example.com"
FWebSocketHeader['Origin'] = InLine.replace(/Origin:\s?/i, "");
} else if (InLine.indexOf("Sec-WebSocket-Key:") === 0) {
} else if (InLine.search(/^Sec-WebSocket-Key:/i) === 0) {
// Example: "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ=="
FWebSocketHeader['Key'] = InLine.replace(/Sec-WebSocket-Key:\s?/i, "");
} else if (InLine.indexOf("Sec-WebSocket-Key1:") === 0) {
} else if (InLine.search(/^Sec-WebSocket-Key1:/i) === 0) {
// Example: "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5"
FWebSocketHeader['Key1'] = CalculateWebSocketKey(InLine.replace(/Sec-WebSocket-Key1:\s?/i, ""));
} else if (InLine.indexOf("Sec-WebSocket-Key2:") === 0) {
} else if (InLine.search(/^Sec-WebSocket-Key2:/i) === 0) {
// Example: "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00"
FWebSocketHeader['Key2'] = CalculateWebSocketKey(InLine.replace(/Sec-WebSocket-Key2:\s?/i, ""));
} else if (InLine.indexOf("Sec-WebSocket-Origin:") === 0) {
} else if (InLine.search(/^Sec-WebSocket-Origin:/i) === 0) {
// Example: "Sec-WebSocket-Origin: http://example.com"
FWebSocketHeader['Origin'] = InLine.replace(/Sec-WebSocket-Origin:\s?/i, "");
} else if (InLine.indexOf("Sec-WebSocket-Protocol:") === 0) {
} else if (InLine.search(/^Sec-WebSocket-Protocol:/i) === 0) {
// Example: "Sec-WebSocket-Protocol: sample"
FWebSocketHeader['SubProtocol'] = InLine.replace(/Sec-WebSocket-Protocol:\s?/i, "");
} else if (InLine.indexOf("Sec-WebSocket-Draft") === 0) {
} else if (InLine.search(/^Sec-WebSocket-Draft/i) === 0) {
// Example: "Sec-WebSocket-Draft: 2"
try {
FWebSocketHeader['Version'] = parseInt(InLine.replace(/Sec-WebSocket-Draft:\s?/i, ""));
} catch (err) {
FWebSocketHeader['Version'] = 0;
}
} else if (InLine.indexOf("Sec-WebSocket-Version") === 0) {
} else if (InLine.search(/^Sec-WebSocket-Version/i) === 0) {
// Example: "Sec-WebSocket-Version: 8"
try {
FWebSocketHeader['Version'] = parseInt(InLine.replace(/Sec-WebSocket-Version:\s?/i, ""));
} catch (err) {
FWebSocketHeader['Version'] = 0;
}
} else if (InLine.indexOf("Upgrade:") === 0) {
} else if (InLine.search(/^Upgrade:/i) === 0) {
// Example: "Upgrade: websocket"
FWebSocketHeader['Upgrade'] = InLine.replace(/Upgrade:\s?/i, "");
} else if (InLine.search(/^X-Forwarded-For/i) === 0) {
FWebSocketHeader['X-Forwarded-For'] = InLine.replace(/X-Forwarded-For:\s?/i, "");
}
}
} catch (err) {
......@@ -588,3 +609,48 @@ function ShakeHandsVersion7() {
return false;
}
}
function inet_pton (a) {
// http://kevin.vanzonneveld.net
// + original by: Theriault
// * example 1: inet_pton('::');
// * returns 1: '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0' (binary)
// * example 2: inet_pton('127.0.0.1');
// * returns 2: '\x7F\x00\x00\x01' (binary)
var r, m, x, i, j, f = String.fromCharCode;
m = a.match(/^(?:\d{1,3}(?:\.|$)){4}/); // IPv4
if (m) {
m = m[0].split('.');
m = f(m[0]) + f(m[1]) + f(m[2]) + f(m[3]);
// Return if 4 bytes, otherwise false.
return m.length === 4 ? m : false;
}
r = /^((?:[\da-f]{1,4}(?::|)){0,8})(::)?((?:[\da-f]{1,4}(?::|)){0,8})$/;
m = a.match(r); // IPv6
if (m) {
// Translate each hexadecimal value.
for (j = 1; j < 4; j++) {
// Indice 2 is :: and if no length, continue.
if (j === 2 || m[j].length === 0) {
continue;
}
m[j] = m[j].split(':');
for (i = 0; i < m[j].length; i++) {
m[j][i] = parseInt(m[j][i], 16);
// Would be NaN if it was blank, return false.
if (isNaN(m[j][i])) {
return false; // Invalid IP.
}
m[j][i] = f(m[j][i] >> 8) + f(m[j][i] & 0xFF);
}
m[j] = m[j].join('');
}
x = m[1].length + m[3].length;
if (x === 16) {
return m[1] + m[3];
} else if (x < 16 && m[2].length > 0) {
return m[1] + (new Array(16 - x + 1)).join('\x00') + m[3];
}
}
return false; // Invalid IP.
}