vodafone-station-cli
Version:
Access your Vodafone Station from the comfort of the command line.
183 lines (182 loc) • 7.15 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Arris = void 0;
exports.normalizeChannelStatus = normalizeChannelStatus;
exports.normalizeDocsisStatus = normalizeDocsisStatus;
const modem_1 = require("./modem");
const crypto_1 = require("./tools/crypto");
const html_parser_1 = require("./tools/html-parser");
function normalizeChannelStatus(channelStatus) {
const frequency = {};
if (channelStatus.ChannelType === 'SC-QAM') {
frequency.frequency = channelStatus.Frequency;
}
if (['OFDM', 'OFDMA'].includes(channelStatus.ChannelType)) {
const ofdmaFrequency = String(channelStatus.Frequency).split('~');
frequency.frequencyStart = Number(ofdmaFrequency[0]);
frequency.frequencyEnd = Number(ofdmaFrequency[1]);
}
return {
channelId: channelStatus.ChannelID,
channelType: channelStatus.ChannelType,
lockStatus: channelStatus.LockStatus,
modulation: channelStatus.Modulation,
powerLevel: Number.parseFloat(channelStatus.PowerLevel.split('/')[0]),
snr: Number.parseInt(`${channelStatus.SNRLevel ?? 0}`, 10),
...frequency,
};
}
function normalizeDocsisStatus(arrisDocsisStatus) {
const result = {
downstream: [],
downstreamOfdm: [],
time: arrisDocsisStatus.time,
upstream: [],
upstreamOfdma: [],
};
result.downstream = arrisDocsisStatus.downstream
.filter(downstream => downstream.ChannelType === 'SC-QAM')
.map(channel => normalizeChannelStatus(channel));
result.downstreamOfdm = arrisDocsisStatus.downstream
.filter(downstream => downstream.ChannelType === 'OFDM')
.map(channel => normalizeChannelStatus(channel));
result.upstream = arrisDocsisStatus.upstream
.filter(upstream => upstream.ChannelType === 'SC-QAM')
.map(channel => normalizeChannelStatus(channel));
result.upstreamOfdma = arrisDocsisStatus.upstream
.filter(upstream => upstream.ChannelType === 'OFDMA')
.map(channel => normalizeChannelStatus(channel));
return result;
}
class Arris extends modem_1.Modem {
modemIp;
protocol;
logger;
csrfNonce = '';
constructor(modemIp, protocol, logger) {
super(modemIp, protocol, logger);
this.modemIp = modemIp;
this.protocol = protocol;
this.logger = logger;
}
async addCredentialToCookie() {
const credential = await this.fetchCredential();
this.logger.debug('Credential: ', credential);
// set obligatory static cookie
this.cookieJar.setCookie(`credential= ${credential}`, this.baseUrl);
}
async createServerRecord(setPasswordRequest) {
try {
const { data } = await this.httpClient.post('/php/ajaxSet_Password.php', setPasswordRequest);
// TODO handle wrong password case
// { p_status: 'Lockout', p_waitTime: 1 }
if (data.p_status === 'Lockout') {
throw new Error(`Remote user locked out for: ${data.p_waitTime}s`);
}
return data;
}
catch (error) {
this.logger.error('Could not pass password on remote router.', error);
throw error;
}
}
async docsis() {
if (!this.csrfNonce) {
throw new Error('A valid csrfNonce is required in order to query the modem.');
}
try {
const { data } = await this.httpClient.get('/php/status_docsis_data.php', {
headers: {
Connection: 'keep-alive',
csrfNonce: this.csrfNonce,
Referer: `${this.baseUrl}/?status_docsis&mid=StatusDocsis`,
},
});
return normalizeDocsisStatus((0, html_parser_1.extractDocsisStatus)(data));
}
catch (error) {
this.logger.error('Could not fetch remote docsis status', error);
throw error;
}
}
encryptPassword(password, cryptoVars) {
const jsData = `{"Password": "${password}", "Nonce": "${cryptoVars.sessionId}"}`;
const key = (0, crypto_1.deriveKey)(password, cryptoVars.salt);
const authData = 'loginPassword';
const encryptData = (0, crypto_1.encrypt)(key, jsData, cryptoVars.iv, authData);
return {
AuthData: authData,
EncryptData: encryptData,
Name: modem_1.Modem.USERNAME,
};
}
async fetchCredential() {
try {
const { data } = await this.httpClient.get('/base_95x.js');
return (0, html_parser_1.extractCredentialString)(data);
}
catch (error) {
this.logger.error('Could not fetch credential.', error);
throw error;
}
}
async getCurrentCryptoVars() {
try {
const { data } = await this.httpClient.get('/', {
headers: { Accept: 'text/html,application/xhtml+xml,application/xml' },
});
const cryptoVars = (0, html_parser_1.extractCryptoVars)(data);
this.logger.debug('Parsed crypto vars: ', cryptoVars);
return cryptoVars;
}
catch (error) {
this.logger.error('Could not get the index page from the router', error);
throw error;
}
}
async login(password) {
const cryptoVars = await this.getCurrentCryptoVars();
const encPw = this.encryptPassword(password, cryptoVars);
this.logger.debug('Encrypted password: ', encPw);
const serverSetPassword = await this.createServerRecord(encPw);
this.logger.debug('ServerSetPassword: ', serverSetPassword);
const csrfNonce = this.loginPasswordCheck(serverSetPassword.encryptData, cryptoVars, (0, crypto_1.deriveKey)(password, cryptoVars.salt));
this.logger.debug('Csrf nonce: ', csrfNonce);
await this.addCredentialToCookie();
this.csrfNonce = csrfNonce;
}
loginPasswordCheck(encryptedData, cryptoVars, key) {
const csrfNonce = (0, crypto_1.decrypt)(key, encryptedData, cryptoVars.iv, 'nonce');
return csrfNonce;
}
async logout() {
try {
this.logger.log('Logging out...');
return this.httpClient.post('/php/logout.php');
}
catch (error) {
this.logger.error('Could not do a full session logout', error);
throw error;
}
}
async restart() {
try {
const { data } = await this.httpClient.post('php/ajaxSet_status_restart.php', {
RestartReset: 'Restart',
}, {
headers: {
Connection: 'keep-alive',
csrfNonce: this.csrfNonce,
Referer: `${this.baseUrl}/?status_docsis&mid=StatusDocsis`,
},
});
this.logger.log('Router is restarting');
return data;
}
catch (error) {
this.logger.error('Could not restart router.', error);
throw error;
}
}
}
exports.Arris = Arris;