Reimplement CPE debug log
This commit is contained in:
1 parent
f45b56bbc4
commit
7154d2cc7e
10 files changed
+327
-65
No files matched your search
@@ -1,7 +1,6 @@
|
|||||||
*~
|
*~
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
debug
|
|
||||||
config/config.json
|
config/config.json
|
||||||
config/config-ui.json
|
config/config-ui.json
|
||||||
config/cwmp.key
|
config/cwmp.key
|
||||||
|
|||||||
+1
-1
@@ -53,6 +53,7 @@ const externals = [
|
|||||||
"stream",
|
"stream",
|
||||||
"mithril",
|
"mithril",
|
||||||
"parsimmon",
|
"parsimmon",
|
||||||
|
"yaml",
|
||||||
"codemirror",
|
"codemirror",
|
||||||
"codemirror/mode/javascript/javascript",
|
"codemirror/mode/javascript/javascript",
|
||||||
"codemirror/mode/yaml/yaml"
|
"codemirror/mode/yaml/yaml"
|
||||||
@@ -78,7 +79,6 @@ async function init(): Promise<void> {
|
|||||||
fs.mkdirSync(OUTPUT_DIR);
|
fs.mkdirSync(OUTPUT_DIR);
|
||||||
fs.mkdirSync(OUTPUT_DIR + "/bin");
|
fs.mkdirSync(OUTPUT_DIR + "/bin");
|
||||||
fs.mkdirSync(OUTPUT_DIR + "/config");
|
fs.mkdirSync(OUTPUT_DIR + "/config");
|
||||||
fs.mkdirSync(OUTPUT_DIR + "/debug");
|
|
||||||
fs.mkdirSync(OUTPUT_DIR + "/public");
|
fs.mkdirSync(OUTPUT_DIR + "/public");
|
||||||
fs.mkdirSync(OUTPUT_DIR + "/tools");
|
fs.mkdirSync(OUTPUT_DIR + "/tools");
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,5 @@
|
|||||||
"FS_HOSTNAME" : "acs.example.com",
|
"FS_HOSTNAME" : "acs.example.com",
|
||||||
"UI_INTERFACE" : "0.0.0.0",
|
"UI_INTERFACE" : "0.0.0.0",
|
||||||
"UI_PORT" : 3000,
|
"UI_PORT" : 3000,
|
||||||
"UI_JWT_SECRET": "",
|
"UI_JWT_SECRET": ""
|
||||||
"DEBUG" : false
|
|
||||||
}
|
}
|
||||||
Whitespace-only changes.
+12
-2
@@ -17,6 +17,7 @@
|
|||||||
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { parse } from "url";
|
||||||
import * as db from "./db";
|
import * as db from "./db";
|
||||||
import * as common from "./common";
|
import * as common from "./common";
|
||||||
import * as cache from "./cache";
|
import * as cache from "./cache";
|
||||||
@@ -76,6 +77,9 @@ export async function connectionRequest(deviceId): Promise<void> {
|
|||||||
serialNumber: device["_deviceId"]["SerialNumber"],
|
serialNumber: device["_deviceId"]["SerialNumber"],
|
||||||
productClass: device["_deviceId"]["ProductClass"],
|
productClass: device["_deviceId"]["ProductClass"],
|
||||||
oui: device["_deviceId"]["OUI"],
|
oui: device["_deviceId"]["OUI"],
|
||||||
|
remoteAddress: connectionRequestUrl
|
||||||
|
? parse(connectionRequestUrl).host
|
||||||
|
: null,
|
||||||
username: username || "",
|
username: username || "",
|
||||||
password: password || ""
|
password: password || ""
|
||||||
};
|
};
|
||||||
@@ -114,13 +118,17 @@ export async function connectionRequest(deviceId): Promise<void> {
|
|||||||
] as Expression;
|
] as Expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const debug = !!getConfig(snapshot, "cwmp.debug", context, now);
|
||||||
|
|
||||||
let udpProm;
|
let udpProm;
|
||||||
if (udpConnectionRequestAddress) {
|
if (udpConnectionRequestAddress) {
|
||||||
udpProm = udpConnectionRequest(
|
udpProm = udpConnectionRequest(
|
||||||
udpConnectionRequestAddress,
|
udpConnectionRequestAddress,
|
||||||
authExp,
|
authExp,
|
||||||
context,
|
context,
|
||||||
UDP_CONNECTION_REQUEST_PORT
|
UDP_CONNECTION_REQUEST_PORT,
|
||||||
|
debug,
|
||||||
|
deviceId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +138,9 @@ export async function connectionRequest(deviceId): Promise<void> {
|
|||||||
authExp,
|
authExp,
|
||||||
context,
|
context,
|
||||||
CONNECTION_REQUEST_ALLOW_BASIC_AUTH,
|
CONNECTION_REQUEST_ALLOW_BASIC_AUTH,
|
||||||
CONNECTION_REQUEST_TIMEOUT
|
CONNECTION_REQUEST_TIMEOUT,
|
||||||
|
debug,
|
||||||
|
deviceId
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!udpProm) throw err;
|
if (!udpProm) throw err;
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ const options = {
|
|||||||
DOWNLOAD_TIMEOUT: { type: "int", default: 3600 },
|
DOWNLOAD_TIMEOUT: { type: "int", default: 3600 },
|
||||||
EXT_TIMEOUT: { type: "int", default: 3000 },
|
EXT_TIMEOUT: { type: "int", default: 3000 },
|
||||||
MAX_CACHE_TTL: { type: "int", default: 86400 },
|
MAX_CACHE_TTL: { type: "int", default: 86400 },
|
||||||
|
DEBUG_FILE: { type: "path", default: "" },
|
||||||
|
DEBUG_FORMAT: { type: "string", default: "yaml" },
|
||||||
DEBUG: { type: "bool", default: false },
|
DEBUG: { type: "bool", default: false },
|
||||||
RETRY_DELAY: { type: "int", default: 300 },
|
RETRY_DELAY: { type: "int", default: 300 },
|
||||||
SESSION_TIMEOUT: { type: "int", default: 30 },
|
SESSION_TIMEOUT: { type: "int", default: 30 },
|
||||||
|
|||||||
+23
-10
@@ -25,6 +25,7 @@ import { evaluateAsync } from "./common/expression";
|
|||||||
import { Expression } from "./types";
|
import { Expression } from "./types";
|
||||||
import * as auth from "./auth";
|
import * as auth from "./auth";
|
||||||
import * as extensions from "./extensions";
|
import * as extensions from "./extensions";
|
||||||
|
import * as debug from "./debug";
|
||||||
|
|
||||||
async function extractAuth(
|
async function extractAuth(
|
||||||
exp: Expression,
|
exp: Expression,
|
||||||
@@ -62,18 +63,25 @@ async function extractAuth(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function httpGet(
|
function httpGet(
|
||||||
options: {},
|
options: http.RequestOptions,
|
||||||
timeout
|
timeout,
|
||||||
|
_debug: boolean,
|
||||||
|
deviceId: string
|
||||||
): Promise<{ statusCode: number; headers: {} }> {
|
): Promise<{ statusCode: number; headers: {} }> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const req = http
|
const req = http
|
||||||
.get(options, res => {
|
.get(options, res => {
|
||||||
res.resume();
|
res.resume();
|
||||||
resolve({ statusCode: res.statusCode, headers: res.headers });
|
resolve({ statusCode: res.statusCode, headers: res.headers });
|
||||||
|
if (_debug) {
|
||||||
|
debug.outgoingHttpRequest(req, deviceId, options, null);
|
||||||
|
debug.incomingHttpResponse(res, deviceId, null);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.on("error", () => {
|
.on("error", err => {
|
||||||
req.abort();
|
req.abort();
|
||||||
reject(new Error("Device is offline"));
|
reject(new Error("Device is offline"));
|
||||||
|
if (_debug) debug.outgoingHttpRequestError(req, deviceId, options, err);
|
||||||
})
|
})
|
||||||
.on("socket", socket => {
|
.on("socket", socket => {
|
||||||
socket.setTimeout(timeout);
|
socket.setTimeout(timeout);
|
||||||
@@ -89,7 +97,9 @@ export async function httpConnectionRequest(
|
|||||||
authExp: Expression,
|
authExp: Expression,
|
||||||
context: {},
|
context: {},
|
||||||
allowBasicAuth: boolean,
|
allowBasicAuth: boolean,
|
||||||
timeout: number
|
timeout: number,
|
||||||
|
_debug: boolean,
|
||||||
|
deviceId: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const options: http.RequestOptions = parse(address);
|
const options: http.RequestOptions = parse(address);
|
||||||
@@ -141,10 +151,11 @@ export async function httpConnectionRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = await httpGet(opts, timeout);
|
let res = await httpGet(opts, timeout, _debug, deviceId);
|
||||||
|
|
||||||
// Workaround for some devices unexpectedly closing the connection
|
// Workaround for some devices unexpectedly closing the connection
|
||||||
if (res.statusCode === 0 && authHeader) res = await httpGet(opts, timeout);
|
if (res.statusCode === 0 && authHeader)
|
||||||
|
res = await httpGet(opts, timeout, _debug, deviceId);
|
||||||
if (res.statusCode === 0) throw new Error("Device is offline");
|
if (res.statusCode === 0) throw new Error("Device is offline");
|
||||||
if (res.statusCode === 200 || res.statusCode === 204) return;
|
if (res.statusCode === 200 || res.statusCode === 204) return;
|
||||||
|
|
||||||
@@ -171,7 +182,9 @@ export async function udpConnectionRequest(
|
|||||||
address: string,
|
address: string,
|
||||||
authExp: Expression,
|
authExp: Expression,
|
||||||
context: {},
|
context: {},
|
||||||
sourcePort: number = 0
|
sourcePort: number = 0,
|
||||||
|
_debug: boolean,
|
||||||
|
deviceId: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const [host, portStr] = address.split(":", 2);
|
const [host, portStr] = address.split(":", 2);
|
||||||
const port = portStr ? parseInt(portStr) : 80;
|
const port = portStr ? parseInt(portStr) : 80;
|
||||||
@@ -207,15 +220,15 @@ export async function udpConnectionRequest(
|
|||||||
.update(`${ts}${id}${username}${cn}`)
|
.update(`${ts}${id}${username}${cn}`)
|
||||||
.digest("hex");
|
.digest("hex");
|
||||||
const uri = `http://${address}?ts=${ts}&id=${id}&un=${username}&cn=${cn}&sig=${sig}`;
|
const uri = `http://${address}?ts=${ts}&id=${id}&un=${username}&cn=${cn}&sig=${sig}`;
|
||||||
const message = Buffer.from(
|
const msg = `GET ${uri} HTTP/1.1\r\nHost: ${address}\r\n\r\n`;
|
||||||
`GET ${uri} HTTP/1.1\r\nHost: ${address}\r\n\r\n`
|
const message = Buffer.from(msg);
|
||||||
);
|
|
||||||
|
|
||||||
for (let i = 0; i < 3; ++i) {
|
for (let i = 0; i < 3; ++i) {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
client.send(message, 0, message.length, port, host, (err: Error) => {
|
client.send(message, 0, message.length, port, host, (err: Error) => {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
else resolve();
|
else resolve();
|
||||||
|
if (_debug) debug.outgoingUdpMessage(host, deviceId, port, msg);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+81
-46
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
import * as zlib from "zlib";
|
import * as zlib from "zlib";
|
||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
import * as fs from "fs";
|
|
||||||
import { Socket } from "net";
|
import { Socket } from "net";
|
||||||
import * as auth from "./auth";
|
import * as auth from "./auth";
|
||||||
import * as config from "./config";
|
import * as config from "./config";
|
||||||
@@ -53,6 +52,7 @@ import { promisify } from "util";
|
|||||||
import { TLSSocket } from "tls";
|
import { TLSSocket } from "tls";
|
||||||
import { dependencies as DEPENDENCIES } from "../package.json";
|
import { dependencies as DEPENDENCIES } from "../package.json";
|
||||||
import { parseXmlDeclaration } from "./xml-parser";
|
import { parseXmlDeclaration } from "./xml-parser";
|
||||||
|
import * as debug from "./debug";
|
||||||
|
|
||||||
const soap =
|
const soap =
|
||||||
DEPENDENCIES["libxmljs"] && config.get("XML_LIBXMLJS")
|
DEPENDENCIES["libxmljs"] && config.get("XML_LIBXMLJS")
|
||||||
@@ -170,18 +170,6 @@ async function writeResponse(
|
|||||||
// Close connection after last request in session
|
// Close connection after last request in session
|
||||||
if (close) res.headers["Connection"] = "close";
|
if (close) res.headers["Connection"] = "close";
|
||||||
|
|
||||||
if (sessionContext.debug) {
|
|
||||||
const dump =
|
|
||||||
`# RESPONSE ${new Date(Date.now())}\n` +
|
|
||||||
JSON.stringify(res.headers) +
|
|
||||||
"\n" +
|
|
||||||
res.data +
|
|
||||||
"\n\n";
|
|
||||||
fs.appendFile(`./debug/${sessionContext.deviceId}.dump`, dump, err => {
|
|
||||||
if (err) throw err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
|
|
||||||
// Respond using the same content-encoding as the request
|
// Respond using the same content-encoding as the request
|
||||||
@@ -200,9 +188,13 @@ async function writeResponse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.headers["Content-Length"] = data.length;
|
const httpResponse = sessionContext.httpResponse;
|
||||||
sessionContext.httpResponse.writeHead(res.code, res.headers);
|
httpResponse.setHeader("Content-Length", Buffer.byteLength(data));
|
||||||
sessionContext.httpResponse.end(data);
|
httpResponse.writeHead(res.code, res.headers);
|
||||||
|
httpResponse.end(data);
|
||||||
|
|
||||||
|
if (sessionContext.debug)
|
||||||
|
debug.outgoingHttpResponse(httpResponse, sessionContext.deviceId, res.data);
|
||||||
|
|
||||||
if (sessionContext.httpRequest.connection.destroyed) {
|
if (sessionContext.httpRequest.connection.destroyed) {
|
||||||
logger.accessError({
|
logger.accessError({
|
||||||
@@ -950,9 +942,14 @@ async function reportBadState(sessionContext: SessionContext): Promise<void> {
|
|||||||
message: "Bad session state",
|
message: "Bad session state",
|
||||||
sessionContext: sessionContext
|
sessionContext: sessionContext
|
||||||
});
|
});
|
||||||
currentSessions.delete(sessionContext.httpResponse.connection);
|
const httpResponse = sessionContext.httpResponse;
|
||||||
sessionContext.httpResponse.writeHead(400, { Connection: "close" });
|
currentSessions.delete(httpResponse.connection);
|
||||||
sessionContext.httpResponse.end("Bad session state");
|
const body = "Bad session state";
|
||||||
|
httpResponse.setHeader("Content-Length", Buffer.byteLength(body));
|
||||||
|
httpResponse.writeHead(400, { Connection: "close" });
|
||||||
|
httpResponse.end(body);
|
||||||
|
if (sessionContext.debug)
|
||||||
|
debug.outgoingHttpResponse(httpResponse, sessionContext.deviceId, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function responseUnauthorized(
|
async function responseUnauthorized(
|
||||||
@@ -983,8 +980,13 @@ async function responseUnauthorized(
|
|||||||
currentSessions.set(sessionContext.httpRequest.connection, sessionContext);
|
currentSessions.set(sessionContext.httpRequest.connection, sessionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionContext.httpResponse.writeHead(401, resHeaders);
|
const httpResponse = sessionContext.httpResponse;
|
||||||
sessionContext.httpResponse.end("Unauthorized");
|
const body = "Unauthorized";
|
||||||
|
httpResponse.setHeader("Content-Length", Buffer.byteLength(body));
|
||||||
|
httpResponse.writeHead(401, resHeaders);
|
||||||
|
httpResponse.end(body);
|
||||||
|
if (sessionContext.debug)
|
||||||
|
debug.outgoingHttpResponse(httpResponse, sessionContext.deviceId, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processRequest(
|
async function processRequest(
|
||||||
@@ -1176,28 +1178,6 @@ async function listenerAsync(
|
|||||||
// Request aborted
|
// Request aborted
|
||||||
if (!body) return;
|
if (!body) return;
|
||||||
|
|
||||||
async function parsedRpc(sessionContext, rpc, parseWarnings): Promise<void> {
|
|
||||||
for (const w of parseWarnings) {
|
|
||||||
w.sessionContext = sessionContext;
|
|
||||||
w.rpc = rpc;
|
|
||||||
logger.accessWarn(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionContext.debug) {
|
|
||||||
const dump =
|
|
||||||
`# REQUEST ${new Date(Date.now())}\n` +
|
|
||||||
JSON.stringify(httpRequest.headers) +
|
|
||||||
"\n" +
|
|
||||||
body +
|
|
||||||
"\n\n";
|
|
||||||
fs.appendFile(`./debug/${sessionContext.deviceId}.dump`, dump, err => {
|
|
||||||
if (err) throw err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return processRequest(sessionContext, rpc);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newConnection = !currentSessions.has(httpRequest.connection);
|
const newConnection = !currentSessions.has(httpRequest.connection);
|
||||||
|
|
||||||
const sessionContext = await getSession(httpRequest.connection, sessionId);
|
const sessionContext = await getSession(httpRequest.connection, sessionId);
|
||||||
@@ -1214,8 +1194,18 @@ async function listenerAsync(
|
|||||||
sessionContext: sessionContext
|
sessionContext: sessionContext
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const _body = "Invalid session";
|
||||||
|
httpResponse.setHeader("Content-Length", Buffer.byteLength(_body));
|
||||||
httpResponse.writeHead(400, { Connection: "close" });
|
httpResponse.writeHead(400, { Connection: "close" });
|
||||||
httpResponse.end("Invalid session");
|
httpResponse.end(_body);
|
||||||
|
if (sessionContext.debug) {
|
||||||
|
debug.outgoingHttpResponse(
|
||||||
|
httpResponse,
|
||||||
|
sessionContext.deviceId,
|
||||||
|
_body
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (stats.concurrentRequests > MAX_CONCURRENT_REQUESTS) {
|
} else if (stats.concurrentRequests > MAX_CONCURRENT_REQUESTS) {
|
||||||
@@ -1242,6 +1232,19 @@ async function listenerAsync(
|
|||||||
|
|
||||||
const bodyStr = body.toString(charset);
|
const bodyStr = body.toString(charset);
|
||||||
|
|
||||||
|
async function parsedRpc(_sessionContext, rpc, parseWarnings): Promise<void> {
|
||||||
|
for (const w of parseWarnings) {
|
||||||
|
w.sessionContext = _sessionContext;
|
||||||
|
w.rpc = rpc;
|
||||||
|
logger.accessWarn(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sessionContext.debug)
|
||||||
|
debug.incomingHttpRequest(httpRequest, _sessionContext.deviceId, bodyStr);
|
||||||
|
|
||||||
|
return processRequest(_sessionContext, rpc);
|
||||||
|
}
|
||||||
|
|
||||||
const parseWarnings = [];
|
const parseWarnings = [];
|
||||||
let rpc;
|
let rpc;
|
||||||
try {
|
try {
|
||||||
@@ -1259,8 +1262,30 @@ async function listenerAsync(
|
|||||||
httpResponse: httpResponse
|
httpResponse: httpResponse
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
httpResponse.setHeader("Content-Length", Buffer.byteLength(error.message));
|
||||||
httpResponse.writeHead(400, { Connection: "close" });
|
httpResponse.writeHead(400, { Connection: "close" });
|
||||||
httpResponse.end(error.message);
|
httpResponse.end(error.message);
|
||||||
|
if (sessionContext) {
|
||||||
|
if (sessionContext.debug) {
|
||||||
|
debug.outgoingHttpResponse(
|
||||||
|
httpResponse,
|
||||||
|
sessionContext.deviceId,
|
||||||
|
error.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
||||||
|
const d = !!localCache.getConfig(cacheSnapshot, "cwmp.debug", {
|
||||||
|
remoteAddress: httpResponse.connection.remoteAddress
|
||||||
|
});
|
||||||
|
if (d) {
|
||||||
|
debug.outgoingHttpResponse(
|
||||||
|
httpResponse,
|
||||||
|
sessionContext.deviceId,
|
||||||
|
error.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1284,8 +1309,17 @@ async function listenerAsync(
|
|||||||
httpResponse: httpResponse
|
httpResponse: httpResponse
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const _body = "Invalid session";
|
||||||
|
httpResponse.setHeader("Content-Length", Buffer.byteLength(_body));
|
||||||
httpResponse.writeHead(400, { Connection: "close" });
|
httpResponse.writeHead(400, { Connection: "close" });
|
||||||
httpResponse.end("Invalid session");
|
httpResponse.end(_body);
|
||||||
|
const cacheSnapshot = await localCache.getCurrentSnapshot();
|
||||||
|
const d = !!localCache.getConfig(cacheSnapshot, "cwmp.debug", {
|
||||||
|
remoteAddress: httpResponse.connection.remoteAddress
|
||||||
|
});
|
||||||
|
if (d)
|
||||||
|
debug.outgoingHttpResponse(httpResponse, sessionContext.deviceId, _body);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1298,7 +1332,8 @@ async function listenerAsync(
|
|||||||
id: deviceId,
|
id: deviceId,
|
||||||
serialNumber: rpc.cpeRequest.deviceId["SerialNumber"],
|
serialNumber: rpc.cpeRequest.deviceId["SerialNumber"],
|
||||||
productClass: rpc.cpeRequest.deviceId["ProductClass"],
|
productClass: rpc.cpeRequest.deviceId["ProductClass"],
|
||||||
oui: rpc.cpeRequest.deviceId["OUI"]
|
oui: rpc.cpeRequest.deviceId["OUI"],
|
||||||
|
remoteAddress: httpRequest.connection.remoteAddress
|
||||||
};
|
};
|
||||||
|
|
||||||
const sessionTimeout =
|
const sessionTimeout =
|
||||||
|
|||||||
+204
@@ -0,0 +1,204 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2013-2019 Zaid Abdulla
|
||||||
|
*
|
||||||
|
* This file is part of GenieACS.
|
||||||
|
*
|
||||||
|
* GenieACS is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GenieACS is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
IncomingMessage,
|
||||||
|
ServerResponse,
|
||||||
|
ClientRequest,
|
||||||
|
RequestOptions
|
||||||
|
} from "http";
|
||||||
|
import { Socket } from "net";
|
||||||
|
import { appendFileSync } from "fs";
|
||||||
|
import * as yaml from "yaml";
|
||||||
|
import * as config from "./config";
|
||||||
|
|
||||||
|
const DEBUG_FILE = "" + config.get("DEBUG_FILE");
|
||||||
|
const DEBUG_FORMAT = "" + config.get("DEBUG_FORMAT");
|
||||||
|
|
||||||
|
const connectionTimestamps = new WeakMap<Socket, Date>();
|
||||||
|
|
||||||
|
function getConnectionTimestamp(connection: Socket): Date {
|
||||||
|
let t = connectionTimestamps.get(connection);
|
||||||
|
if (!t) {
|
||||||
|
t = new Date();
|
||||||
|
connectionTimestamps.set(connection, t);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function incomingHttpRequest(
|
||||||
|
httpRequest: IncomingMessage,
|
||||||
|
deviceId: string,
|
||||||
|
body: string
|
||||||
|
): void {
|
||||||
|
if (!DEBUG_FILE) return;
|
||||||
|
const now = new Date();
|
||||||
|
const con = httpRequest.connection;
|
||||||
|
const msg = {
|
||||||
|
event: "incoming HTTP request",
|
||||||
|
timestamp: now,
|
||||||
|
remoteAddress: con.remoteAddress,
|
||||||
|
deviceId: deviceId,
|
||||||
|
connection: getConnectionTimestamp(con),
|
||||||
|
localPort: con.localPort,
|
||||||
|
method: httpRequest.method,
|
||||||
|
url: httpRequest.url,
|
||||||
|
headers: httpRequest.headers,
|
||||||
|
body: body
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DEBUG_FORMAT === "yaml")
|
||||||
|
appendFileSync(DEBUG_FILE, "---\n" + yaml.stringify(msg));
|
||||||
|
else if (DEBUG_FORMAT === "json")
|
||||||
|
appendFileSync(DEBUG_FILE, JSON.stringify(msg) + "\n");
|
||||||
|
else throw new Error(`Unrecognized DEBUG_FORMAT option`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function outgoingHttpResponse(
|
||||||
|
httpResponse: ServerResponse,
|
||||||
|
deviceId: string,
|
||||||
|
body: string
|
||||||
|
): void {
|
||||||
|
if (!DEBUG_FILE) return;
|
||||||
|
const now = new Date();
|
||||||
|
const con = httpResponse.connection;
|
||||||
|
const msg = {
|
||||||
|
event: "outgoing HTTP response",
|
||||||
|
timestamp: now,
|
||||||
|
remoteAddress: con.remoteAddress,
|
||||||
|
deviceId: deviceId,
|
||||||
|
connection: getConnectionTimestamp(con),
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
headers: httpResponse.getHeaders(),
|
||||||
|
body: body
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DEBUG_FORMAT === "yaml")
|
||||||
|
appendFileSync(DEBUG_FILE, "---\n" + yaml.stringify(msg));
|
||||||
|
else if (DEBUG_FORMAT === "json")
|
||||||
|
appendFileSync(DEBUG_FILE, JSON.stringify(msg) + "\n");
|
||||||
|
else throw new Error(`Unrecognized DEBUG_FORMAT option`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function outgoingHttpRequest(
|
||||||
|
httpRequest: ClientRequest,
|
||||||
|
deviceId: string,
|
||||||
|
options: RequestOptions,
|
||||||
|
body: string
|
||||||
|
): void {
|
||||||
|
if (!DEBUG_FILE) return;
|
||||||
|
const now = new Date();
|
||||||
|
const con = httpRequest.connection;
|
||||||
|
const msg = {
|
||||||
|
event: "outgoing HTTP request",
|
||||||
|
timestamp: now,
|
||||||
|
remoteAddress: con.remoteAddress,
|
||||||
|
deviceId: deviceId,
|
||||||
|
connection: getConnectionTimestamp(con),
|
||||||
|
remotePort: options.port,
|
||||||
|
method: options.method || "GET",
|
||||||
|
url: options.path,
|
||||||
|
headers: httpRequest.getHeaders(),
|
||||||
|
body: body
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DEBUG_FORMAT === "yaml")
|
||||||
|
appendFileSync(DEBUG_FILE, "---\n" + yaml.stringify(msg));
|
||||||
|
else if (DEBUG_FORMAT === "json")
|
||||||
|
appendFileSync(DEBUG_FILE, JSON.stringify(msg) + "\n");
|
||||||
|
else throw new Error(`Unrecognized DEBUG_FORMAT option`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function outgoingHttpRequestError(
|
||||||
|
httpRequest: ClientRequest,
|
||||||
|
deviceId: string,
|
||||||
|
options: RequestOptions,
|
||||||
|
err: Error
|
||||||
|
): void {
|
||||||
|
if (!DEBUG_FILE) return;
|
||||||
|
const now = new Date();
|
||||||
|
const msg = {
|
||||||
|
event: "outgoing HTTP request",
|
||||||
|
timestamp: now,
|
||||||
|
remoteAddress: options.hostname,
|
||||||
|
deviceId: deviceId,
|
||||||
|
connection: null,
|
||||||
|
remotePort: options.port,
|
||||||
|
method: options.method,
|
||||||
|
url: options.path,
|
||||||
|
headers: httpRequest.getHeaders(),
|
||||||
|
error: err.message
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DEBUG_FORMAT === "yaml")
|
||||||
|
appendFileSync(DEBUG_FILE, "---\n" + yaml.stringify(msg));
|
||||||
|
else if (DEBUG_FORMAT === "json")
|
||||||
|
appendFileSync(DEBUG_FILE, JSON.stringify(msg) + "\n");
|
||||||
|
else throw new Error(`Unrecognized DEBUG_FORMAT option`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function incomingHttpResponse(
|
||||||
|
httpResponse: IncomingMessage,
|
||||||
|
deviceId: string,
|
||||||
|
body: string
|
||||||
|
): void {
|
||||||
|
if (!DEBUG_FILE) return;
|
||||||
|
const now = new Date();
|
||||||
|
const con = httpResponse.connection;
|
||||||
|
const msg = {
|
||||||
|
event: "incoming HTTP response",
|
||||||
|
timestamp: now,
|
||||||
|
remoteAddress: con.remoteAddress,
|
||||||
|
deviceId: deviceId,
|
||||||
|
connection: getConnectionTimestamp(httpResponse.connection),
|
||||||
|
statusCode: httpResponse.statusCode,
|
||||||
|
headers: httpResponse.headers,
|
||||||
|
body: body
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DEBUG_FORMAT === "yaml")
|
||||||
|
appendFileSync(DEBUG_FILE, "---\n" + yaml.stringify(msg));
|
||||||
|
else if (DEBUG_FORMAT === "json")
|
||||||
|
appendFileSync(DEBUG_FILE, JSON.stringify(msg) + "\n");
|
||||||
|
else throw new Error(`Unrecognized DEBUG_FORMAT option`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function outgoingUdpMessage(
|
||||||
|
remoteAddress: string,
|
||||||
|
deviceId: string,
|
||||||
|
remotePort: number,
|
||||||
|
body: string
|
||||||
|
): void {
|
||||||
|
if (!DEBUG_FILE) return;
|
||||||
|
const now = new Date();
|
||||||
|
const msg = {
|
||||||
|
event: "outgoing UDP message",
|
||||||
|
timestamp: now,
|
||||||
|
remoteAddress: remoteAddress,
|
||||||
|
deviceId: deviceId,
|
||||||
|
remotePort: remotePort,
|
||||||
|
body: body
|
||||||
|
};
|
||||||
|
|
||||||
|
if (DEBUG_FORMAT === "yaml")
|
||||||
|
appendFileSync(DEBUG_FILE, "---\n" + yaml.stringify(msg));
|
||||||
|
else if (DEBUG_FORMAT === "json")
|
||||||
|
appendFileSync(DEBUG_FILE, JSON.stringify(msg) + "\n");
|
||||||
|
else throw new Error(`Unrecognized DEBUG_FORMAT option`);
|
||||||
|
}
|
||||||
+3
-3
@@ -31,7 +31,8 @@
|
|||||||
"libxmljs": "^0.19.5",
|
"libxmljs": "^0.19.5",
|
||||||
"mongodb": "^3.1.13",
|
"mongodb": "^3.1.13",
|
||||||
"parsimmon": "^1.12.0",
|
"parsimmon": "^1.12.0",
|
||||||
"seedrandom": "^2.4.4"
|
"seedrandom": "^2.4.4",
|
||||||
|
"yaml": "^1.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jsonwebtoken": "^8.3.0",
|
"@types/jsonwebtoken": "^8.3.0",
|
||||||
@@ -62,8 +63,7 @@
|
|||||||
"rollup-plugin-typescript": "^1.0.0",
|
"rollup-plugin-typescript": "^1.0.0",
|
||||||
"ts-node": "^8.0.2",
|
"ts-node": "^8.0.2",
|
||||||
"typescript": "^3.3.3",
|
"typescript": "^3.3.3",
|
||||||
"webpack": "^4.29.5",
|
"webpack": "^4.29.5"
|
||||||
"yaml": "^1.3.2"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.14.0"
|
"node": ">=8.14.0"
|
||||||
|
|||||||
Reference in new issue
Block a user