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:
		@@ -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);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user