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