Ignore session cookie on Inform
Some devices preserve cookies set in a previous session and pass them on to the next session. The presence of a session cookie in the Inform request causes an unnecessary DB query and, in some edge cases, an Invalid Session error.
This commit is contained in:
1 parent
bc2d258f9c
commit
ca83704958
2 files changed
+111
-142
No files matched your search
+109
-139
@@ -878,27 +878,6 @@ async function sendAcsRequest(
|
||||
return writeResponse(sessionContext, res);
|
||||
}
|
||||
|
||||
async function getSession(
|
||||
connection: Socket,
|
||||
sessionId: string
|
||||
): Promise<SessionContext> {
|
||||
let sessionContext = currentSessions.get(connection);
|
||||
if (sessionContext) {
|
||||
currentSessions.delete(connection);
|
||||
return sessionContext;
|
||||
}
|
||||
|
||||
if (!sessionId) return null;
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
const sessionContextString = await cache.pop(`session_${sessionId}`);
|
||||
if (!sessionContextString) return null;
|
||||
sessionContext = await session.deserialize(sessionContextString);
|
||||
connection.setTimeout(sessionContext.timeout * 1000);
|
||||
return sessionContext;
|
||||
}
|
||||
|
||||
// Only needed to prevent tree shaking from removing the remoteAddress
|
||||
// workaround in onConnection function.
|
||||
const remoteAddressWorkaround = new WeakMap<Socket, string>();
|
||||
@@ -1045,13 +1024,13 @@ async function reportBadState(sessionContext: SessionContext): Promise<void> {
|
||||
sessionContext: sessionContext,
|
||||
});
|
||||
const httpResponse = sessionContext.httpResponse;
|
||||
currentSessions.delete(httpResponse.socket);
|
||||
const body = "Bad session state";
|
||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(body));
|
||||
httpResponse.writeHead(400, { Connection: "close" });
|
||||
if (sessionContext.debug)
|
||||
debug.outgoingHttpResponse(httpResponse, sessionContext.deviceId, body);
|
||||
httpResponse.end(body);
|
||||
return endSession(sessionContext);
|
||||
}
|
||||
|
||||
async function responseUnauthorized(
|
||||
@@ -1323,6 +1302,48 @@ export function listener(
|
||||
});
|
||||
}
|
||||
|
||||
async function clientError(
|
||||
httpRequest: IncomingMessage,
|
||||
httpResponse: ServerResponse,
|
||||
sessionContext: SessionContext,
|
||||
body: string,
|
||||
msg: string
|
||||
): Promise<void> {
|
||||
let debugEnabled = false;
|
||||
let deviceId: string = null;
|
||||
|
||||
if (sessionContext) {
|
||||
debugEnabled = sessionContext.debug;
|
||||
deviceId = sessionContext.deviceId;
|
||||
} else {
|
||||
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
||||
debugEnabled = !!localCache.getConfig(
|
||||
cacheSnapshot,
|
||||
"cwmp.debug",
|
||||
{
|
||||
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
|
||||
},
|
||||
Date.now(),
|
||||
(e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
||||
return getRequestOrigin(httpRequest).remoteAddress;
|
||||
return e;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(msg));
|
||||
httpResponse.writeHead(400, { Connection: "close" });
|
||||
|
||||
if (debugEnabled) {
|
||||
debug.incomingHttpRequest(httpRequest, deviceId, body);
|
||||
debug.outgoingHttpResponse(httpResponse, deviceId, msg);
|
||||
}
|
||||
|
||||
httpResponse.end(msg);
|
||||
if (sessionContext) await endSession(sessionContext);
|
||||
}
|
||||
|
||||
function decodeString(buffer: Buffer, charset: string): string {
|
||||
try {
|
||||
return buffer.toString(charset as BufferEncoding);
|
||||
@@ -1410,15 +1431,14 @@ async function listenerAsync(
|
||||
// Request aborted
|
||||
if (!body) return;
|
||||
|
||||
const newConnection = !currentSessions.has(httpRequest.socket);
|
||||
|
||||
const sessionContext = await getSession(httpRequest.socket, sessionId);
|
||||
let sessionContext = currentSessions.get(httpRequest.socket);
|
||||
|
||||
if (sessionContext) {
|
||||
currentSessions.delete(httpRequest.socket);
|
||||
sessionContext.httpRequest = httpRequest;
|
||||
sessionContext.httpResponse = httpResponse;
|
||||
if (
|
||||
(newConnection && sessionContext.sessionId !== sessionId) ||
|
||||
sessionContext.sessionId !== sessionId ||
|
||||
sessionContext.lastActivity + sessionContext.timeout * 1000 < Date.now()
|
||||
) {
|
||||
logger.accessError({
|
||||
@@ -1426,29 +1446,14 @@ async function listenerAsync(
|
||||
sessionContext: sessionContext,
|
||||
});
|
||||
|
||||
const _body = "Invalid session";
|
||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(_body));
|
||||
httpResponse.writeHead(400, { Connection: "close" });
|
||||
if (sessionContext.debug) {
|
||||
debug.outgoingHttpResponse(
|
||||
return clientError(
|
||||
httpRequest,
|
||||
httpResponse,
|
||||
sessionContext.deviceId,
|
||||
_body
|
||||
sessionContext,
|
||||
body.toString(),
|
||||
"Invalid session"
|
||||
);
|
||||
}
|
||||
httpResponse.end(_body);
|
||||
await endSession(sessionContext);
|
||||
return;
|
||||
}
|
||||
if (newConnection && sessionContext.authState !== 1)
|
||||
sessionContext.authState = 0;
|
||||
} else if (stats.concurrentRequests > MAX_CONCURRENT_REQUESTS) {
|
||||
// Check again just in case device included old session ID
|
||||
// from the previous session
|
||||
httpResponse.writeHead(503, { "Retry-after": 60, Connection: "close" });
|
||||
httpResponse.end("503 Service Unavailable");
|
||||
stats.droppedRequests += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
let charset: string;
|
||||
@@ -1468,6 +1473,16 @@ async function listenerAsync(
|
||||
const bodyStr = decodeString(body, charset);
|
||||
|
||||
if (bodyStr == null) {
|
||||
if (!sessionContext && sessionId) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
const sessionContextString = await cache.pop(`session_${sessionId}`);
|
||||
if (sessionContextString) {
|
||||
sessionContext = await session.deserialize(sessionContextString);
|
||||
sessionContext.httpRequest = httpRequest;
|
||||
sessionContext.httpResponse = httpResponse;
|
||||
}
|
||||
}
|
||||
|
||||
const msg = `Unknown encoding '${charset}'`;
|
||||
logger.accessError({
|
||||
message: "XML parse error",
|
||||
@@ -1477,97 +1492,58 @@ async function listenerAsync(
|
||||
httpResponse: httpResponse,
|
||||
},
|
||||
});
|
||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(msg));
|
||||
httpResponse.writeHead(400, { Connection: "close" });
|
||||
if (sessionContext) {
|
||||
if (sessionContext.debug) {
|
||||
debug.incomingHttpRequest(
|
||||
return clientError(
|
||||
httpRequest,
|
||||
sessionContext.deviceId,
|
||||
body.toString()
|
||||
httpResponse,
|
||||
sessionContext,
|
||||
body.toString(),
|
||||
msg
|
||||
);
|
||||
debug.outgoingHttpResponse(httpResponse, sessionContext.deviceId, msg);
|
||||
}
|
||||
await endSession(sessionContext);
|
||||
} else {
|
||||
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
||||
const d = !!localCache.getConfig(
|
||||
cacheSnapshot,
|
||||
"cwmp.debug",
|
||||
{
|
||||
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
|
||||
},
|
||||
Date.now(),
|
||||
(e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
||||
return getRequestOrigin(httpRequest).remoteAddress;
|
||||
return e;
|
||||
}
|
||||
);
|
||||
if (d) {
|
||||
debug.incomingHttpRequest(httpRequest, null, body.toString());
|
||||
debug.outgoingHttpResponse(httpResponse, null, msg);
|
||||
}
|
||||
}
|
||||
httpResponse.end(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const parseWarnings = [];
|
||||
let rpc: SoapMessage;
|
||||
try {
|
||||
rpc = soap.request(
|
||||
bodyStr,
|
||||
sessionContext ? sessionContext.cwmpVersion : null,
|
||||
parseWarnings
|
||||
);
|
||||
} catch (error) {
|
||||
rpc = soap.request(bodyStr, parseWarnings);
|
||||
} catch (err) {
|
||||
if (!sessionContext && sessionId) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
const sessionContextString = await cache.pop(`session_${sessionId}`);
|
||||
if (sessionContextString) {
|
||||
sessionContext = await session.deserialize(sessionContextString);
|
||||
sessionContext.httpRequest = httpRequest;
|
||||
sessionContext.httpResponse = httpResponse;
|
||||
}
|
||||
}
|
||||
|
||||
logger.accessError({
|
||||
message: "XML parse error",
|
||||
parseError: error.message.trim(),
|
||||
parseError: err.message,
|
||||
sessionContext: sessionContext || {
|
||||
httpRequest: httpRequest,
|
||||
httpResponse: httpResponse,
|
||||
},
|
||||
});
|
||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(error.message));
|
||||
httpResponse.writeHead(400, { Connection: "close" });
|
||||
if (sessionContext) {
|
||||
if (sessionContext.debug) {
|
||||
debug.incomingHttpRequest(
|
||||
|
||||
return clientError(
|
||||
httpRequest,
|
||||
sessionContext.deviceId,
|
||||
bodyStr
|
||||
);
|
||||
debug.outgoingHttpResponse(
|
||||
httpResponse,
|
||||
sessionContext.deviceId,
|
||||
error.message
|
||||
sessionContext,
|
||||
bodyStr,
|
||||
err.message
|
||||
);
|
||||
}
|
||||
await endSession(sessionContext);
|
||||
} else {
|
||||
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
||||
const d = !!localCache.getConfig(
|
||||
cacheSnapshot,
|
||||
"cwmp.debug",
|
||||
{
|
||||
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
|
||||
},
|
||||
Date.now(),
|
||||
(e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
||||
return getRequestOrigin(httpRequest).remoteAddress;
|
||||
return e;
|
||||
|
||||
if (!sessionContext && sessionId && rpc.cpeRequest?.name !== "Inform") {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
const sessionContextString = await cache.pop(`session_${sessionId}`);
|
||||
if (sessionContextString) {
|
||||
sessionContext = await session.deserialize(sessionContextString);
|
||||
sessionContext.httpRequest = httpRequest;
|
||||
sessionContext.httpResponse = httpResponse;
|
||||
httpRequest.socket.setTimeout(sessionContext.timeout * 1000);
|
||||
if (sessionContext.authState !== 1) sessionContext.authState = 0;
|
||||
}
|
||||
);
|
||||
if (d) {
|
||||
debug.incomingHttpRequest(httpRequest, null, bodyStr);
|
||||
debug.outgoingHttpResponse(httpResponse, null, error.message);
|
||||
}
|
||||
}
|
||||
httpResponse.end(error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sessionContext)
|
||||
@@ -1576,33 +1552,27 @@ async function listenerAsync(
|
||||
if (!(rpc.cpeRequest && rpc.cpeRequest.name === "Inform")) {
|
||||
logger.accessError({
|
||||
message: "Invalid session",
|
||||
sessionContext: sessionContext || {
|
||||
sessionContext: {
|
||||
httpRequest: httpRequest,
|
||||
httpResponse: httpResponse,
|
||||
},
|
||||
});
|
||||
const _body = "Invalid session";
|
||||
httpResponse.setHeader("Content-Length", Buffer.byteLength(_body));
|
||||
httpResponse.writeHead(400, { Connection: "close" });
|
||||
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
||||
const d = !!localCache.getConfig(
|
||||
cacheSnapshot,
|
||||
"cwmp.debug",
|
||||
{
|
||||
remoteAddress: getRequestOrigin(httpRequest).remoteAddress,
|
||||
},
|
||||
Date.now(),
|
||||
(e) => {
|
||||
if (Array.isArray(e) && e[0] === "FUNC" && e[1] === "REMOTE_ADDRESS")
|
||||
return getRequestOrigin(httpRequest).remoteAddress;
|
||||
return e;
|
||||
}
|
||||
|
||||
return clientError(
|
||||
httpRequest,
|
||||
httpResponse,
|
||||
null,
|
||||
bodyStr,
|
||||
"Invalid session"
|
||||
);
|
||||
if (d) {
|
||||
debug.incomingHttpRequest(httpRequest, null, bodyStr);
|
||||
debug.outgoingHttpResponse(httpResponse, null, _body);
|
||||
}
|
||||
httpResponse.end(_body);
|
||||
|
||||
if (stats.concurrentRequests > MAX_CONCURRENT_REQUESTS) {
|
||||
// Check again just in case device included old session ID
|
||||
// from the previous session
|
||||
httpResponse.writeHead(503, { "Retry-after": 60, Connection: "close" });
|
||||
httpResponse.end("503 Service Unavailable");
|
||||
stats.droppedRequests += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -827,14 +827,13 @@ function fault(xml: Element): CpeFault {
|
||||
|
||||
export function request(
|
||||
body: string,
|
||||
cwmpVersion: string,
|
||||
warn: Record<string, unknown>[]
|
||||
): SoapMessage {
|
||||
warnings = warn;
|
||||
|
||||
const rpc = {
|
||||
id: null,
|
||||
cwmpVersion: cwmpVersion,
|
||||
cwmpVersion: null,
|
||||
sessionTimeout: null,
|
||||
cpeRequest: null,
|
||||
cpeFault: null,
|
||||
@@ -877,7 +876,7 @@ export function request(
|
||||
|
||||
const methodElement = bodyElement.children[0];
|
||||
|
||||
if (!rpc.cwmpVersion && methodElement.localName !== "Fault") {
|
||||
if (methodElement.localName === "Inform") {
|
||||
let namespace, namespaceHref;
|
||||
for (const e of [methodElement, bodyElement, envelope]) {
|
||||
namespace = namespace || e.namespace;
|
||||
|
||||
Reference in new issue
Block a user