I am trying to learn more about SIP and trying to implement small parts of the protocol in node.js with UDP
so far I have this
const dgram = require('dgram');
const crypto = require('crypto');
const asteriskIP = 'ASTERISK_IP';
const asteriskPort = 6111;
const clientIP = '192.168.1.2';
const clientPort = 6111;
const username = 'USERNAME';
const password = 'PASSWORD';
// Create a UDP socket
const socket = dgram.createSocket('udp4');
// Generate a random branch identifier for the Via header
const generateBranch = () => {
const branchId = Math.floor(Math.random() * 100000000);
return `z9hG4bK${branchId}`;
};
// Generate Digest response
function generateDigestResponse(username, password, realm, nonce, method, uri) {
const ha1 = crypto.createHash('md5')
.update(`${username}:${realm}:${password}`)
.digest('hex');
const ha2 = crypto.createHash('md5')
.update(`${method}:${uri}`)
.digest('hex');
const response = crypto.createHash('md5')
.update(`${ha1}:${nonce}:${ha2}`)
.digest('hex');
return response;
}
// SIP REGISTER request
const generateRegisterRequest = (branch, withAuth = false, realm = '', nonce = '') => {
let request = `REGISTER sip:${asteriskIP}:${asteriskPort} SIP/2.0\r\n` +
`Via: SIP/2.0/UDP ${clientIP}:${clientPort};branch=${branch}\r\n` +
`From: <sip:${username}@${asteriskIP}>;tag=${branch}\r\n` +
`To: <sip:${username}@${asteriskIP}>\r\n` +
`Call-ID: ${branch}@${clientIP}\r\n` +
`CSeq: 1 REGISTER\r\n` +
`Contact: <sip:${username}@${clientIP}:${clientPort}>\r\n` +
'Max-Forwards: 70\r\n' +
'Expires: 3600\r\n' +
'User-Agent: Node.js SIP Library\r\n';
if (withAuth && realm && nonce) {
const digestResponse = generateDigestResponse(username, password, realm, nonce, 'REGISTER', `sip:${asteriskIP}:${asteriskPort}`);
request += 'Authorization: Digest ' +
`username="${username}", realm="${realm}", ` +
`nonce="${nonce}", uri="sip:${asteriskIP}:${asteriskPort}", ` +
`response="${digestResponse}"\r\n`;
}
request += 'Content-Length: 0\r\n\r\n';
return request;
};
// Send the REGISTER request
const sendRegisterRequest = (request) => {
socket.send(request, 0, request.length, asteriskPort, asteriskIP, (error) => {
if (error) {
console.error('Error sending UDP packet:', error);
} else {
console.log('REGISTER request sent successfully.');
}
});
};
let realm = '';
let nonce = '';
// Listen for incoming responses
socket.on('message', (message) => {
const response = message.toString();
console.log('Received response:', response);
if (response.startsWith('SIP/2.0 200 OK')) {
console.log('Registration successful.');
// Do further processing or initiate calls here
} else if (response.startsWith('SIP/2.0 401 Unauthorized')) {
const authenticateHeader = response.match(/WWW-Authenticate:.*realm="([^"]+)".*nonce="([^"]+)"/i);
if (authenticateHeader) {
realm = authenticateHeader[1];
nonce = authenticateHeader[2];
console.log('Received realm:', realm);
console.log('Received nonce:', nonce);
// Generate Digest response and proceed with registration
const branch = generateBranch();
const registerRequestWithAuth = generateRegisterRequest(branch, true, realm, nonce);
console.log('Sending REGISTER request with authentication:');
console.log(registerRequestWithAuth);
// Send the REGISTER request with authentication
sendRegisterRequest(registerRequestWithAuth+ '\r\n');
}
}
});
// Bind the socket to the client's port and IP
socket.bind(clientPort, clientIP, () => {
console.log('Socket bound successfully.');
// Generate branch identifier for the initial REGISTER request
const branch = generateBranch();
const registerRequest = generateRegisterRequest(branch);
// Send the initial REGISTER request
sendRegisterRequest(registerRequest);
});
I have turned on SIP debugging in asterisk 18 and this is what I see.
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.1.2:6111;branch=z9hG4bK2049260;received=72.172.213.173;rport=6111
From: <sip:Tim@64.227.16.15>;tag=z9hG4bK2049260
To: <sip:Tim@64.227.16.15>;tag=as12883ca4
Call-ID: z9hG4bK2049260@192.168.1.2
CSeq: 1 REGISTER
Server: Asterisk PBX 18.14.0~dfsg+~cs6.12.40431414-1
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces
WWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="5b4af6d3"
Content-Length: 0
REGISTER sip:ASTERISK_IP:6111 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.2:6111;branch=z9hG4bK99247361
From: <sip:Tim@ASTERISK_IP>;tag=z9hG4bK99247361
To: <sip:Tim@ASTERISK_IP>
Call-ID: z9hG4bK99247361@192.168.1.2
CSeq: 1 REGISTER
Contact: <sip:Tim@192.168.1.2:6111>
Max-Forwards: 70
Expires: 3600
User-Agent: Node.js SIP Library
Authorization: Digest username="Tim", realm="asterisk", nonce="5b4af6d3", uri="sip:ASTERISK_IP:6111", response="2e0cbc55739537d46ff0c0ff862ae28a"
Content-Length: 0
SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.1.2:6111;branch=z9hG4bK99247361;received=72.172.213.173;rport=6111
From: <sip:Tim@ASTERISK_IP>;tag=z9hG4bK99247361
To: <sip:Tim@ASTERISK_IP>;tag=as75c4d7b6
Call-ID: z9hG4bK99247361@192.168.1.2
CSeq: 1 REGISTER
Server: Asterisk PBX 18.14.0~dfsg+~cs6.12.40431414-1
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO, PUBLISH, MESSAGE
Supported: replaces
WWW-Authenticate: Digest algorithm=MD5, realm="asterisk", nonce="1fd88a71"
Content-Length: 0
But I am not getting 200 OK
from asterisk at all. I am wondering if I am calculating the nonce incorrectly. I know the code is a bit messy but this is just a quick test first. Am I missing any headers? does asterisk need something more? Any help or push in the right direction would be greatly appreciated. Thank you.