diff --git a/channels/chan_iax.c b/channels/chan_iax.c index 7f8e23391db558b6d6adb4f014d0a90e071ebfde..71ca1e7f92ee48d10f24962a226c71c7a553795b 100755 --- a/channels/chan_iax.c +++ b/channels/chan_iax.c @@ -28,6 +28,7 @@ #include <asterisk/cdr.h> #include <asterisk/crypto.h> #include <asterisk/acl.h> +#include <asterisk/manager.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> @@ -74,7 +75,7 @@ static int lagrq_time = 10; static int nextcallno = 0; static int maxjitterbuffer=3000; -static int iaxdefaultdpcache=30 * 60; /* Cache dialplan entries for 30 minutes by default */ +static int iaxdefaultdpcache=10 * 60; /* Cache dialplan entries for 10 minutes by default */ static int iaxdefaulttimeout = 5; /* Default to wait no more than 5 seconds for a reply to come back */ @@ -86,7 +87,6 @@ static int expirey = AST_DEFAULT_REG_EXPIRE; static int usecnt; static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER; -static pthread_mutex_t iaxs_lock = AST_MUTEX_INITIALIZER; int (*regfunk)(char *username, int onoff) = NULL; @@ -105,6 +105,11 @@ int (*regfunk)(char *username, int onoff) = NULL; #define IAX_CAPABILITY_LOWFREE (IAX_CAPABILITY_LOWBANDWIDTH & \ ~AST_FORMAT_G723_1) + +#define DEFAULT_MAXMS 2000 /* Must be faster than 2 seconds by default */ +#define DEFAULT_FREQ_OK 60 * 1000 /* How often to check for the host to be up */ +#define DEFAULT_FREQ_NOTOK 10 * 1000 /* How often to check, if the host is down... */ + static struct io_context *io; static struct sched_context *sched; @@ -170,6 +175,13 @@ struct iax_peer { int expirey; /* How soon to expire */ int capability; /* Capability */ int delme; /* I need to be deleted */ + + /* Qualification */ + int callno; /* Call number of POKE request */ + int pokeexpire; /* When to expire poke */ + int lastms; /* How long last response took (in ms), or -1 for no response */ + int maxms; /* Max ms we will accept for the host to be up, 0 to not monitor */ + struct ast_ha *ha; struct iax_peer *next; }; @@ -229,6 +241,8 @@ struct chan_iax_pvt { unsigned int lastsent; /* Ping time */ unsigned int pingtime; + /* Max time for initial response */ + int maxtime; /* Peer Address */ struct sockaddr_in addr; /* Our call number */ @@ -295,6 +309,8 @@ struct chan_iax_pvt { char language[80]; /* Associated registry */ struct iax_registry *reg; + /* Associated peer for poking */ + struct iax_peer *peerpoke; /* Transferring status */ int transferring; @@ -310,9 +326,10 @@ struct chan_iax_pvt { /* Who we are bridged to */ int bridgecallno; - int pingid; - int lagid; - int autoid; + int pingid; /* Transmit PING request */ + int lagid; /* Retransmit lag request */ + int autoid; /* Auto hangup for Dialplan requestor */ + int initid; /* Initial peer auto-congest ID (based on qualified peers) */ char dproot[AST_MAX_EXTENSION]; char accountcode[20]; int amaflags; @@ -389,6 +406,8 @@ static struct ast_peer_list { #define CACHE_FLAG_TRANSMITTED (1 << 5) /* Timeout */ #define CACHE_FLAG_UNKNOWN (1 << 6) +/* Matchmore */ +#define CACHE_FLAG_MATCHMORE (1 << 7) static struct iax_dpcache { char peercontext[AST_MAX_EXTENSION]; @@ -446,6 +465,9 @@ void showframe(struct ast_iax_frame *f, struct ast_iax_full_hdr *fhi, int rx, st "TXREADY", "TXREL ", "TXREJ ", + "QUELCH ", + "UNQULCH", + "POKE", }; char *cmds[] = { "(0?)", @@ -514,6 +536,7 @@ void showframe(struct ast_iax_frame *f, struct ast_iax_full_hdr *fhi, int rx, st /* XXX We probably should use a mutex when working with this XXX */ static struct chan_iax_pvt *iaxs[AST_IAX_MAX_CALLS]; +static pthread_mutex_t iaxsl[AST_IAX_MAX_CALLS]; static int send_command(struct chan_iax_pvt *, char, int, unsigned int, char *, int, int); static int send_command_immediate(struct chan_iax_pvt *, char, int, unsigned int, char *, int, int); @@ -573,8 +596,13 @@ static unsigned char compress_subclass(int subclass) static int uncompress_subclass(unsigned char csub) { /* If the SC_LOG flag is set, return 2^csub otherwise csub */ - if (csub & AST_FLAG_SC_LOG) - return 1 << (csub & ~AST_FLAG_SC_LOG & AST_MAX_SHIFT); + if (csub & AST_FLAG_SC_LOG) { + /* special case for 'compressed' -1 */ + if (csub == 0xff) + return -1; + else + return 1 << (csub & ~AST_FLAG_SC_LOG & AST_MAX_SHIFT); + } else return csub; } @@ -592,39 +620,43 @@ static struct chan_iax_pvt *new_iax(void) tmp->pingid = -1; tmp->lagid = -1; tmp->autoid = -1; + tmp->initid = -1; /* strncpy(tmp->context, context, sizeof(tmp->context)-1); */ strncpy(tmp->exten, "s", sizeof(tmp->exten)-1); } return tmp; } -static int get_timelen(struct ast_frame *f) +static int get_samples(struct ast_frame *f) { - int timelen=0; + int samples=0; switch(f->subclass) { case AST_FORMAT_G723_1: - timelen = 30 /* XXX Not necessarily true XXX */; + samples = 240 /* XXX Not necessarily true XXX */; break; case AST_FORMAT_GSM: - timelen = 20 * (f->datalen / 20); + samples = 160 * (f->datalen / 33); break; case AST_FORMAT_SLINEAR: - timelen = f->datalen / 16; + samples = f->datalen / 2; break; case AST_FORMAT_LPC10: - timelen = 22; - timelen += ((char *)(f->data))[7] & 0x1; + samples = 22 * 8; + samples += (((char *)(f->data))[7] & 0x1) * 8; break; case AST_FORMAT_ULAW: - timelen = f->datalen / 8; + samples = f->datalen; + break; + case AST_FORMAT_ALAW: + samples = f->datalen; break; case AST_FORMAT_ADPCM: - timelen = f->datalen / 4; + samples = f->datalen *2; break; default: - ast_log(LOG_WARNING, "Don't know how to calculate timelen on %d packets\n", f->subclass); + ast_log(LOG_WARNING, "Don't know how to calculate samples on %d packets\n", f->subclass); } - return timelen; + return samples; } static int frames = 0; @@ -720,14 +752,15 @@ static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int int start; if (new <= NEW_ALLOW) { /* Look for an existing connection first */ - for (x=0;x<AST_IAX_MAX_CALLS;x++) { + for (x=0;(res < 0) && (x<AST_IAX_MAX_CALLS);x++) { + ast_pthread_mutex_lock(&iaxsl[x]); if (iaxs[x]) { /* Look for an exact match */ if (match(sin, callno, dcallno, iaxs[x])) { res = x; - break; } } + ast_pthread_mutex_unlock(&iaxsl[x]); } } if ((res < 0) && (new >= NEW_ALLOW)) { @@ -738,7 +771,9 @@ static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int ast_log(LOG_WARNING, "Unable to accept more calls\n"); return -1; } + ast_pthread_mutex_lock(&iaxsl[x]); iaxs[x] = new_iax(); + ast_pthread_mutex_unlock(&iaxsl[x]); if (iaxs[x]) { if (option_debug) ast_log(LOG_DEBUG, "Creating new call structure %d\n", x); @@ -763,11 +798,39 @@ static int find_callno(short callno, short dcallno ,struct sockaddr_in *sin, int return res; } +static int iax_queue_frame(int callno, struct ast_frame *f) +{ + int pass =0; + /* Assumes lock for callno is already held... */ + for (;;) { + pass++; + if (!pthread_mutex_trylock(&iaxsl[callno])) { + ast_log(LOG_WARNING, "Lock is not held on pass %d of iax_queue_frame\n", pass); + CRASH; + } + if (iaxs[callno] && iaxs[callno]->owner) { + if (pthread_mutex_trylock(&iaxs[callno]->owner->lock)) { + /* Avoid deadlock by pausing and trying again */ + ast_pthread_mutex_unlock(&iaxsl[callno]); + usleep(1); + ast_pthread_mutex_lock(&iaxsl[callno]); + } else { + ast_queue_frame(iaxs[callno]->owner, f, 0); + ast_pthread_mutex_unlock(&iaxs[callno]->owner->lock); + break; + } + } else + break; + } + return 0; +} + static int iax_send(struct chan_iax_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final); -static int do_deliver(void *data) +static int __do_deliver(void *data) { - /* Just deliver the packet by using queueing */ + /* Just deliver the packet by using queueing. This is called by + the IAX thread with the iaxsl lock held. */ struct ast_iax_frame *fr = data; unsigned int ts; fr->retrans = -1; @@ -785,9 +848,7 @@ static int do_deliver(void *data) iaxs[fr->callno]->lag = ts - fr->ts; } } else { - if (iaxs[fr->callno]->owner) { - ast_queue_frame(iaxs[fr->callno]->owner, fr->f, 1); - } + iax_queue_frame(fr->callno, fr->f); } } /* Free the packet */ @@ -798,6 +859,18 @@ static int do_deliver(void *data) return 0; } +static int do_deliver(void *data) +{ + /* Locking version of __do_deliver */ + struct ast_iax_frame *fr = data; + int callno = fr->callno; + int res; + ast_pthread_mutex_lock(&iaxsl[callno]); + res = __do_deliver(data); + ast_pthread_mutex_unlock(&iaxsl[callno]); + return res; +} + static int handle_error() { /* XXX Ideally we should figure out why an error occured and then abort those @@ -835,6 +908,7 @@ static int handle_error() static int send_packet(struct ast_iax_frame *f) { int res; + /* Called with iaxsl held */ if (option_debug) ast_log(LOG_DEBUG, "Sending %d on %d/%d to %s:%d\n", f->ts, f->callno, iaxs[f->callno]->peercallno, inet_ntoa(iaxs[f->callno]->addr.sin_addr), ntohs(iaxs[f->callno]->addr.sin_port)); /* Don't send if there was an error, but return error instead */ @@ -871,11 +945,82 @@ static int send_packet(struct ast_iax_frame *f) } +static int iax_predestroy(int callno) +{ + struct ast_channel *c; + struct chan_iax_pvt *pvt; + ast_pthread_mutex_lock(&iaxsl[callno]); + pvt = iaxs[callno]; + if (!pvt) { + ast_pthread_mutex_unlock(&iaxsl[callno]); + return -1; + } + if (!pvt->alreadygone) { + /* No more pings or lagrq's */ + if (pvt->pingid > -1) + ast_sched_del(sched, pvt->pingid); + if (pvt->lagid > -1) + ast_sched_del(sched, pvt->lagid); + if (pvt->autoid > -1) + ast_sched_del(sched, pvt->autoid); + if (pvt->initid > -1) + ast_sched_del(sched, pvt->initid); + pvt->pingid = -1; + pvt->lagid = -1; + pvt->autoid = -1; + pvt->initid = -1; + pvt->alreadygone = 1; + } + c = pvt->owner; + if (c) { + c->_softhangup |= AST_SOFTHANGUP_DEV; + c->pvt->pvt = NULL; + pvt->owner = NULL; + ast_pthread_mutex_lock(&usecnt_lock); + usecnt--; + if (usecnt < 0) + ast_log(LOG_WARNING, "Usecnt < 0???\n"); + ast_pthread_mutex_unlock(&usecnt_lock); + ast_update_use_count(); + } + ast_pthread_mutex_unlock(&iaxsl[callno]); + return 0; +} + +static int iax_predestroy_nolock(int callno) +{ + int res; + ast_pthread_mutex_unlock(&iaxsl[callno]); + res = iax_predestroy(callno); + ast_pthread_mutex_lock(&iaxsl[callno]); + return res; +} + static void iax_destroy(int callno) { - struct chan_iax_pvt *pvt = iaxs[callno]; + struct chan_iax_pvt *pvt; struct ast_iax_frame *cur; + struct ast_channel *owner; + +retry: + ast_pthread_mutex_lock(&iaxsl[callno]); + pvt = iaxs[callno]; + iaxs[callno] = NULL; + + if (pvt) + owner = pvt->owner; + else + owner = NULL; + if (owner) { + if (pthread_mutex_trylock(&owner->lock)) { + ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n"); + ast_pthread_mutex_unlock(&iaxsl[callno]); + usleep(1); + goto retry; + } + } if (pvt) { + pvt->owner = NULL; /* No more pings or lagrq's */ if (pvt->pingid > -1) ast_sched_del(sched, pvt->pingid); @@ -883,29 +1028,23 @@ static void iax_destroy(int callno) ast_sched_del(sched, pvt->lagid); if (pvt->autoid > -1) ast_sched_del(sched, pvt->autoid); + if (pvt->initid > -1) + ast_sched_del(sched, pvt->initid); pvt->pingid = -1; pvt->lagid = -1; pvt->autoid = -1; + pvt->initid = -1; /* Already gone */ pvt->alreadygone = 1; - if (pvt->owner) { - if (ast_pthread_mutex_lock(&pvt->owner->lock)) { - ast_log(LOG_WARNING, "Unable to lock channel %s\n", pvt->owner->name); - } else if (!pvt->owner || (pvt->owner->pvt->pvt != pvt)) { - ast_log(LOG_WARNING, "Something very strange happened...\n"); - } else { - /* If there's an owner, prod it to give up */ - pvt->owner->pvt->pvt = NULL; - pvt->owner->_softhangup |= AST_SOFTHANGUP_DEV; - ast_queue_hangup(pvt->owner, 0); - ast_pthread_mutex_unlock(&pvt->owner->lock); - } + if (owner) { + /* If there's an owner, prod it to give up */ + owner->pvt->pvt = NULL; + owner->_softhangup |= AST_SOFTHANGUP_DEV; + ast_queue_hangup(owner, 0); } - iaxs[callno] = NULL; - for (cur = iaxq.head; cur ; cur = cur->next) { /* Cancel any pending transmissions */ if (cur->callno == pvt->callno) @@ -916,15 +1055,31 @@ static void iax_destroy(int callno) } free(pvt); } + if (owner) { + ast_pthread_mutex_unlock(&owner->lock); + } + ast_pthread_mutex_unlock(&iaxsl[callno]); } +static void iax_destroy_nolock(int callno) +{ + /* Actually it's easier to unlock, kill it, and relock */ + ast_pthread_mutex_unlock(&iaxsl[callno]); + iax_destroy(callno); + ast_pthread_mutex_lock(&iaxsl[callno]); +} + static int attempt_transmit(void *data) { - /* Attempt to transmit the frame to the remote peer */ + /* Attempt to transmit the frame to the remote peer... + Called without iaxsl held. */ struct ast_iax_frame *f = data; int freeme=0; + int callno = f->callno; /* Make sure this call is still active */ + if (callno > -1) + ast_pthread_mutex_lock(&iaxsl[callno]); if ((f->callno > -1) && iaxs[f->callno]) { if ((f->retries < 0) /* Already ACK'd */ || (f->retries >= max_retries) /* Too many attempts */) { @@ -935,22 +1090,24 @@ static int attempt_transmit(void *data) send_command(iaxs[f->callno], AST_FRAME_IAX, AST_IAX_COMMAND_TXREJ, 0, NULL, 0, -1); } else if (f->final) { if (f->final) - iax_destroy(f->callno); + iax_destroy_nolock(f->callno); } else { if (iaxs[f->callno]->owner) ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", inet_ntoa(iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->f->frametype, f->f->subclass, f->ts, f->seqno); iaxs[f->callno]->error = ETIMEDOUT; if (iaxs[f->callno]->owner) { + struct ast_frame fr = { 0, }; /* Hangup the fd */ - ast_softhangup(iaxs[f->callno]->owner, AST_SOFTHANGUP_DEV); - ast_queue_hangup(iaxs[f->callno]->owner, 1); + fr.frametype = AST_FRAME_CONTROL; + fr.subclass = AST_CONTROL_HANGUP; + iax_queue_frame(f->callno, &fr); } else { if (iaxs[f->callno]->reg) { memset(&iaxs[f->callno]->reg->us, 0, sizeof(iaxs[f->callno]->reg->us)); iaxs[f->callno]->reg->regstate = REG_STATE_TIMEOUT; iaxs[f->callno]->reg->refresh = AST_DEFAULT_REG_EXPIRE; } - iax_destroy(f->callno); + iax_destroy_nolock(f->callno); } } @@ -974,6 +1131,8 @@ static int attempt_transmit(void *data) f->retries = -1; freeme++; } + if (callno > -1) + ast_pthread_mutex_unlock(&iaxsl[callno]); /* Do not try again */ if (freeme) { /* Don't attempt delivery, just remove it from the queue */ @@ -1006,7 +1165,6 @@ static int iax_set_jitter(int fd, int argc, char *argv[]) max_jitter_buffer = 0; } else { if (argc == 5) { - ast_pthread_mutex_lock(&iaxs_lock); if ((atoi(argv[3]) >= 0) && (atoi(argv[3]) < AST_IAX_MAX_CALLS)) { if (iaxs[atoi(argv[3])]) { iaxs[atoi(argv[3])]->jitterbuffer = atoi(argv[4]); @@ -1016,7 +1174,6 @@ static int iax_set_jitter(int fd, int argc, char *argv[]) ast_cli(fd, "No such call '%d'\n", atoi(argv[3])); } else ast_cli(fd, "%d is not a valid call number\n", atoi(argv[3])); - ast_pthread_mutex_unlock(&iaxs_lock); } } return RESULT_SUCCESS; @@ -1075,6 +1232,8 @@ static int iax_show_cache(int fd, int argc, char *argv[]) strcat(tmp, "TIMEOUT|"); if (dp->flags & CACHE_FLAG_TRANSMITTED) strcat(tmp, "TRANSMITTED|"); + if (dp->flags & CACHE_FLAG_MATCHMORE) + strcat(tmp, "MATCHMORE|"); if (dp->flags & CACHE_FLAG_UNKNOWN) strcat(tmp, "UNKNOWN|"); /* Trim trailing pipe */ @@ -1147,7 +1306,8 @@ static int schedule_delivery(struct ast_iax_frame *fr, int reallydeliver) int drops[MEMORY_SIZE]; int min, max=0, maxone=0,y,z, match; /* ms is a measure of the "lateness" of the packet relative to the first - packet we received, which always has a lateness of 1. */ + packet we received, which always has a lateness of 1. Called by + IAX thread, with iaxsl lock held. */ ms = calc_rxstamp(iaxs[fr->callno]) - fr->ts; if (ms > 32767) { @@ -1254,7 +1414,7 @@ static int schedule_delivery(struct ast_iax_frame *fr, int reallydeliver) ast_log(LOG_DEBUG, "Calculated ms is %d\n", ms); /* Don't deliver it more than 4 ms late */ if ((ms > -4) || (fr->f->frametype != AST_FRAME_VOICE)) { - do_deliver(fr); + __do_deliver(fr); } else { if (option_debug) ast_log(LOG_DEBUG, "Dropping voice packet since %d ms is, too old\n", ms); @@ -1328,13 +1488,15 @@ static int iax_fixup(struct ast_channel *oldchannel, struct ast_channel *newchan return 0; } -static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, char *peer, char *context) +static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, int *maxtime, char *peer, char *context) { struct hostent *hp; struct iax_peer *p; int found=0; if (sendani) *sendani = 0; + if (maxtime) + *maxtime = 0; sin->sin_family = AF_INET; ast_pthread_mutex_lock(&peerl.lock); p = peerl.peers; @@ -1343,9 +1505,12 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, c found++; if (capability) *capability = p->capability; - if (p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) { + if ((p->addr.sin_addr.s_addr || p->defaddr.sin_addr.s_addr) && + (!p->maxms || ((p->lastms > 0) && (p->lastms <= p->maxms)))) { if (sendani) - *sendani = p->sendani; + *sendani = p->sendani; /* Whether we transmit ANI */ + if (maxtime) + *maxtime = p->maxms; /* Max time they should take */ if (context) strncpy(context, p->context, AST_MAX_EXTENSION - 1); if (p->addr.sin_addr.s_addr) { @@ -1376,6 +1541,21 @@ static int create_addr(struct sockaddr_in *sin, int *capability, int *sendani, c else return 0; } + +static int auto_congest(void *nothing) +{ + int callno = (int)(long)(nothing); + struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_CONGESTION }; + ast_pthread_mutex_lock(&iaxsl[callno]); + if (iaxs[callno]) { + iaxs[callno]->initid = -1; + iax_queue_frame(callno, &f); + ast_log(LOG_NOTICE, "Auto-congesting call due to slow response\n"); + } + ast_pthread_mutex_unlock(&iaxsl[callno]); + return 0; +} + static int iax_call(struct ast_channel *c, char *dest, int timeout) { struct sockaddr_in sin; @@ -1390,20 +1570,24 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout) char context[AST_MAX_EXTENSION] =""; char *portno = NULL; struct chan_iax_pvt *p = c->pvt->pvt; + char *stringp=NULL; if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) { ast_log(LOG_WARNING, "Line is already in use (%s)?\n", c->name); return -1; } strncpy(host, dest, sizeof(host)-1); - strtok(host, "/"); + stringp=host; + strsep(&stringp, "/"); /* If no destination extension specified, use 's' */ - rdest = strtok(NULL, "/"); + rdest = strsep(&stringp, "/"); if (!rdest) rdest = myrdest; - strtok(rdest, "@"); - rcontext = strtok(NULL, "@"); - strtok(host, "@"); - username = strtok(NULL, "@"); + stringp=rdest; + strsep(&stringp, "@"); + rcontext = strsep(&stringp, "@"); + stringp=host; + strsep(&stringp, "@"); + username = strsep(&stringp, "@"); if (username) { /* Really the second argument is the host, not the username */ hname = username; @@ -1412,14 +1596,17 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout) hname = host; } if (username) { - username = strtok(username, ":"); - secret = strtok(NULL, ":"); + stringp=username; + username = strsep(&stringp, ":"); + secret = strsep(&stringp, ":"); } - if (strtok(hname, ":")) { - strtok(hname, ":"); - portno = strtok(hname, ":"); + stringp=hname; + if (strsep(&stringp, ":")) { + stringp=hname; + strsep(&stringp, ":"); + portno = strsep(&stringp, ":"); } - if (create_addr(&sin, NULL, NULL, hname, context)) { + if (create_addr(&sin, NULL, NULL, NULL, hname, context)) { ast_log(LOG_WARNING, "No address associated with '%s'\n", hname); return -1; } @@ -1464,59 +1651,38 @@ static int iax_call(struct ast_channel *c, char *dest, int timeout) /* Transmit the string in a "NEW" request */ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Calling using options '%s'\n", requeststr); - send_command((struct chan_iax_pvt *)c->pvt->pvt, AST_FRAME_IAX, + if (p->maxtime) { + /* Initialize pingtime and auto-congest time */ + p->pingtime = p->maxtime / 2; + p->initid = ast_sched_add(sched, p->maxtime * 2, auto_congest, (void *)p->callno); + } + send_command(p, AST_FRAME_IAX, AST_IAX_COMMAND_NEW, 0, requeststr, strlen(requeststr) + 1, -1); ast_setstate(c, AST_STATE_RINGING); return 0; } -static int iax_predestroy(struct chan_iax_pvt *pvt) -{ - struct ast_channel *c; - if (!pvt->alreadygone) { - /* No more pings or lagrq's */ - if (pvt->pingid > -1) - ast_sched_del(sched, pvt->pingid); - if (pvt->lagid > -1) - ast_sched_del(sched, pvt->lagid); - if (pvt->autoid > -1) - ast_sched_del(sched, pvt->autoid); - pvt->pingid = -1; - pvt->lagid = -1; - pvt->autoid = -1; - pvt->alreadygone = 1; - } - c = pvt->owner; - if (c) { - c->_softhangup |= AST_SOFTHANGUP_DEV; - c->pvt->pvt = NULL; - pvt->owner = NULL; - ast_pthread_mutex_lock(&usecnt_lock); - usecnt--; - if (usecnt < 0) - ast_log(LOG_WARNING, "Usecnt < 0???\n"); - ast_pthread_mutex_unlock(&usecnt_lock); - ast_update_use_count(); - } - return 0; -} - static int iax_hangup(struct ast_channel *c) { struct chan_iax_pvt *pvt = c->pvt->pvt; int alreadygone; + int callno; if (pvt) { + callno = pvt->callno; + ast_pthread_mutex_lock(&iaxsl[callno]); ast_log(LOG_DEBUG, "We're hanging up %s now...\n", c->name); alreadygone = pvt->alreadygone; /* Send the hangup unless we have had a transmission error or are already gone */ if (!pvt->error && !alreadygone) send_command_final(pvt, AST_FRAME_IAX, AST_IAX_COMMAND_HANGUP, 0, NULL, 0, -1); /* Explicitly predestroy it */ - iax_predestroy(pvt); + iax_predestroy_nolock(callno); /* If we were already gone to begin with, destroy us now */ if (alreadygone) { - iax_destroy(pvt->callno); + ast_log(LOG_DEBUG, "Really destroying %s now...\n", c->name); + iax_destroy_nolock(callno); } + ast_pthread_mutex_unlock(&iaxsl[callno]); } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Hungup '%s'\n", c->name); @@ -1972,22 +2138,38 @@ static int iax_show_users(int fd, int argc, char *argv[]) static int iax_show_peers(int fd, int argc, char *argv[]) { -#define FORMAT2 "%-15.15s %-15.15s %-15.15s %s %-15.15s %s\n" -#define FORMAT "%-15.15s %-15.15s %-15.15s %s %-15.15s %d\n" +#define FORMAT2 "%-15.15s %-15.15s %s %-15.15s %-8s %-10s\n" +#define FORMAT "%-15.15s %-15.15s %s %-15.15s %-8d %-10s\n" struct iax_peer *peer; + char name[256] = ""; if (argc != 3) return RESULT_SHOWUSAGE; ast_pthread_mutex_lock(&peerl.lock); - ast_cli(fd, FORMAT2, "Name", "Username", "Host", " ", "Mask", "Port"); + ast_cli(fd, FORMAT2, "Name/Username", "Host", " ", "Mask", "Port", "Status"); for (peer = peerl.peers;peer;peer = peer->next) { char nm[20]; + char status[20]; + if (strlen(peer->username)) + snprintf(name, sizeof(name), "%s/%s", peer->name, peer->username); + else + strncpy(name, peer->name, sizeof(name) - 1); + if (peer->maxms) { + if (peer->lastms < 0) + strcpy(status, "UNREACHABLE"); + else if (peer->lastms > peer->maxms) + snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms); + else if (peer->lastms) + snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms); + else + strcpy(status, "UNKNOWN"); + } else + strcpy(status, "Unmonitored"); strncpy(nm, inet_ntoa(peer->mask), sizeof(nm)-1); - ast_cli(fd, FORMAT, peer->name, - peer->username ? peer->username : "(Any)", + ast_cli(fd, FORMAT, name, peer->addr.sin_addr.s_addr ? inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", - peer->dynamic ? "(D)" : " ", + peer->dynamic ? "(D)" : "(S)", nm, - ntohs(peer->addr.sin_port)); + ntohs(peer->addr.sin_port), status); } ast_pthread_mutex_unlock(&peerl.lock); return RESULT_SUCCESS; @@ -1995,6 +2177,16 @@ static int iax_show_peers(int fd, int argc, char *argv[]) #undef FORMAT2 } +/* JDG: callback to display iax peers in manager */ +static int manager_iax_show_peers( struct mansession *s, struct message *m ) +{ + char *a[] = { "iax", "show", "users" }; + int ret; + ret = iax_show_peers( s->fd, 3, a ); + ast_cli( s->fd, "\r\n" ); + return ret; +} /* /JDG */ + static char *regstate2str(int regstate) { switch(regstate) { @@ -2050,9 +2242,9 @@ static int iax_show_channels(int fd, int argc, char *argv[]) int x; if (argc != 3) return RESULT_SHOWUSAGE; - ast_pthread_mutex_lock(&iaxs_lock); ast_cli(fd, FORMAT2, "Peer", "Username", "ID (Lo/Rem)", "Seq (Tx/Rx)", "Lag", "Jitter", "Format"); - for (x=0;x<AST_IAX_MAX_CALLS;x++) + for (x=0;x<AST_IAX_MAX_CALLS;x++) { + ast_pthread_mutex_lock(&iaxsl[x]); if (iaxs[x]) ast_cli(fd, FORMAT, inet_ntoa(iaxs[x]->addr.sin_addr), strlen(iaxs[x]->username) ? iaxs[x]->username : "(None)", @@ -2061,7 +2253,8 @@ static int iax_show_channels(int fd, int argc, char *argv[]) iaxs[x]->lag, iaxs[x]->jitter, iaxs[x]->voiceformat); - ast_pthread_mutex_unlock(&iaxs_lock); + ast_pthread_mutex_unlock(&iaxsl[x]); + } return RESULT_SUCCESS; #undef FORMAT #undef FORMAT2 @@ -2158,7 +2351,7 @@ static int __send_command(struct chan_iax_pvt *i, char type, int command, unsign f.frametype = type; f.subclass = command; f.datalen = datalen; - f.timelen = 0; + f.samples = 0; f.mallocd = 0; f.offset = 0; f.src = __FUNCTION__; @@ -2180,7 +2373,8 @@ static int forward_command(struct chan_iax_pvt *i, char type, int command, unsig static int send_command_final(struct chan_iax_pvt *i, char type, int command, unsigned int ts, char *data, int datalen, int seqno) { - iax_predestroy(i); + /* It is assumed that the callno has already been locked */ + iax_predestroy_nolock(i->callno); return __send_command(i, type, command, ts, data, datalen, seqno, 0, 0, 1); } @@ -2208,8 +2402,10 @@ static int iax_getformats(int callno, char *orequest) { char *var, *value; char request[256]; + char *stringp=NULL; strncpy(request, orequest, sizeof(request)-1); - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -2220,7 +2416,7 @@ static int iax_getformats(int callno, char *orequest) } else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } return 0; } @@ -2235,10 +2431,12 @@ static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int struct iax_user *user; char request[256]; int gotcapability=0; + char *stringp=NULL; strncpy(request, orequest, sizeof(request)-1); if (!iaxs[callno]) return res; - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -2270,7 +2468,7 @@ static int check_access(int callno, struct sockaddr_in *sin, char *orequest, int else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } if (!gotcapability) iaxs[callno]->peercapability = iaxs[callno]->peerformat; @@ -2366,11 +2564,13 @@ static int authenticate_verify(struct chan_iax_pvt *p, char *orequest) char rsasecret[256] = ""; int res = -1; int x; + char *stringp=NULL; if (!(p->state & IAX_STATE_AUTHENTICATED)) return res; strncpy(request, orequest, sizeof(request)-1); - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -2385,14 +2585,16 @@ static int authenticate_verify(struct chan_iax_pvt *p, char *orequest) else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } if (strstr(p->methods, "rsa") && strlen(rsasecret) && strlen(p->inkeys)) { struct ast_key *key; char *keyn; char tmpkey[256]; + char *stringp=NULL; strncpy(tmpkey, p->inkeys, sizeof(tmpkey)); - keyn = strtok(tmpkey, ":"); + stringp=tmpkey; + keyn = strsep(&stringp, ":"); while(keyn) { key = ast_key_get(keyn, AST_KEY_PUBLIC); if (key && !ast_check_signature(key, p->challenge, rsasecret)) { @@ -2400,7 +2602,7 @@ static int authenticate_verify(struct chan_iax_pvt *p, char *orequest) break; } else if (!key) ast_log(LOG_WARNING, "requested inkey '%s' for RSA authentication does not exist\n", keyn); - keyn = strtok(NULL, ":"); + keyn = strsep(&stringp, ":"); } } else if (strstr(p->methods, "md5")) { struct MD5Context md5; @@ -2436,13 +2638,15 @@ static int register_verify(int callno, struct sockaddr_in *sin, char *orequest) char *keyn; int x; int expire = 0; + char *stringp=NULL; iaxs[callno]->state &= ~IAX_STATE_AUTHENTICATED; strcpy(iaxs[callno]->peer, ""); if (!orequest) return -1; strncpy(request, orequest, sizeof(request)-1); - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -2461,7 +2665,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, char *orequest) else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } if (!strlen(peer)) { @@ -2493,8 +2697,10 @@ static int register_verify(int callno, struct sockaddr_in *sin, char *orequest) if (strlen(rsasecret) && strstr(p->methods, "rsa") && strlen(p->challenge)) { if (strlen(p->inkeys)) { char tmpkeys[256]; + char *stringp=NULL; strncpy(tmpkeys, p->inkeys, sizeof(tmpkeys)); - keyn = strtok(tmpkeys, ":"); + stringp=tmpkeys; + keyn = strsep(&stringp, ":"); while(keyn) { key = ast_key_get(keyn, AST_KEY_PUBLIC); if (key && !ast_check_signature(key, p->challenge, rsasecret)) { @@ -2502,7 +2708,7 @@ static int register_verify(int callno, struct sockaddr_in *sin, char *orequest) break; } else if (!key) ast_log(LOG_WARNING, "requested inkey '%s' does not exist\n", keyn); - keyn = strtok(NULL, ":"); + keyn = strsep(&stringp, ":"); } if (!keyn) { ast_log(LOG_NOTICE, "Host %s failed RSA authentication with inkeys '%s'\n", peer, p->inkeys); @@ -2605,9 +2811,11 @@ static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, c char methods[80] = ""; char requeststr[256] = ""; char *var, *value; + char *stringp=NULL; strncpy(request, orequest, sizeof(request)-1); - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -2622,7 +2830,7 @@ static int authenticate_reply(struct chan_iax_pvt *p, struct sockaddr_in *sin, c else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } /* Check for override RSA authentication first */ @@ -2674,12 +2882,14 @@ static int try_transfer(struct chan_iax_pvt *pvt, char *orequest) char *var, *value; struct sockaddr_in new; + char *stringp=NULL; if (!orequest) return -1; strncpy(request, orequest, sizeof(request)-1); - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -2694,7 +2904,7 @@ static int try_transfer(struct chan_iax_pvt *pvt, char *orequest) else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } if (!newcall || !inet_aton(newip, &new.sin_addr) || !newport) { ast_log(LOG_WARNING, "Invalid transfer request\n"); @@ -2717,10 +2927,13 @@ static int complete_dpreply(struct chan_iax_pvt *pvt, char *orequest) int status = CACHE_FLAG_UNKNOWN; int expirey = iaxdefaultdpcache; int x; + int matchmore = 0; struct iax_dpcache *dp, *prev; + char *stringp=NULL; strncpy(request, orequest, sizeof(request)-1); - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -2741,10 +2954,12 @@ static int complete_dpreply(struct chan_iax_pvt *pvt, char *orequest) expirey = atoi(value); else if (!strcmp(var, "ignorepat")) { /* Don' really do much with it */ - } else + } else if (!strcmp(var, "matchmore")) { + matchmore = CACHE_FLAG_MATCHMORE; + } else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } ast_pthread_mutex_lock(&dpcache_lock); prev = NULL; @@ -2762,6 +2977,7 @@ static int complete_dpreply(struct chan_iax_pvt *pvt, char *orequest) if (dp->flags & CACHE_FLAG_PENDING) { dp->flags &= ~CACHE_FLAG_PENDING; dp->flags |= status; + dp->flags |= CACHE_FLAG_MATCHMORE; } /* Wake up waiters */ for (x=0;x<sizeof(dp->waiters) / sizeof(dp->waiters[0]); x++) @@ -2782,11 +2998,13 @@ static int complete_transfer(int callno, char *orequest) char *var, *value; struct chan_iax_pvt *pvt = iaxs[callno]; struct ast_iax_frame *cur; + char *stringp=NULL; if (!orequest) return -1; strncpy(request, orequest, sizeof(request)-1); - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -2797,7 +3015,7 @@ static int complete_transfer(int callno, char *orequest) else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } if (peercallno < 0) { ast_log(LOG_WARNING, "Invalid transfer request\n"); @@ -2835,7 +3053,7 @@ static int complete_transfer(int callno, char *orequest) return 0; } -static int iax_ack_registry(char *orequest, struct sockaddr_in *sin) +static int iax_ack_registry(char *orequest, struct sockaddr_in *sin, int callno) { struct iax_registry *reg; /* Start pessimistic */ @@ -2846,12 +3064,14 @@ static int iax_ack_registry(char *orequest, struct sockaddr_in *sin) char ourip[256] = "<Unspecified>"; struct sockaddr_in oldus; char *var, *value; + char *stringp=NULL; if (!orequest) return -1; strncpy(request, orequest, sizeof(request)-1); - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -2870,11 +3090,9 @@ static int iax_ack_registry(char *orequest, struct sockaddr_in *sin) } else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } - reg = registrations; - while(reg) { - if (!strcasecmp(reg->username, peer)) { + reg = iaxs[callno]->reg; memcpy(&oldus, ®->us, sizeof(oldus)); if (memcmp(®->addr, sin, sizeof(®->addr))) { ast_log(LOG_WARNING, "Received unsolicited registry ack from '%s'\n", inet_ntoa(sin->sin_addr)); @@ -2898,9 +3116,6 @@ static int iax_ack_registry(char *orequest, struct sockaddr_in *sin) } reg->regstate = REG_STATE_REGISTERED; return 0; - } - reg = reg->next; - } ast_log(LOG_WARNING, "Registry acknowledge on unknown registery '%s'\n", peer); return -1; } @@ -2911,21 +3126,25 @@ static int iax_register(char *value, int lineno) char copy[256]; char *username, *hostname, *secret; char *porta; + char *stringp=NULL; struct hostent *hp; if (!value) return -1; strncpy(copy, value, sizeof(copy)-1); - username = strtok(copy, "@"); - hostname = strtok(NULL, "@"); + stringp=copy; + username = strsep(&stringp, "@"); + hostname = strsep(&stringp, "@"); if (!hostname) { ast_log(LOG_WARNING, "Format for registration is user[:secret]@host[:port] at line %d", lineno); return -1; } - username = strtok(username, ":"); - secret = strtok(NULL, ":"); - hostname = strtok(hostname, ":"); - porta = strtok(NULL, ";"); + stringp=username; + username = strsep(&stringp, ":"); + secret = strsep(&stringp, ":"); + stringp=hostname; + hostname = strsep(&stringp, ":"); + porta = strsep(&stringp, ":"); if (porta && !atoi(porta)) { ast_log(LOG_WARNING, "%s is not a valid port number at line %d\n", porta, lineno); @@ -2971,8 +3190,13 @@ static int expire_registry(void *data) return 0; } + +static int iax_poke_peer(struct iax_peer *peer); + + static int update_registry(char *name, struct sockaddr_in *sin, int callno) { + /* Called from IAX thread only, with proper iaxsl lock */ char requeststr[256] = ""; struct iax_peer *p; for (p = peerl.peers;p;p = p->next) { @@ -2983,6 +3207,7 @@ static int update_registry(char *name, struct sockaddr_in *sin, int callno) if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Registered '%s' (%s) at %s:%d\n", p->name, iaxs[callno]->state & IAX_STATE_AUTHENTICATED ? "AUTHENTICATED" : "UNAUTHENTICATED", inet_ntoa(sin->sin_addr), htons(sin->sin_port)); + iax_poke_peer(p); } /* Update the host */ memcpy(&p->addr, sin, sizeof(p->addr)); @@ -3034,12 +3259,14 @@ static int registry_rerequest(char *orequest, int callno, struct sockaddr_in *si char challenge[256] = ""; char *var, *value; int res; + char *stringp=NULL; if (!orequest) return -1; strncpy(request, orequest, sizeof(request)-1); - var = strtok(request, ";"); + stringp=request; + var = strsep(&stringp, ";"); while(var) { value = strchr(var, '='); if (value) { @@ -3054,11 +3281,9 @@ static int registry_rerequest(char *orequest, int callno, struct sockaddr_in *si else ast_log(LOG_WARNING, "Unknown variable '%s' with value '%s'\n", var, value); } - var = strtok(NULL, ";"); + var = strsep(&stringp, ";"); } - reg = registrations; - while(reg) { - if (!strcasecmp(reg->username, peer)) { + reg = iaxs[callno]->reg; if (memcmp(®->addr, sin, sizeof(®->addr))) { ast_log(LOG_WARNING, "Received unsolicited registry authenticate request from '%s'\n", inet_ntoa(sin->sin_addr)); return -1; @@ -3081,9 +3306,6 @@ static int registry_rerequest(char *orequest, int callno, struct sockaddr_in *si return send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_REGREQ, 0, requeststr, strlen(requeststr) + 1, -1); } else return -1; - } - reg = reg->next; - } ast_log(LOG_WARNING, "Registry acknowledge on unknown registery '%s'\n", peer); return -1; } @@ -3099,16 +3321,22 @@ static int stop_stuff(int callno) if (iaxs[callno]->autoid > -1) ast_sched_del(sched, iaxs[callno]->autoid); iaxs[callno]->autoid = -1; + if (iaxs[callno]->initid > -1) + ast_sched_del(sched, iaxs[callno]->initid); + iaxs[callno]->initid = -1; return 0; } static int auto_hangup(void *nothing) { + /* Called from IAX thread only, without iaxs lock */ int callno = (int)(long)(nothing); + ast_pthread_mutex_lock(&iaxsl[callno]); if (iaxs[callno]) { iaxs[callno]->autoid = -1; send_command_final(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_HANGUP, 0, "Timeout", strlen("Timeout") + 1, -1); } + ast_pthread_mutex_unlock(&iaxsl[callno]); return 0; } @@ -3143,6 +3371,14 @@ static void vnak_retransmit(int callno, int last) ast_pthread_mutex_unlock(&iaxq.lock); } +static int iax_poke_peer_s(void *data) +{ + struct iax_peer *peer = data; + peer->pokeexpire = -1; + iax_poke_peer(peer); + return 0; +} + static int socket_read(int *id, int fd, short events, void *cbdata) { struct sockaddr_in sin; @@ -3158,8 +3394,10 @@ static int socket_read(int *id, int fd, short events, void *cbdata) struct ast_frame f; struct ast_channel *c; struct iax_dpcache *dp; + struct iax_peer *peer; int format; int exists; + int mm; char rel0[256]; char rel1[255]; char empty[32]=""; /* Safety measure */ @@ -3188,15 +3426,20 @@ static int socket_read(int *id, int fd, short events, void *cbdata) f.subclass = fh->subclasshigh << 16; f.subclass += ntohs(fh->subclasslow); #endif - if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == AST_IAX_COMMAND_NEW) || (f.subclass == AST_IAX_COMMAND_REGREQ))) + if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == AST_IAX_COMMAND_NEW) || (f.subclass == AST_IAX_COMMAND_REGREQ) + || (f.subclass == AST_IAX_COMMAND_POKE))) new = NEW_ALLOW; } else { /* Don't knwo anything about it yet */ f.frametype = AST_FRAME_NULL; f.subclass = 0; } - ast_pthread_mutex_lock(&iaxs_lock); + fr.callno = find_callno(ntohs(mh->callno) & ~AST_FLAG_FULL, dcallno, &sin, new); + + if (fr.callno > 0) + ast_pthread_mutex_lock(&iaxsl[fr.callno]); + if ((fr.callno < 0) || !iaxs[fr.callno]) { /* A call arrived for a non-existant destination. Unless it's an "inval" frame, reply with an inval */ @@ -3209,7 +3452,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata) raw_hangup(&sin, ntohs(fh->dcallno), ntohs(mh->callno) & ~AST_FLAG_FULL ); } - ast_pthread_mutex_unlock(&iaxs_lock); + if (fr.callno > 0) + ast_pthread_mutex_unlock(&iaxsl[fr.callno]); return 1; } if (((f.subclass != AST_IAX_COMMAND_TXCNT) && @@ -3248,7 +3492,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) /* Send a VNAK requesting retransmission */ iax_vnak(fr.callno); } - ast_pthread_mutex_unlock(&iaxs_lock); + ast_pthread_mutex_unlock(&iaxsl[fr.callno]); return 1; } } else { @@ -3264,7 +3508,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) /* A full frame */ if (res < sizeof(struct ast_iax_full_hdr)) { ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, sizeof(struct ast_iax_full_hdr)); - ast_pthread_mutex_unlock(&iaxs_lock); + ast_pthread_mutex_unlock(&iaxsl[fr.callno]); return 1; } f.datalen = res - sizeof(struct ast_iax_full_hdr); @@ -3298,6 +3542,11 @@ static int socket_read(int *id, int fd, short events, void *cbdata) } } if (f.frametype == AST_FRAME_IAX) { + if (iaxs[fr.callno]->initid > -1) { + /* Don't auto congest anymore since we've gotten something usefulb ack */ + ast_sched_del(sched, iaxs[fr.callno]->initid); + iaxs[fr.callno]->initid = -1; + } /* Handle the IAX pseudo frame itself */ if (option_debug) ast_log(LOG_DEBUG, "IAX subclass %d received\n", f.subclass); @@ -3314,8 +3563,11 @@ static int socket_read(int *id, int fd, short events, void *cbdata) if ((fr.callno == cur->callno) && (fr.seqno == cur->seqno)) { cur->retries = -1; /* Destroy call if this is the end */ - if (cur->final) - iax_destroy(fr.callno); + if (cur->final) { + if (option_debug) + ast_log(LOG_DEBUG, "Really destroying %d, having been acked on final message\n", fr.callno); + iax_destroy_nolock(fr.callno); + } } } ast_pthread_mutex_unlock(&iaxq.lock); @@ -3354,10 +3606,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata) ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s'\n", inet_ntoa(sin.sin_addr), (char *)f.data); break; } - ast_pthread_mutex_unlock(&iaxs_lock); /* This might re-enter the IAX code and need the lock */ exists = ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->callerid); - ast_pthread_mutex_lock(&iaxs_lock); if (!strlen(iaxs[fr.callno]->secret) && !strlen(iaxs[fr.callno]->inkeys)) { if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) { send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1); @@ -3382,23 +3632,23 @@ static int socket_read(int *id, int fd, short events, void *cbdata) } } } - /* No authentication required, let them in */ - snprintf(rel1, sizeof(rel1), "formats=%d;", format); - send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, rel1, strlen(rel1) + 1, -1); - if (strcmp(iaxs[fr.callno]->exten, "TBD")) { - iaxs[fr.callno]->state |= IAX_STATE_STARTED; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting unauthenticated call from %s, requested format = %d, actual format = %d\n", - inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat,format); - if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, iax_capability))) { - iax_destroy(fr.callno); - } else - c->nativeformats = format; - } else { - iaxs[fr.callno]->state |= IAX_STATE_TBD; - /* If this is a TBD call, we're ready but now what... */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", inet_ntoa(sin.sin_addr)); + if (format) { + /* No authentication required, let them in */ + snprintf(rel1, sizeof(rel1), "formats=%d;", format); + send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, rel1, strlen(rel1) + 1, -1); + if (strcmp(iaxs[fr.callno]->exten, "TBD")) { + iaxs[fr.callno]->state |= IAX_STATE_STARTED; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Accepting unauthenticated call from %s, requested format = %d, actual format = %d\n", + inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat,format); + if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, format))) + iax_destroy_nolock(fr.callno); + } else { + iaxs[fr.callno]->state |= IAX_STATE_TBD; + /* If this is a TBD call, we're ready but now what... */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Accepted unauthenticated TBD call from %s\n", inet_ntoa(sin.sin_addr)); + } } } break; @@ -3411,6 +3661,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) ((char *)f.data)[f.datalen] = '\0'; if ((iaxs[fr.callno]->state & IAX_STATE_TBD) && !(iaxs[fr.callno]->state & IAX_STATE_STARTED) && f.datalen) { + mm = ast_matchmore_extension(NULL, iaxs[fr.callno]->context, (char *)f.data, 1, iaxs[fr.callno]->callerid); /* Must be started */ if (ast_exists_extension(NULL, iaxs[fr.callno]->context, (char *)f.data, 1, iaxs[fr.callno]->callerid)) { strcpy(rel0, "exists"); @@ -3419,16 +3670,17 @@ static int socket_read(int *id, int fd, short events, void *cbdata) } else { strcpy(rel0, "nonexistant"); } - snprintf(rel1, sizeof(rel1), "number=%s;status=%s;ignorepat=%s;expirey=%d;", + snprintf(rel1, sizeof(rel1), "number=%s;status=%s;ignorepat=%s;expirey=%d;matchmore=%s;", (char *)f.data, rel0, ast_ignore_pattern(iaxs[fr.callno]->context, (char *)f.data) ? "yes" : "no", - iaxdefaultdpcache); + iaxdefaultdpcache, mm ? "yes" : "no"); send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_DPREP, 0, rel1, strlen(rel1) + 1, -1); } break; case AST_IAX_COMMAND_HANGUP: iaxs[fr.callno]->alreadygone = 1; - iax_destroy(fr.callno); + ast_log(LOG_DEBUG, "Immediately destroying %d, having received hangup\n", fr.callno); + iax_destroy_nolock(fr.callno); break; case AST_IAX_COMMAND_REJECT: if (f.data) @@ -3436,7 +3688,8 @@ static int socket_read(int *id, int fd, short events, void *cbdata) if (iaxs[fr.callno]->owner) ast_log(LOG_WARNING, "Call rejected by %s: %s\n", inet_ntoa(iaxs[fr.callno]->addr.sin_addr), (char *)f.data); iaxs[fr.callno]->error = EPERM; - iax_destroy(fr.callno); + ast_log(LOG_DEBUG, "Immediately destroying %d, having received reject\n", fr.callno); + iax_destroy_nolock(fr.callno); break; case AST_IAX_COMMAND_ACCEPT: /* Ignore if call is already up or needs authentication or is a TBD */ @@ -3480,6 +3733,10 @@ static int socket_read(int *id, int fd, short events, void *cbdata) } ast_pthread_mutex_unlock(&dpcache_lock); break; + case AST_IAX_COMMAND_POKE: + /* Send back a pong packet with the original timestamp */ + send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_PONG, fr.ts, NULL, 0, -1); + break; case AST_IAX_COMMAND_PING: #ifdef BRIDGE_OPTIMIZATION if (iaxs[fr.callno]->bridgecallno > -1) { @@ -3492,7 +3749,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) #else /* Send back a pong packet with the original timestamp */ send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_PONG, fr.ts, NULL, 0, -1); -#endif +#endif break; case AST_IAX_COMMAND_PONG: #ifdef BRIDGE_OPTIMIZATION @@ -3506,7 +3763,27 @@ static int socket_read(int *id, int fd, short events, void *cbdata) #else /* Calculate ping time */ iaxs[fr.callno]->pingtime = calc_timestamp(iaxs[fr.callno], 0) - fr.ts; -#endif +#endif + if (iaxs[fr.callno]->peerpoke) { + peer = iaxs[fr.callno]->peerpoke; + if ((peer->lastms < 0) || (peer->lastms > peer->maxms)) { + if (iaxs[fr.callno]->pingtime <= peer->maxms) + ast_log(LOG_NOTICE, "Peer '%s' is now REACHABLE!\n", peer->name); + } else if ((peer->lastms > 0) && (peer->lastms <= peer->maxms)) { + if (iaxs[fr.callno]->pingtime > peer->maxms) + ast_log(LOG_NOTICE, "Peer '%s' is now TOO LAGGED (%d ms)!\n", peer->name, iaxs[fr.callno]->pingtime); + } + peer->lastms = iaxs[fr.callno]->pingtime; + peer->callno = -1; + if (peer->pokeexpire > -1) + ast_sched_del(sched, peer->pokeexpire); + iax_destroy_nolock(fr.callno); + /* Try again eventually */ + if ((peer->lastms < 0) || (peer->lastms > peer->maxms)) + peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, iax_poke_peer_s, peer); + else + peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_OK, iax_poke_peer_s, peer); + } break; case AST_IAX_COMMAND_LAGRQ: case AST_IAX_COMMAND_LAGRP: @@ -3523,7 +3800,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) f.mallocd = 0; f.offset = 0; fr.f = &f; - f.timelen = 0; + f.samples = 0; schedule_delivery(iaxfrdup2(&fr, 0), 1); #ifdef BRIDGE_OPTIMIZATION } @@ -3553,13 +3830,11 @@ static int socket_read(int *id, int fd, short events, void *cbdata) send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No authority found", strlen("No authority found"), -1); break; } - ast_pthread_mutex_unlock(&iaxs_lock); /* This might re-enter the IAX code and need the lock */ exists = ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->callerid); - ast_pthread_mutex_lock(&iaxs_lock); if (strcmp(iaxs[fr.callno]->exten, "TBD") && !exists) { - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1); ast_log(LOG_NOTICE, "Rejected connect attempt from %s, request '%s@%s' does not exist\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context); + send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1); } else { /* Select an appropriate format */ format = iaxs[fr.callno]->peerformat & iax_capability; @@ -3567,36 +3842,36 @@ static int socket_read(int *id, int fd, short events, void *cbdata) ast_log(LOG_DEBUG, "We don't do requested format %d, falling back to peer capability %d\n", iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability); format = iaxs[fr.callno]->peercapability & iax_capability; if (!format) { - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "Unable to negotiate codec", strlen("Unable to negotiate codec"), -1); ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability, iax_capability); + send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "Unable to negotiate codec", strlen("Unable to negotiate codec"), -1); } else { /* Pick one... */ format = ast_best_codec(iaxs[fr.callno]->peercapability & iax_capability); if (!format) { ast_log(LOG_ERROR, "No best format in 0x%x???\n", iaxs[fr.callno]->peercapability & iax_capability); - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "Unable to negotiate codec", strlen("Unable to negotiate codec"), -1); ast_log(LOG_NOTICE, "Rejected connect attempt from %s, requested/capability 0x%x/0x%x incompatible with our capability 0x%x.\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat, iaxs[fr.callno]->peercapability, iax_capability); + send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "Unable to negotiate codec", strlen("Unable to negotiate codec"), -1); } } } - /* Authentication received */ - snprintf(rel1, sizeof(rel1), "formats=%d;", format); - send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, rel1, strlen(rel1) + 1, -1); - if (strcmp(iaxs[fr.callno]->exten, "TBD")) { - iaxs[fr.callno]->state |= IAX_STATE_STARTED; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepting AUTHENTICATED call from %s, requested format = %d, actual format = %d\n", - inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat,format); - iaxs[fr.callno]->state |= IAX_STATE_STARTED; - if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, iax_capability))) { - iax_destroy(fr.callno); - } else - c->nativeformats = iaxs[fr.callno]->peerformat; - } else { - iaxs[fr.callno]->state |= IAX_STATE_TBD; - /* If this is a TBD call, we're ready but now what... */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", inet_ntoa(sin.sin_addr)); + if (format) { + /* Authentication received */ + snprintf(rel1, sizeof(rel1), "formats=%d;", format); + send_command(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_ACCEPT, 0, rel1, strlen(rel1) + 1, -1); + if (strcmp(iaxs[fr.callno]->exten, "TBD")) { + iaxs[fr.callno]->state |= IAX_STATE_STARTED; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Accepting AUTHENTICATED call from %s, requested format = %d, actual format = %d\n", + inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat,format); + iaxs[fr.callno]->state |= IAX_STATE_STARTED; + if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, format))) + iax_destroy_nolock(fr.callno); + } else { + iaxs[fr.callno]->state |= IAX_STATE_TBD; + /* If this is a TBD call, we're ready but now what... */ + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Accepted AUTHENTICATED TBD call from %s\n", inet_ntoa(sin.sin_addr)); + } } } break; @@ -3606,23 +3881,22 @@ static int socket_read(int *id, int fd, short events, void *cbdata) iaxs[fr.callno]->state &= ~IAX_STATE_TBD; strncpy(iaxs[fr.callno]->exten, (char *)f.data, sizeof(iaxs[fr.callno]->exten)-1); if (!ast_exists_extension(NULL, iaxs[fr.callno]->context, iaxs[fr.callno]->exten, 1, iaxs[fr.callno]->callerid)) { - send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1); ast_log(LOG_NOTICE, "Rejected dial attempt from %s, request '%s@%s' does not exist\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->exten, iaxs[fr.callno]->context); + send_command_final(iaxs[fr.callno], AST_FRAME_IAX, AST_IAX_COMMAND_REJECT, 0, "No such context/extension", strlen("No such context/extension"), -1); } else { iaxs[fr.callno]->state |= IAX_STATE_STARTED; if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Accepting DIAL from %s, formats = 0x%x\n", inet_ntoa(sin.sin_addr), iaxs[fr.callno]->peerformat); iaxs[fr.callno]->state |= IAX_STATE_STARTED; - if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, iax_capability))) { - iax_destroy(fr.callno); - } else - c->nativeformats = iaxs[fr.callno]->peerformat; + if(!(c = ast_iax_new(iaxs[fr.callno], AST_STATE_RING, iaxs[fr.callno]->peerformat))) + iax_destroy_nolock(fr.callno); } } break; case AST_IAX_COMMAND_INVAL: iaxs[fr.callno]->error = ENOTCONN; - iax_destroy(fr.callno); + ast_log(LOG_DEBUG, "Immediately destroying %d, having received INVAL\n", fr.callno); + iax_destroy_nolock(fr.callno); if (option_debug) ast_log(LOG_DEBUG, "Destroying call %d\n", fr.callno); break; @@ -3648,9 +3922,9 @@ static int socket_read(int *id, int fd, short events, void *cbdata) case AST_IAX_COMMAND_REGACK: if (f.data) ((char *)f.data)[f.datalen] = '\0'; - if (iax_ack_registry(f.data, &sin)) + if (iax_ack_registry(f.data, &sin, fr.callno)) ast_log(LOG_WARNING, "Registration failure\n"); - iax_destroy(fr.callno); + iax_destroy_nolock(fr.callno); break; case AST_IAX_COMMAND_REGREJ: if (f.data) @@ -3659,7 +3933,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) ast_log(LOG_NOTICE, "Registration of '%s' rejected: %s\n", iaxs[fr.callno]->reg->username, (char *)f.data); iaxs[fr.callno]->reg->regstate = REG_STATE_REJECTED; } - iax_destroy(fr.callno); + iax_destroy_nolock(fr.callno); break; case AST_IAX_COMMAND_REGAUTH: /* Authentication request */ @@ -3734,7 +4008,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr.callno, iaxs[fr.callno]->peercallno); } /* Don't actually pass these frames along */ - ast_pthread_mutex_unlock(&iaxs_lock); + ast_pthread_mutex_unlock(&iaxsl[fr.callno]); return 1; } } else { @@ -3745,13 +4019,13 @@ static int socket_read(int *id, int fd, short events, void *cbdata) else { ast_log(LOG_WARNING, "Received mini frame before first full voice frame\n "); iax_vnak(fr.callno); - ast_pthread_mutex_unlock(&iaxs_lock); + ast_pthread_mutex_unlock(&iaxsl[fr.callno]); return 1; } f.datalen = res - sizeof(struct ast_iax_mini_hdr); if (f.datalen < 0) { ast_log(LOG_WARNING, "Datalen < 0?\n"); - ast_pthread_mutex_unlock(&iaxs_lock); + ast_pthread_mutex_unlock(&iaxsl[fr.callno]); return 1; } if (f.datalen) @@ -3762,7 +4036,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) } /* Don't pass any packets until we're started */ if (!(iaxs[fr.callno]->state & IAX_STATE_STARTED)) { - ast_pthread_mutex_unlock(&iaxs_lock); + ast_pthread_mutex_unlock(&iaxsl[fr.callno]); return 1; } /* Common things */ @@ -3771,9 +4045,9 @@ static int socket_read(int *id, int fd, short events, void *cbdata) f.offset = 0; fr.f = &f; if (f.datalen && (f.frametype == AST_FRAME_VOICE)) - f.timelen = get_timelen(&f); + f.samples = get_samples(&f); else - f.timelen = 0; + f.samples = 0; /* If this is our most recent packet, use it as our basis for timestamping */ if (iaxs[fr.callno]->last < fr.ts) { @@ -3794,7 +4068,7 @@ static int socket_read(int *id, int fd, short events, void *cbdata) schedule_delivery(iaxfrdup2(&fr, 0), 1); #endif /* Always run again */ - ast_pthread_mutex_unlock(&iaxs_lock); + ast_pthread_mutex_unlock(&iaxsl[fr.callno]); return 1; } @@ -3826,6 +4100,51 @@ static int iax_do_register(struct iax_registry *reg) return 0; } + +static int iax_poke_noanswer(void *data) +{ + struct iax_peer *peer = data; + peer->pokeexpire = -1; + if (peer->lastms > -1) + ast_log(LOG_NOTICE, "Peer '%s' is now UNREACHABLE!\n", peer->name); + if (peer->callno > 0) + iax_destroy(peer->callno); + peer->callno = 0; + peer->lastms = -1; + /* Try again quickly */ + peer->pokeexpire = ast_sched_add(sched, DEFAULT_FREQ_NOTOK, iax_poke_peer_s, peer); + return 0; +} + +static int iax_poke_peer(struct iax_peer *peer) +{ + if (!peer->maxms || !peer->addr.sin_addr.s_addr) { + /* IF we have no IP, or this isn't to be monitored, return + imeediately after clearing things out */ + peer->lastms = 0; + peer->pokeexpire = -1; + peer->callno = 0; + return 0; + } + if (peer->callno > 0) { + ast_log(LOG_NOTICE, "Still have a callno...\n"); + iax_destroy(peer->callno); + } + peer->callno = find_callno(-1, -1, &peer->addr, NEW_FORCE); + if (peer->callno < 0) { + ast_log(LOG_WARNING, "Unable to allocate call for poking peer '%s'\n", peer->name); + return -1; + } + if (peer->pokeexpire > -1) + ast_sched_del(sched, peer->pokeexpire); + /* Speed up retransmission times */ + iaxs[peer->callno]->pingtime = peer->maxms / 4 + 1; + iaxs[peer->callno]->peerpoke = peer; + send_command(iaxs[peer->callno], AST_FRAME_IAX, AST_IAX_COMMAND_POKE, 0, NULL, 0, -1); + peer->pokeexpire = ast_sched_add(sched, DEFAULT_MAXMS * 2, iax_poke_noanswer, peer); + return 0; +} + static void free_context(struct iax_context *con) { struct iax_context *conl; @@ -3841,31 +4160,39 @@ static struct ast_channel *iax_request(char *type, int format, void *data) int callno; int res; int sendani; + int maxtime; int fmt, native; struct sockaddr_in sin; char s[256]; char *st; struct ast_channel *c; + char *stringp=NULL; int capability = iax_capability; strncpy(s, (char *)data, sizeof(s)-1); - strtok(s, "/"); - strtok(s, "@"); - st = strtok(NULL, "@"); + /* FIXME The next two lines seem useless */ + stringp=s; + strsep(&stringp, "/"); + + stringp=s; + strsep(&stringp, "@"); + st = strsep(&stringp, "@"); if (!st) st = s; /* Populate our address from the given */ - if (create_addr(&sin, &capability, &sendani, st, NULL)) { + if (create_addr(&sin, &capability, &sendani, &maxtime, st, NULL)) { return NULL; } - ast_pthread_mutex_lock(&iaxs_lock); callno = find_callno(-1, -1, &sin, NEW_FORCE); if (callno < 0) { ast_log(LOG_WARNING, "Unable to create call\n"); return NULL; } + ast_pthread_mutex_lock(&iaxsl[callno]); /* Keep track of sendani flag */ iaxs[callno]->sendani = sendani; + iaxs[callno]->maxtime = maxtime; c = ast_iax_new(iaxs[callno], AST_STATE_DOWN, capability); + ast_pthread_mutex_unlock(&iaxsl[callno]); if (c) { /* Choose a format we can live with */ if (c->nativeformats & format) @@ -3877,13 +4204,11 @@ static struct ast_channel *iax_request(char *type, int format, void *data) if (res < 0) { ast_log(LOG_WARNING, "Unable to create translator path for %d to %d on %s\n", c->nativeformats, fmt, c->name); ast_hangup(c); - ast_pthread_mutex_unlock(&iaxs_lock); return NULL; } c->nativeformats = native; } } - ast_pthread_mutex_unlock(&iaxs_lock); return c; } @@ -3895,7 +4220,6 @@ static void *network_thread(void *ignore) struct ast_iax_frame *f, *freeme; /* Establish I/O callback for socket read */ ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL); - ast_pthread_mutex_lock(&iaxs_lock); for(;;) { /* Go through the queue, sending messages which have not yet been sent, and scheduling retransmissions if appropriate */ @@ -3905,7 +4229,7 @@ static void *network_thread(void *ignore) freeme = NULL; if (!f->sentyet) { f->sentyet++; - /* Send a copy immediately */ + /* Send a copy immediately -- errors here are ok, so don't bother locking */ if (iaxs[f->callno]) { send_packet(f); } @@ -3936,10 +4260,10 @@ static void *network_thread(void *ignore) ast_iax_frame_free(freeme); } ast_pthread_mutex_unlock(&iaxq.lock); - ast_pthread_mutex_unlock(&iaxs_lock); res = ast_sched_wait(sched); + if ((res > 1000) || (res < 0)) + res = 1000; res = ast_io_wait(io, res); - ast_pthread_mutex_lock(&iaxs_lock); if (res >= 0) { ast_sched_runq(sched); } @@ -3993,6 +4317,7 @@ static struct iax_peer *build_peer(char *name, struct ast_variable *v) peer = malloc(sizeof(struct iax_peer)); memset(peer, 0, sizeof(struct iax_peer)); peer->expire = -1; + peer->pokeexpire = -1; } if (peer) { if (!found) { @@ -4075,6 +4400,15 @@ static struct iax_peer *build_peer(char *name, struct ast_variable *v) strncpy(peer->inkeys, v->value, sizeof(peer->inkeys)); } else if (!strcasecmp(v->name, "outkey")) { strncpy(peer->outkey, v->value, sizeof(peer->outkey)); + } else if (!strcasecmp(v->name, "qualify")) { + if (!strcasecmp(v->value, "no")) { + peer->maxms = 0; + } else if (!strcasecmp(v->value, "yes")) { + peer->maxms = DEFAULT_MAXMS; + } else if (sscanf(v->value, "%d", &peer->maxms) != 1) { + ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno); + peer->maxms = 0; + } } v=v->next; @@ -4181,14 +4515,26 @@ void delete_users(void){ void prune_peers(void){ /* Prune peers who still are supposed to be deleted */ struct iax_peer *peer, *peerlast, *peernext; + int x; ast_pthread_mutex_lock(&peerl.lock); peerlast = NULL; for (peer=peerl.peers;peer;) { peernext = peer->next; if (peer->delme) { + for (x=0;x<AST_IAX_MAX_CALLS;x++) { + ast_pthread_mutex_lock(&iaxsl[x]); + if (iaxs[x] && (iaxs[x]->peerpoke == peer)) { + iax_destroy(x); + } + ast_pthread_mutex_unlock(&iaxsl[x]); + } /* Delete it, it needs to disappear */ if (peer->expire > -1) ast_sched_del(sched, peer->expire); + if (peer->pokeexpire > -1) + ast_sched_del(sched, peer->pokeexpire); + if (peer->callno > 0) + iax_destroy(peer->callno); free(peer); if (peerlast) peerlast->next = peernext; @@ -4270,7 +4616,9 @@ int set_config(char *config_file, struct sockaddr_in* sin){ } else if (!strcasecmp(v->name, "register")) { iax_register(v->value, v->lineno); } else if (!strcasecmp(v->name, "tos")) { - if (!strcasecmp(v->value, "lowdelay")) + if (sscanf(v->value, "%i", &format) == 1) + tos = format & 0xff; + else if (!strcasecmp(v->value, "lowdelay")) tos = IPTOS_LOWDELAY; else if (!strcasecmp(v->value, "throughput")) tos = IPTOS_THROUGHPUT; @@ -4364,8 +4712,12 @@ static int cache_get_callno(char *data) for (x=0;x<AST_IAX_MAX_CALLS; x++) { /* Look for an *exact match* call. Once a call is negotiated, it can only look up entries for a single context */ - if (iaxs[x] && !strcasecmp(data, iaxs[x]->dproot)) + ast_pthread_mutex_lock(&iaxsl[x]); + if (iaxs[x] && !strcasecmp(data, iaxs[x]->dproot)) { + ast_pthread_mutex_unlock(&iaxsl[x]); return x; + } + ast_pthread_mutex_unlock(&iaxsl[x]); } /* No match found, we need to create a new one */ strncpy(st, data, sizeof(st)-1); @@ -4393,18 +4745,17 @@ static int cache_get_callno(char *data) host = st; } /* Populate our address from the given */ - if (create_addr(&sin, NULL, NULL, host, NULL)) { + if (create_addr(&sin, NULL, NULL, NULL, host, NULL)) { return -1; } ast_log(LOG_DEBUG, "host: %s, user: %s, password: %s, context: %s\n", host, username, password, context); - ast_pthread_mutex_lock(&iaxs_lock); callno = find_callno(-1, -1, &sin, NEW_FORCE); - ast_pthread_mutex_unlock(&iaxs_lock); - strncpy(iaxs[callno]->dproot, data, sizeof(iaxs[callno]->dproot)-1); if (callno < 0) { ast_log(LOG_WARNING, "Unable to create call\n"); return -1; } + ast_pthread_mutex_lock(&iaxsl[callno]); + strncpy(iaxs[callno]->dproot, data, sizeof(iaxs[callno]->dproot)-1); iaxs[callno]->capability = IAX_CAPABILITY_FULLBANDWIDTH; MYSNPRINTF "exten=TBD;"); if (context) @@ -4424,6 +4775,7 @@ static int cache_get_callno(char *data) ast_verbose(VERBOSE_PREFIX_3 "Calling TBD using options '%s'\n", requeststr); /* Start the call going */ send_command(iaxs[callno], AST_FRAME_IAX, AST_IAX_COMMAND_NEW, 0, requeststr, strlen(requeststr) + 1, -1); + ast_pthread_mutex_unlock(&iaxsl[callno]); return callno; } @@ -4618,6 +4970,28 @@ static int iax_canmatch(struct ast_channel *chan, char *context, char *exten, in return res; } +static int iax_matchmore(struct ast_channel *chan, char *context, char *exten, int priority, char *callerid, char *data) +{ + int res = 0; + struct iax_dpcache *dp; +#if 0 + ast_log(LOG_NOTICE, "iax_matchmore: con: %s, exten: %s, pri: %d, cid: %s, data: %s\n", context, exten, priority, callerid ? callerid : "<unknown>", data); +#endif + if (priority != 1) + return 0; + ast_pthread_mutex_lock(&dpcache_lock); + dp = find_cache(chan, data, context, exten, priority); + if (dp) { + if (dp->flags & CACHE_FLAG_MATCHMORE) + res= 1; + } + ast_pthread_mutex_unlock(&dpcache_lock); + if (!dp) { + ast_log(LOG_WARNING, "Unable to make DP cache\n"); + } + return res; +} + static int iax_exec(struct ast_channel *chan, char *context, char *exten, int priority, char *callerid, int newstack, char *data) { char odata[256]; @@ -4668,13 +5042,16 @@ static struct ast_switch iax_switch = exists: iax_exists, canmatch: iax_canmatch, exec: iax_exec, + matchmore: iax_matchmore, }; int load_module(void) { char *config = "iax.conf"; int res = 0; + int x; struct iax_registry *reg; + struct iax_peer *peer; struct sockaddr_in sin; @@ -4684,6 +5061,9 @@ int load_module(void) sin.sin_family = AF_INET; sin.sin_port = ntohs(AST_DEFAULT_IAX_PORTNO); sin.sin_addr.s_addr = INADDR_ANY; + + for (x=0;x<AST_IAX_MAX_CALLS;x++) + ast_pthread_mutex_init(&iaxsl[x]); io = io_context_create(); sched = sched_context_create(); @@ -4706,6 +5086,8 @@ int load_module(void) ast_cli_register(&cli_show_stats); ast_cli_register(&cli_show_cache); + ast_manager_register( "IAXpeers", 0, manager_iax_show_peers, "List IAX Peers" ); + set_config(config,&sin); if (ast_channel_register(type, tdesc, iax_capability, iax_request)) { @@ -4745,6 +5127,10 @@ int load_module(void) } for (reg = registrations; reg; reg = reg->next) iax_do_register(reg); + ast_pthread_mutex_lock(&peerl.lock); + for (peer = peerl.peers; peer; peer = peer->next) + iax_poke_peer(peer); + ast_pthread_mutex_unlock(&peerl.lock); return res; } @@ -4763,6 +5149,7 @@ int unload_module() for (x=0;x<AST_IAX_MAX_CALLS;x++) if (iaxs[x]) iax_destroy(x); + ast_manager_unregister( "IAXpeers" ); ast_cli_unregister(&cli_show_users); ast_cli_unregister(&cli_show_channels); ast_cli_unregister(&cli_show_peers); diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index 992aa07b15fd35ac3d30951117471fd54afc5cc4..ed0ed6f931196df1e6ece5d4172cf6fe088077bd 100755 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -5,6 +5,8 @@ port = 5060 ; Port to bind to bindaddr = 0.0.0.0 ; Address to bind to context = default ; Default for incoming calls +;tos=lowdelay +;tos=184 ;[snomsip] ;type=friend @@ -25,6 +27,8 @@ context = default ; Default for incoming calls ;username=cisco ;secret=blah ;host=dynamic +;canreinvite=no ; Cisco poops on reinvite sometimes +;qualify=200 ; Qualify peer is no more than 200ms away ;defaultip=192.168.0.4 ;[cisco1] diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h index 791e46e9f8cf458437ec700a0785a6f4abada456..8b21767f37e1f5f22df7f6be6bd9028ac563eeb6 100755 --- a/include/asterisk/rtp.h +++ b/include/asterisk/rtp.h @@ -32,6 +32,8 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io); void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them); +void ast_rtp_get_peer(struct ast_rtp *rpt, struct sockaddr_in *them); + void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us); void ast_rtp_destroy(struct ast_rtp *rtp); @@ -42,6 +44,10 @@ void ast_rtp_set_data(struct ast_rtp *rtp, void *data); int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *f); +int ast_rtp_senddigit(struct ast_rtp *rtp, char digit); + +int ast_rtp_settos(struct ast_rtp *rtp, int tos); + int ast2rtp(int id); int rtp2ast(int id);