forked from cory/tildefriends
Add some protection against bad requests. Also bail if we can't start properly.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3870 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
2826efea56
commit
87224d2bb6
@ -603,6 +603,8 @@ loadSettings().then(function() {
|
||||
httpd.registerSocketHandler("/app/socket", app.socket);
|
||||
}).catch(function(error) {
|
||||
print('Failed to load settings.');
|
||||
print(error);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
exports.getSessionProcessBlob = getSessionProcessBlob;
|
||||
|
@ -2,9 +2,13 @@
|
||||
|
||||
var gHandlers = [];
|
||||
var gSocketHandlers = [];
|
||||
var gBadRequests = {};
|
||||
|
||||
function logError(error) {
|
||||
print("ERROR " + error);
|
||||
if (error.stackTrace) {
|
||||
print(error.stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
function addHandler(handler) {
|
||||
@ -48,7 +52,7 @@ function Request(method, uri, version, headers, body, client) {
|
||||
this.uri = uri;
|
||||
this.query = undefined;
|
||||
}
|
||||
this.version = version;
|
||||
this.version = version || '';
|
||||
this.headers = headers;
|
||||
this.client = {peerName: client.peerName, tls: client.tls};
|
||||
this.body = body;
|
||||
@ -127,7 +131,7 @@ function Response(request, client) {
|
||||
}
|
||||
headerString += "\r\n";
|
||||
_started = true;
|
||||
client.write(headerString);
|
||||
client.write(headerString).catch(function() {});
|
||||
},
|
||||
end: function(data) {
|
||||
if (_finished) {
|
||||
@ -135,25 +139,24 @@ function Response(request, client) {
|
||||
}
|
||||
if (data) {
|
||||
if (_chunked) {
|
||||
client.write(data.length.toString(16) + "\r\n" + data + "\r\n" + "0\r\n\r\n");
|
||||
client.write(data.length.toString(16) + "\r\n" + data + "\r\n" + "0\r\n\r\n").catch(function() {});
|
||||
} else {
|
||||
client.write(data);
|
||||
client.write(data).catch(function() {});
|
||||
}
|
||||
} else if (_chunked) {
|
||||
client.write("0\r\n\r\n");
|
||||
client.write("0\r\n\r\n").catch(function() {});
|
||||
}
|
||||
_finished = true;
|
||||
if (!_keepAlive) {
|
||||
client.shutdown();
|
||||
client.shutdown().catch(function() {});
|
||||
}
|
||||
},
|
||||
reportError: function(error) {
|
||||
if (!_started) {
|
||||
client.write("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n");
|
||||
client.write("HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain; charset=utf-8\r\n\r\n").catch(function() {});
|
||||
}
|
||||
if (!_finished) {
|
||||
client.write("500 Internal Server Error\r\n\r\n" + error.stackTrace);
|
||||
client.shutdown();
|
||||
client.write("500 Internal Server Error\r\n\r\n" + error?.stackTrace).catch(function() {});
|
||||
}
|
||||
logError(client.peerName + " - - [" + new Date() + "] " + error);
|
||||
},
|
||||
@ -175,7 +178,6 @@ function handleRequest(request, response) {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
print(error);
|
||||
response.reportError(error);
|
||||
}
|
||||
} else {
|
||||
@ -344,7 +346,46 @@ function webSocketAcceptResponse(key) {
|
||||
return binary;
|
||||
}
|
||||
|
||||
function badRequest(client, reason) {
|
||||
var now = new Date();
|
||||
var count = 0;
|
||||
var old = gBadRequests[client.peerName];
|
||||
if (!old) {
|
||||
gBadRequests[client.peerName] = {
|
||||
expire: new Date(now.getTime() + 10 * 60 * 1000),
|
||||
count: 1,
|
||||
};
|
||||
count = 1;
|
||||
} else {
|
||||
old.count++;
|
||||
count = old.count;
|
||||
}
|
||||
new Response({version: '1.0'}, client).reportError(reason + ': ' + count);
|
||||
client.close();
|
||||
}
|
||||
|
||||
function allowRequest(client) {
|
||||
var old = gBadRequests[client.peerName];
|
||||
if (old) {
|
||||
var now = new Date();
|
||||
if (old.expire < now) {
|
||||
delete gBadRequests[client.peerName];
|
||||
return true;
|
||||
} else {
|
||||
return old.count < 3;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleConnection(client) {
|
||||
if (!allowRequest(client)) {
|
||||
print('Rejecting client for too many bad requests: ', client.peerName);
|
||||
client.close();
|
||||
return;
|
||||
}
|
||||
|
||||
var inputBuffer = new Uint8Array(0);
|
||||
var request;
|
||||
var headers = {};
|
||||
@ -378,7 +419,16 @@ function handleConnection(client) {
|
||||
if (bodyToRead == -1) {
|
||||
line = utf8Decode(line);
|
||||
if (!request) {
|
||||
if (!line) {
|
||||
badRequest(client, 'Empty request.');
|
||||
return false;
|
||||
}
|
||||
request = line.split(' ');
|
||||
if (request.length != 3 || !request[2].startsWith('HTTP/1.')) {
|
||||
badRequest(client, 'Bad request.');
|
||||
request = null;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (line) {
|
||||
var colon = line.indexOf(':');
|
||||
@ -390,6 +440,10 @@ function handleConnection(client) {
|
||||
if (headers["content-length"] != undefined) {
|
||||
bodyToRead = parseInt(headers["content-length"]);
|
||||
lineByLine = false;
|
||||
if (bodyToRead > 16 * 1024 * 1024) {
|
||||
badRequest(client, 'Reuqest too large: ' + bodyToRead + '.');
|
||||
return false;
|
||||
}
|
||||
body = new Uint8Array(bodyToRead);
|
||||
return true;
|
||||
} else if (headers["connection"]
|
||||
@ -426,6 +480,7 @@ function handleConnection(client) {
|
||||
|
||||
client.read(function(data) {
|
||||
if (data) {
|
||||
const kMaxLineLength = 4096;
|
||||
var newBuffer = new Uint8Array(inputBuffer.length + data.length);
|
||||
newBuffer.set(inputBuffer, 0);
|
||||
newBuffer.set(data, inputBuffer.length);
|
||||
@ -443,6 +498,10 @@ function handleConnection(client) {
|
||||
if (end > 0 && inputBuffer[end - 1] == carriageReturn) {
|
||||
--end;
|
||||
}
|
||||
if (end > kMaxLineLength || end == -1 && inputBuffer.length > kMaxLineLength) {
|
||||
badRequest(client, 'Request too long.');
|
||||
return;
|
||||
}
|
||||
if (end != -1) {
|
||||
var line = inputBuffer.slice(0, end);
|
||||
inputBuffer = inputBuffer.slice(realEnd + 1);
|
||||
|
Loading…
Reference in New Issue
Block a user