void CheckResponseAndStoreState()
{
if(NoBotState.InvalidUnknown != _state) return;
try {
// Report valid when page first loaded (to avoid breaking pages that unconditionally check IsValid)
if(!Page.IsPostBack) {
_state = NoBotState.Valid;
return;
}
// Report invalid if response arrives too soon
var responseTime = (DateTime)ViewState[ResponseTimeKey];
var utcNow = DateTime.UtcNow;
if(utcNow < responseTime) {
_state = NoBotState.InvalidResponseTooSoon;
return;
}
// Report invalid if too many responses from same IP address
// NOTE: The performance of the following code can be improved
// if performance becomes an issue.
lock(_pastAddresses) {
// Add user address to address cache, taking care not to duplicate keys
var userAddress = Page.Request.UserHostAddress;
var utcAdd = utcNow;
while(_pastAddresses.ContainsKey(utcAdd))
utcAdd = utcAdd.AddTicks(1);
_pastAddresses.Add(utcAdd, userAddress);
// Calculate cutoff window for cached addresses
var utcCutoff = utcNow.AddSeconds(-_cutoffWindowSeconds);
// Determine number of expired addresses
var cutoffs = 0;
foreach(var time in _pastAddresses.Keys) {
if(time < utcCutoff)
cutoffs++;
else
break;
}
// Remove expired addresses
while(0 < cutoffs) {
_pastAddresses.RemoveAt(0);
cutoffs--;
}
// Determine number of instances of user address in cache
var instances = 0;
foreach(var address in _pastAddresses.Values) {
if(userAddress == address)
instances++;
}
// Fail if too many
if(_cutoffMaximumInstances < instances) {
_state = NoBotState.InvalidAddressTooActive;
return;
}
}
// Report invalid if response is wrong
var sessionKey = (string)ViewState[SessionKeyKey];
var requiredResponse = (string)Page.Session[sessionKey];
Page.Session.Remove(sessionKey);
if(requiredResponse != _extender.ClientState) {
_state = NoBotState.InvalidBadResponse;
return;
}
// All checks OK, report valid
_state = NoBotState.Valid;
}
catch(NullReferenceException) {
_state = NoBotState.InvalidBadSession;
}
}