164 lines
4.5 KiB
JavaScript
164 lines
4.5 KiB
JavaScript
const logger = require('../utils/logger');
|
|
const config = require('config');
|
|
|
|
/**
|
|
* Registration Handler - Equivalent to Kamailio's REGISTRAR route
|
|
* Handles SIP REGISTER requests and manages user location database
|
|
*/
|
|
class RegistrationHandler {
|
|
constructor(srf) {
|
|
this.srf = srf;
|
|
this.registry = new Map(); // In-memory registry (equivalent to Kamailio usrloc)
|
|
this.config = config.get('registry');
|
|
}
|
|
|
|
/**
|
|
* Validate From header before processing (equivalent to kamailio.cfg:635-639)
|
|
*/
|
|
validateFromHeader(req, res) {
|
|
const from = req.getParsedHeader('from');
|
|
if (!from || !from.uri || !from.uri.user || !from.uri.host) {
|
|
logger.error('[REGISTER] Cannot register user - From header is invalid | Call-ID: %s', req.get('Call-ID'));
|
|
res.send(400, 'Bad Request - Invalid From header');
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get registration expiry from Contact header (equivalent to kamailio.cfg:647-657)
|
|
*/
|
|
getRegistrationExpiry(req) {
|
|
let expires = req.get('Expires');
|
|
|
|
if (!expires || expires === '') {
|
|
expires = this.config.defaultExpires.toString();
|
|
}
|
|
|
|
// Validate expires is a number
|
|
if (expires !== '0' && !/^[0-9]+$/.test(expires)) {
|
|
expires = this.config.defaultExpires.toString();
|
|
}
|
|
|
|
const expiresNum = parseInt(expires);
|
|
|
|
// Validate expiry range
|
|
if (expiresNum > this.config.maxExpires) {
|
|
return this.config.maxExpires;
|
|
}
|
|
if (expiresNum < this.config.minExpires && expiresNum !== 0) {
|
|
return this.config.minExpires;
|
|
}
|
|
|
|
return expiresNum;
|
|
}
|
|
|
|
/**
|
|
* Save registration to registry (equivalent to kamailio.cfg:644)
|
|
*/
|
|
saveToRegistry(from, contact, expires, sourceIp, sourcePort) {
|
|
const key = `${from.uri.user}@${from.uri.host}`;
|
|
const registration = {
|
|
aor: key,
|
|
contact: contact,
|
|
expires: expires,
|
|
registeredAt: Date.now(),
|
|
sourceIp: sourceIp,
|
|
sourcePort: sourcePort,
|
|
callId: from.params.tag || 'unknown'
|
|
};
|
|
|
|
if (expires === 0) {
|
|
// Remove registration (unregister)
|
|
this.registry.delete(key);
|
|
logger.info('[REGISTER] User %s unregistered | From: %s:%s', key, sourceIp, sourcePort);
|
|
} else {
|
|
// Add/update registration
|
|
this.registry.set(key, registration);
|
|
logger.info('[REGISTER] User %s registered | Contact: %s | Expires: %ds | From: %s:%s',
|
|
key, contact, expires, sourceIp, sourcePort);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle REGISTER request (equivalent to kamailio.cfg:630-676)
|
|
*/
|
|
handleRegister(req, res) {
|
|
const callId = req.get('Call-ID');
|
|
const sourceIp = req.source_address;
|
|
const sourcePort = req.source_port;
|
|
|
|
logger.info('[REGISTER] Registration request from %s:%s | Call-ID: %s', sourceIp, sourcePort, callId);
|
|
|
|
// Validate From header
|
|
if (!this.validateFromHeader(req, res)) {
|
|
return;
|
|
}
|
|
|
|
// Get registration details
|
|
const from = req.getParsedHeader('from');
|
|
const contact = req.get('Contact');
|
|
const expires = this.getRegistrationExpiry(req);
|
|
|
|
// Save to registry
|
|
this.saveToRegistry(from, contact, expires, sourceIp, sourcePort);
|
|
|
|
// Send response
|
|
if (expires === 0) {
|
|
res.send(200, 'OK - Unregistered from Cyanet VoIP system.');
|
|
} else {
|
|
res.send(200, 'OK - Welcome to Cyanet VoIP system.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lookup user in registry (equivalent to Kamailio's lookup() function)
|
|
*/
|
|
lookup(aor) {
|
|
return this.registry.get(aor);
|
|
}
|
|
|
|
/**
|
|
* Get all registered users
|
|
*/
|
|
getAllRegistrations() {
|
|
return Array.from(this.registry.values());
|
|
}
|
|
|
|
/**
|
|
* Clean expired registrations
|
|
*/
|
|
cleanExpiredRegistrations() {
|
|
const now = Date.now();
|
|
const expiredKeys = [];
|
|
|
|
for (const [key, registration] of this.registry.entries()) {
|
|
const expiryTime = registration.registeredAt + (registration.expires * 1000);
|
|
if (registration.expires > 0 && expiryTime <= now) {
|
|
expiredKeys.push(key);
|
|
}
|
|
}
|
|
|
|
for (const key of expiredKeys) {
|
|
this.registry.delete(key);
|
|
logger.info('[REGISTER] Expired registration removed: %s', key);
|
|
}
|
|
|
|
return expiredKeys.length;
|
|
}
|
|
|
|
/**
|
|
* Start registration cleanup timer
|
|
*/
|
|
startCleanupTimer() {
|
|
// Clean expired registrations every 60 seconds (equivalent to Kamailio timer_interval)
|
|
setInterval(() => {
|
|
const cleaned = this.cleanExpiredRegistrations();
|
|
if (cleaned > 0) {
|
|
logger.info('[REGISTER] Cleaned %d expired registrations', cleaned);
|
|
}
|
|
}, 60000);
|
|
}
|
|
}
|
|
|
|
module.exports = RegistrationHandler; |