📄 iax.c
字号:
memset(&ied, 0, sizeof(ied));
if ((methods & IAX_AUTH_MD5) && challenge) {
MD5Init(&md5);
MD5Update(&md5, (const unsigned char *) challenge, strlen(challenge));
MD5Update(&md5, (const unsigned char *) password, strlen(password));
MD5Final((unsigned char *) reply, &md5);
memset(realreply, 0, sizeof(realreply));
convert_reply(realreply, (unsigned char *) reply);
iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, (unsigned char *) realreply);
} else {
iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, (unsigned char *) password);
}
return send_command(session, AST_FRAME_IAX, IAX_COMMAND_AUTHREP, 0, ied.buf, ied.pos, -1);
}
static int iax_regauth_reply(struct iax_session *session, char *password, char *challenge, int methods)
{
char reply[16];
struct MD5Context md5;
char realreply[256];
struct iax_ie_data ied;
memset(&ied, 0, sizeof(ied));
iax_ie_append_str(&ied, IAX_IE_USERNAME, (unsigned char *) session->username);
iax_ie_append_short(&ied, IAX_IE_REFRESH, session->refresh);
if ((methods & IAX_AUTHMETHOD_MD5) && challenge) {
MD5Init(&md5);
MD5Update(&md5, (const unsigned char *) challenge, strlen(challenge));
MD5Update(&md5, (const unsigned char *) password, strlen(password));
MD5Final((unsigned char *) reply, &md5);
memset(realreply, 0, sizeof(realreply));
convert_reply(realreply, (unsigned char *) reply);
iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, (unsigned char *) realreply);
} else {
iax_ie_append_str(&ied, IAX_IE_MD5_RESULT, (unsigned char *) password);
}
return send_command(session, AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
}
int iax_dial(struct iax_session *session, char *number)
{
struct iax_ie_data ied;
memset(&ied, 0, sizeof(ied));
iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, (unsigned char *) number);
return send_command(session, AST_FRAME_IAX, IAX_COMMAND_DIAL, 0, ied.buf, ied.pos, -1);
}
int iax_quelch(struct iax_session *session)
{
return send_command(session, AST_FRAME_IAX, IAX_COMMAND_QUELCH, 0, NULL, 0, -1);
}
int iax_unquelch(struct iax_session *session)
{
return send_command(session, AST_FRAME_IAX, IAX_COMMAND_UNQUELCH, 0, NULL, 0, -1);
}
int iax_dialplan_request(struct iax_session *session, char *number)
{
struct iax_ie_data ied;
memset(&ied, 0, sizeof(ied));
iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, (unsigned char *) number);
return send_command(session, AST_FRAME_IAX, IAX_COMMAND_DPREQ, 0, ied.buf, ied.pos, -1);
}
int iax_call(struct iax_session *session, char *cidnum, char *cidname, char *ich, char *lang, int wait, int formats, int capabilities)
{
char tmp[256]="";
char *part1, *part2;
int res;
int portno;
char *username, *hostname, *secret, *context, *exten, *dnid;
struct iax_ie_data ied;
struct hostent *hp;
/* We start by parsing up the temporary variable which is of the form of:
[user@]peer[:portno][/exten[@context]] */
if (!ich) {
IAXERROR "Invalid IAX Call Handle\n");
DEBU(G "Invalid IAX Call Handle\n");
return -1;
}
memset(&ied, 0, sizeof(ied));
strncpy(tmp, ich, sizeof(tmp) - 1);
iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION);
if (cidnum)
iax_ie_append_str(&ied, IAX_IE_CALLING_NUMBER, (unsigned char *) cidnum);
if (cidname)
iax_ie_append_str(&ied, IAX_IE_CALLING_NAME, (unsigned char *) cidname);
session->capability = capabilities;
session->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)session, 2 * 1000);
/* XXX We should have a preferred format XXX */
iax_ie_append_int(&ied, IAX_IE_FORMAT, formats);
iax_ie_append_int(&ied, IAX_IE_CAPABILITY, capabilities);
if (lang)
iax_ie_append_str(&ied, IAX_IE_LANGUAGE, (unsigned char *) lang);
/* Part 1 is [user[:password]@]peer[:port] */
part1 = strtok(tmp, "/");
/* Part 2 is exten[@context] if it is anything all */
part2 = strtok(NULL, "/");
if (strchr(part1, '@')) {
username = strtok(part1, "@");
hostname = strtok(NULL, "@");
} else {
username = NULL;
hostname = part1;
}
if (username && strchr(username, ':')) {
username = strtok(username, ":");
secret = strtok(NULL, ":");
} else
secret = NULL;
if(username)
strncpy(session->username, username, sizeof(session->username) - 1);
if(secret)
strncpy(session->secret, secret, sizeof(session->secret) - 1);
if (strchr(hostname, ':')) {
strtok(hostname, ":");
portno = atoi(strtok(NULL, ":"));
} else {
portno = IAX_DEFAULT_PORTNO;
}
if (part2) {
exten = strtok(part2, "@");
dnid = exten;
context = strtok(NULL, "@");
} else {
exten = NULL;
dnid = NULL;
context = NULL;
}
if (username)
iax_ie_append_str(&ied, IAX_IE_USERNAME, (unsigned char *) username);
if (exten && strlen(exten))
iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, (unsigned char *) exten);
if (dnid && strlen(dnid))
iax_ie_append_str(&ied, IAX_IE_DNID, (unsigned char *) dnid);
if (context && strlen(context))
iax_ie_append_str(&ied, IAX_IE_CALLED_CONTEXT, (unsigned char *) context);
/* Setup host connection */
hp = gethostbyname(hostname);
if (!hp) {
snprintf(iax_errstr, sizeof(iax_errstr), "Invalid hostname: %s", hostname);
session->peeraddr.sin_addr.s_addr = inet_addr(hostname);
#pragma message("Fix this hostname bug\n")
// return -1;
}else
memcpy(&session->peeraddr.sin_addr, hp->h_addr, sizeof(session->peeraddr.sin_addr));
session->peeraddr.sin_port = htons(portno);
session->peeraddr.sin_family = AF_INET;
res = send_command(session, AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
if (res < 0)
return res;
if (wait) {
DEBU(G "Waiting not yet implemented\n");
return -1;
}
return res;
}
static int calc_rxstamp(struct iax_session *session)
{
struct timeval tv;
int ms;
if (!session->rxcore.tv_sec && !session->rxcore.tv_usec) {
gettimeofday(&session->rxcore, NULL);
}
gettimeofday(&tv, NULL);
ms = (tv.tv_sec - session->rxcore.tv_sec) * 1000 +
(tv.tv_usec - session->rxcore.tv_usec) / 1000;
return ms;
}
static int match(struct sockaddr_in *sin, short callno, short dcallno, struct iax_session *cur)
{
if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->peeraddr.sin_port == sin->sin_port)) {
/* This is the main host */
if ((cur->peercallno == callno) ||
((dcallno == cur->callno) && !cur->peercallno)) {
/* That's us. Be sure we keep track of the peer call number */
cur->peercallno = callno;
return 1;
}
}
if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) {
/* We're transferring */
if (dcallno == cur->callno)
return 1;
}
return 0;
}
/* splitted match into 2 passes otherwise causing problem of matching
up the wrong session using the dcallno and the peercallno because
during a transfer (2 IAX channels on the same client/system) the
same peercallno (from two different asterisks) exist in more than
one session.
*/
static int forward_match(struct sockaddr_in *sin, short callno, short dcallno, struct iax_session *cur)
{
if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) {
/* We're transferring */
if (dcallno == cur->callno)
{
return 1;
}
}
if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->peeraddr.sin_port == sin->sin_port)) {
if (dcallno == cur->callno && dcallno != 0) {
/* That's us. Be sure we keep track of the peer call number */
if (cur->peercallno == 0) {
cur->peercallno = callno;
}
return 1;
}
}
return 0;
}
static int reverse_match(struct sockaddr_in *sin, short callno, struct iax_session *cur)
{
if ((cur->transfer.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->transfer.sin_port == sin->sin_port) && (cur->transferring)) {
/* We're transferring */
if (callno == cur->peercallno) {
return 1;
}
}
if ((cur->peeraddr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->peeraddr.sin_port == sin->sin_port)) {
if (callno == cur->peercallno) {
return 1;
}
}
return 0;
}
static struct iax_session *iax_find_session(struct sockaddr_in *sin,
short callno,
short dcallno,
int makenew)
{
struct iax_session *cur = sessions;
while(cur) {
if (forward_match(sin, callno, dcallno, cur)) {
return cur;
}
cur = cur->next;
}
cur = sessions;
while(cur) {
if (reverse_match(sin, callno, cur)) {
return cur;
}
cur = cur->next;
}
if (makenew && !dcallno) {
cur = iax_session_new();
cur->peercallno = callno;
cur->peeraddr.sin_addr.s_addr = sin->sin_addr.s_addr;
cur->peeraddr.sin_port = sin->sin_port;
cur->peeraddr.sin_family = AF_INET;
cur->pingid = iax_sched_add(NULL,NULL, send_ping, (void *)cur, 2 * 1000);
DEBU(G "Making new session, peer callno %d, our callno %d\n", callno, cur->callno);
} else {
DEBU(G "No session, peer = %d, us = %d\n", callno, dcallno);
}
return cur;
}
#ifdef EXTREME_DEBUG
static int display_time(int ms)
{
static int oldms = -1;
if (oldms < 0) {
DEBU(G "First measure\n");
oldms = ms;
return 0;
}
DEBU(G "Time from last frame is %d ms\n", ms - oldms);
oldms = ms;
return 0;
}
#endif
#define FUDGE 1
#ifdef NEWJB
/* From chan_iax2/steve davies: need to get permission from steve or digium, I guess */
static long unwrap_timestamp(long ts, long last)
{
int x;
if ( (ts & 0xFFFF0000) == (last & 0xFFFF0000) ) {
x = ts - last;
if (x < -50000) {
/* Sudden big jump backwards in timestamp:
What likely happened here is that miniframe timestamp has circled but we haven't
gotten the update from the main packet. We'll just pretend that we did, and
update the timestamp appropriately. */
ts = ( (last & 0xFFFF0000) + 0x10000) | (ts & 0xFFFF);
DEBU(G "schedule_delivery: pushed forward timestamp\n");
}
if (x > 50000) {
/* Sudden apparent big jump forwards in timestamp:
What's likely happened is this is an old miniframe belonging to the previous
top-16-bit timestamp that has turned up out of order.
Adjust the timestamp appropriately. */
ts = ( (last & 0xFFFF0000) - 0x10000) | (ts & 0xFFFF);
DEBU(G "schedule_delivery: pushed back timestamp\n");
}
}
return ts;
}
#endif
static struct iax_event *schedule_delivery(struct iax_event *e, unsigned int ts, int updatehistory)
{
/*
* This is the core of the IAX jitterbuffer delivery mechanism:
* Dynamically adjust the jitterbuffer and decide how long to wait
* before delivering the packet.
*/
int ms, x;
int drops[MEMORY_SIZE];
int min, max=0, maxone=0, y, z, match;
#ifdef EXTREME_DEBUG
DEBU(G "[%p] We are at %d, packet is for %d\n", e->session, calc_rxstamp(e->session), ts);
#endif
#ifdef VOICE_SMOOTHING
if (e->etype == IAX_EVENT_VOICE) {
/* Smooth voices if we know enough about the format */
switch(e->event.voice.format) {
case AST_FORMAT_GSM:
/* GSM frames are 20 ms long, although there could be periods of
silence. If the time is < 50 ms, assume it ought to be 20 ms */
if (ts - e->session->lastts < 50)
ts = e->session->lastts + 20;
#ifdef EXTREME_DEBUG
display_time(ts);
#endif
break;
default:
/* Can't do anything */
}
e->session->lastts = ts;
}
#endif
#ifdef NEWJB
{
int type = JB_TYPE_CONTROL;
int len = 0;
if(e->etype == IAX_EVENT_VOICE) {
type = JB_TYPE_VOICE;
len = get_sample_cnt(e) / 8;
} else if(e->etype == IAX_EVENT_CNG) {
type = JB_TYPE_SILENCE;
}
/* unwrap timestamp */
ts = unwrap_timestamp(ts,e->session->last_ts);
/* move forward last_ts if it's greater. We do this _after_ unwrapping, because
* asterisk _still_ has cases where it doesn't send full frames when it ought to */
if(ts > e->session->last_ts) {
e->session->last_ts = ts;
}
/* insert into jitterbuffer */
/* TODO: Perhaps we could act immediately if it's not droppable and late */
if(jb_put(e->session->jb, e, type, len, ts, calc_rxstamp(e->session)) == JB_DROP) {
iax_event_free(e);
}
}
#else
/* How many ms from now should this packet be delivered? (remember
this can be a negative number, too */
ms = calc_rxstamp(e->session) - ts;
/* Drop voice frame if timestamp is way off */
if ((e->etype == IAX_EVENT_VOICE) && ((ms > 65536) || (ms < -65536))) {
DEBU(G "Dropping a voice packet with odd ts (ts = %d; ms = %d)\n", ts, ms);
free(e);
return NULL;
}
/* Adjust if voice frame timestamp is off by a step */
if (ms > 32768) {
/* What likely happened here is that our counter has circled but we haven't
gotten the update from the main packet. We'll just pretend that we did, and
update the timestamp appropriately. */
ms -= 65536;
}
if (ms < -32768) {
/* We got this packet out of order. Lets add 65536 to it to bring it into our new
time frame */
ms += 65536;
}
#if 0
printf("rxstamp is %d, timestamp is %d, ms is %d\n", calc_rxstamp(e->session), ts, ms);
#endif
/* Rotate history queue. Leading 0's are irrelevant. */
if (updatehistory) {
for (x=0; x < MEMORY_SIZE - 1; x++)
e->session->history[x] = e->session->history[x+1];
/* Add new entry for this time */
e->session->history[x] = ms;
}
/* We have to find the maximum and minimum time delay we've had to deliver. */
min = e->session->history[0];
for (z=0;z < iax_dropcount + 1; z++) {
/* We drop the top iax_dropcount entries. iax_dropcount represents
a tradeoff between quality of voice and latency. 3% drop seems to
be unnoticable to the client and can significantly improve latency.
We add one more to our droplist, but that's the one we actually use,
and don't drop. */
max = -99999999;
for (x=0;x<MEMORY_SIZE;x++) {
if (max < e->session->history[x]) {
/* New candidate value. Make sure we haven't dropped it. */
match=0;
for(y=0;!match && (y<z); y++)
match |= (drops[y] == x);
/* If there is no match, this is our new maximum */
if (!match) {
max = e->session->history[x];
maxone = x;
}
}
if (!z) {
/* First pass, calcualte our minimum, too */
if (min > e->session->h
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -