Newer
Older
static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, const char *host)
if (!(tmp = ast_calloc(1, sizeof(*tmp))))
return NULL;
tmp->prefs = prefs;
tmp->callno = 0;
tmp->peercallno = 0;
tmp->transfercallno = 0;
tmp->bridgecallno = 0;
tmp->pingid = -1;
tmp->lagid = -1;
tmp->autoid = -1;
tmp->authid = -1;
tmp->initid = -1;
/* ast_copy_string(tmp->context, context, sizeof(tmp->context)); */
ast_copy_string(tmp->exten, "s", sizeof(tmp->exten));
ast_copy_string(tmp->host, host, sizeof(tmp->host));
Mark Spencer
committed
#ifdef NEWJB
{
jb_conf jbconf;
Mark Spencer
committed
tmp->jb = jb_new();
tmp->jbid = -1;
jbconf.max_jitterbuf = maxjitterbuffer;
jbconf.resync_threshold = resyncthreshold;
jbconf.max_contig_interp = maxjitterinterps;
jb_setconf(tmp->jb,&jbconf);
static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
{
/* Malloc() a copy of a frame */
struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen);
memcpy(new, fr, sizeof(struct iax_frame));
iax_frame_wrap(new, &fr->af);
new->data = NULL;
new->datalen = 0;
new->direction = DIRECTION_INGRESS;
new->retrans = -1;
}
return new;
}
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
#define NEW_ALLOW 1
#define NEW_FORCE 2
static int match(struct sockaddr_in *sin, unsigned short callno, unsigned short dcallno, struct chan_iax2_pvt *cur)
{
if ((cur->addr.sin_addr.s_addr == sin->sin_addr.s_addr) &&
(cur->addr.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 */
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;
}
static void update_max_trunk(void)
{
int max = TRUNK_CALL_START;
int x;
/* XXX Prolly don't need locks here XXX */
for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
if (iaxs[x])
max = x + 1;
}
maxtrunkcall = max;
Kevin P. Fleming
committed
if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "New max trunk callno is %d\n", max);
}
static void update_max_nontrunk(void)
{
int max = 1;
int x;
/* XXX Prolly don't need locks here XXX */
for (x=1;x<TRUNK_CALL_START - 1; x++) {
if (iaxs[x])
max = x + 1;
}
maxnontrunkcall = max;
Kevin P. Fleming
committed
if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "New max nontrunk callno is %d\n", max);
}
static int make_trunk(unsigned short callno, int locked)
{
int x;
int res= 0;
struct timeval now;
if (iaxs[callno]->oseqno) {
ast_log(LOG_WARNING, "Can't make trunk once a call has started!\n");
return -1;
}
if (callno & TRUNK_CALL_START) {
ast_log(LOG_WARNING, "Call %d is already a trunk\n", callno);
return -1;
}
gettimeofday(&now, NULL);
for (x=TRUNK_CALL_START;x<IAX_MAX_CALLS - 1; x++) {
ast_mutex_lock(&iaxsl[x]);
if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) {
iaxs[x] = iaxs[callno];
iaxs[x]->callno = x;
iaxs[callno] = NULL;
/* Update the two timers that should have been started */
if (iaxs[x]->pingid > -1)
ast_sched_del(sched, iaxs[x]->pingid);
if (iaxs[x]->lagid > -1)
ast_sched_del(sched, iaxs[x]->lagid);
iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
ast_mutex_unlock(&iaxsl[callno]);
ast_mutex_unlock(&iaxsl[x]);
ast_mutex_unlock(&iaxsl[x]);
ast_log(LOG_WARNING, "Unable to trunk call: Insufficient space\n");
return -1;
}
ast_log(LOG_DEBUG, "Made call %d into trunk call %d\n", callno, x);
/* We move this call from a non-trunked to a trunked call */
update_max_trunk();
update_max_nontrunk();
return res;
}
static int find_callno(unsigned short callno, unsigned short dcallno, struct sockaddr_in *sin, int new, int lockpeer, int sockfd)
char iabuf[INET_ADDRSTRLEN];
char host[80];
if (new <= NEW_ALLOW) {
/* Look for an existing connection first */
for (x=1;(res < 1) && (x<maxnontrunkcall);x++) {
ast_mutex_lock(&iaxsl[x]);
if (iaxs[x]) {
/* Look for an exact match */
if (match(sin, callno, dcallno, iaxs[x])) {
res = x;
}
}
ast_mutex_unlock(&iaxsl[x]);
}
for (x=TRUNK_CALL_START;(res < 1) && (x<maxtrunkcall);x++) {
ast_mutex_lock(&iaxsl[x]);
if (iaxs[x]) {
/* Look for an exact match */
if (match(sin, callno, dcallno, iaxs[x])) {
res = x;
}
}
ast_mutex_unlock(&iaxsl[x]);
if ((res < 1) && (new >= NEW_ALLOW)) {
if (!iax2_getpeername(*sin, host, sizeof(host), lockpeer))
snprintf(host, sizeof(host), "%s:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), ntohs(sin->sin_port));
gettimeofday(&now, NULL);
for (x=1;x<TRUNK_CALL_START;x++) {
/* Find first unused call number that hasn't been used in a while */
ast_mutex_lock(&iaxsl[x]);
if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) break;
ast_mutex_unlock(&iaxsl[x]);
}
/* We've still got lock held if we found a spot */
if (x >= TRUNK_CALL_START) {
ast_log(LOG_WARNING, "No more space\n");
iaxs[x] = new_iax(sin, lockpeer, host);
Kevin P. Fleming
committed
if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "Creating new call structure %d\n", x);
iaxs[x]->sockfd = sockfd;
iaxs[x]->addr.sin_port = sin->sin_port;
iaxs[x]->addr.sin_family = sin->sin_family;
iaxs[x]->addr.sin_addr.s_addr = sin->sin_addr.s_addr;
iaxs[x]->peercallno = callno;
iaxs[x]->callno = x;
iaxs[x]->pingtime = DEFAULT_RETRY_TIME;
Kevin P. Fleming
committed
iaxs[x]->expiry = min_reg_expire;
iaxs[x]->pingid = ast_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
iaxs[x]->lagid = ast_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
Mark Spencer
committed
ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
Kevin P. Fleming
committed
ast_copy_string(iaxs[x]->accountcode, accountcode, sizeof(iaxs[x]->accountcode));
} else {
ast_log(LOG_WARNING, "Out of resources\n");
ast_mutex_unlock(&iaxsl[x]);
ast_mutex_unlock(&iaxsl[x]);
res = x;
}
return res;
}
static void iax2_frame_free(struct iax_frame *fr)
{
if (fr->retrans > -1)
ast_sched_del(sched, fr->retrans);
iax_frame_free(fr);
}
static int iax2_queue_frame(int callno, struct ast_frame *f)
{
/* Assumes lock for callno is already held... */
for (;;) {
if (iaxs[callno] && iaxs[callno]->owner) {
if (ast_mutex_trylock(&iaxs[callno]->owner->lock)) {
/* Avoid deadlock by pausing and trying again */
ast_mutex_unlock(&iaxsl[callno]);
ast_mutex_lock(&iaxsl[callno]);
Mark Spencer
committed
ast_queue_frame(iaxs[callno]->owner, f);
ast_mutex_unlock(&iaxs[callno]->owner->lock);
break;
}
} else
break;
}
return 0;
}
static void destroy_firmware(struct iax_firmware *cur)
{
/* Close firmware */
if (cur->fwh) {
munmap(cur->fwh, ntohl(cur->fwh->datalen) + sizeof(*(cur->fwh)));
}
close(cur->fd);
free(cur);
}
static int try_firmware(char *s)
{
struct stat stbuf;
struct iax_firmware *cur;
Mark Spencer
committed
int ifd;
Mark Spencer
committed
struct ast_iax2_firmware_header *fwh, fwh2;
struct MD5Context md5;
unsigned char sum[16];
Mark Spencer
committed
unsigned char buf[1024];
int len, chunk;
char *s2;
char *last;
s2 = alloca(strlen(s) + 100);
if (!s2) {
ast_log(LOG_WARNING, "Alloca failed!\n");
return -1;
}
last = strrchr(s, '/');
if (last)
last++;
else
last = s;
Tilghman Lesher
committed
snprintf(s2, strlen(s) + 100, "/var/tmp/%s-%ld", last, (unsigned long)ast_random());
res = stat(s, &stbuf);
if (res < 0) {
ast_log(LOG_WARNING, "Failed to stat '%s': %s\n", s, strerror(errno));
return -1;
}
/* Make sure it's not a directory */
if (S_ISDIR(stbuf.st_mode))
return -1;
Mark Spencer
committed
ifd = open(s, O_RDONLY);
if (ifd < 0) {
ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
return -1;
}
Mark Spencer
committed
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
fd = open(s2, O_RDWR | O_CREAT | O_EXCL);
if (fd < 0) {
ast_log(LOG_WARNING, "Cannot open '%s' for writing: %s\n", s2, strerror(errno));
close(ifd);
return -1;
}
/* Unlink our newly created file */
unlink(s2);
/* Now copy the firmware into it */
len = stbuf.st_size;
while(len) {
chunk = len;
if (chunk > sizeof(buf))
chunk = sizeof(buf);
res = read(ifd, buf, chunk);
if (res != chunk) {
ast_log(LOG_WARNING, "Only read %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
close(ifd);
close(fd);
return -1;
}
res = write(fd, buf, chunk);
if (res != chunk) {
ast_log(LOG_WARNING, "Only write %d of %d bytes of data :(: %s\n", res, chunk, strerror(errno));
close(ifd);
close(fd);
return -1;
}
len -= chunk;
}
close(ifd);
/* Return to the beginning */
lseek(fd, 0, SEEK_SET);
if ((res = read(fd, &fwh2, sizeof(fwh2))) != sizeof(fwh2)) {
ast_log(LOG_WARNING, "Unable to read firmware header in '%s'\n", s);
close(fd);
return -1;
}
if (ntohl(fwh2.magic) != IAX_FIRMWARE_MAGIC) {
ast_log(LOG_WARNING, "'%s' is not a valid firmware file\n", s);
close(fd);
return -1;
}
if (ntohl(fwh2.datalen) != (stbuf.st_size - sizeof(fwh2))) {
ast_log(LOG_WARNING, "Invalid data length in firmware '%s'\n", s);
close(fd);
return -1;
}
Kevin P. Fleming
committed
if (fwh2.devname[sizeof(fwh2.devname) - 1] || ast_strlen_zero((char *)fwh2.devname)) {
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
ast_log(LOG_WARNING, "No or invalid device type specified for '%s'\n", s);
close(fd);
return -1;
}
fwh = mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (!fwh) {
ast_log(LOG_WARNING, "mmap failed: %s\n", strerror(errno));
close(fd);
return -1;
}
MD5Init(&md5);
MD5Update(&md5, fwh->data, ntohl(fwh->datalen));
MD5Final(sum, &md5);
if (memcmp(sum, fwh->chksum, sizeof(sum))) {
ast_log(LOG_WARNING, "Firmware file '%s' fails checksum\n", s);
munmap(fwh, stbuf.st_size);
close(fd);
return -1;
}
cur = waresl.wares;
while(cur) {
Kevin P. Fleming
committed
if (!strcmp((char *)cur->fwh->devname, (char *)fwh->devname)) {
/* Found a candidate */
if (cur->dead || (ntohs(cur->fwh->version) < ntohs(fwh->version)))
/* The version we have on loaded is older, load this one instead */
break;
/* This version is no newer than what we have. Don't worry about it.
We'll consider it a proper load anyhow though */
munmap(fwh, stbuf.st_size);
close(fd);
return 0;
}
cur = cur->next;
}
if (!cur) {
/* Allocate a new one and link it */
if ((cur = ast_calloc(1, sizeof(*cur)))) {
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
cur->fd = -1;
cur->next = waresl.wares;
waresl.wares = cur;
}
}
if (cur) {
if (cur->fwh) {
munmap(cur->fwh, cur->mmaplen);
}
if (cur->fd > -1)
close(cur->fd);
cur->fwh = fwh;
cur->fd = fd;
cur->mmaplen = stbuf.st_size;
cur->dead = 0;
}
return 0;
}
static int iax_check_version(char *dev)
{
int res = 0;
struct iax_firmware *cur;
if (!ast_strlen_zero(dev)) {
ast_mutex_lock(&waresl.lock);
cur = waresl.wares;
while(cur) {
Kevin P. Fleming
committed
if (!strcmp(dev, (char *)cur->fwh->devname)) {
res = ntohs(cur->fwh->version);
break;
}
cur = cur->next;
}
ast_mutex_unlock(&waresl.lock);
}
return res;
}
static int iax_firmware_append(struct iax_ie_data *ied, const unsigned char *dev, unsigned int desc)
{
int res = -1;
unsigned int bs = desc & 0xff;
unsigned int start = (desc >> 8) & 0xffffff;
unsigned int bytes;
struct iax_firmware *cur;
if (!ast_strlen_zero((char *)dev) && bs) {
start *= bs;
ast_mutex_lock(&waresl.lock);
cur = waresl.wares;
while(cur) {
Kevin P. Fleming
committed
if (!strcmp((char *)dev, (char *)cur->fwh->devname)) {
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
iax_ie_append_int(ied, IAX_IE_FWBLOCKDESC, desc);
if (start < ntohl(cur->fwh->datalen)) {
bytes = ntohl(cur->fwh->datalen) - start;
if (bytes > bs)
bytes = bs;
iax_ie_append_raw(ied, IAX_IE_FWBLOCKDATA, cur->fwh->data + start, bytes);
} else {
bytes = 0;
iax_ie_append(ied, IAX_IE_FWBLOCKDATA);
}
if (bytes == bs)
res = 0;
else
res = 1;
break;
}
cur = cur->next;
}
ast_mutex_unlock(&waresl.lock);
}
return res;
}
static void reload_firmware(void)
{
struct iax_firmware *cur, *curl, *curp;
DIR *fwd;
struct dirent *de;
char dir[256];
char fn[256];
/* Mark all as dead */
ast_mutex_lock(&waresl.lock);
cur = waresl.wares;
while(cur) {
cur->dead = 1;
cur = cur->next;
}
/* Now that we've freed them, load the new ones */
snprintf(dir, sizeof(dir), "%s/firmware/iax", (char *)ast_config_AST_VAR_DIR);
fwd = opendir(dir);
if (fwd) {
while((de = readdir(fwd))) {
if (de->d_name[0] != '.') {
snprintf(fn, sizeof(fn), "%s/%s", dir, de->d_name);
if (!try_firmware(fn)) {
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Loaded firmware '%s'\n", de->d_name);
}
closedir(fwd);
} else
ast_log(LOG_WARNING, "Error opening firmware directory '%s': %s\n", dir, strerror(errno));
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
/* Clean up leftovers */
cur = waresl.wares;
curp = NULL;
while(cur) {
curl = cur;
cur = cur->next;
if (curl->dead) {
if (curp) {
curp->next = cur;
} else {
waresl.wares = cur;
}
destroy_firmware(curl);
} else {
curp = cur;
}
}
ast_mutex_unlock(&waresl.lock);
}
static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned int ts, int seqno, int now, int transfer, int final);
static int __do_deliver(void *data)
{
/* Just deliver the packet by using queueing. This is called by
the IAX thread with the iaxsl lock held. */
if (iaxs[fr->callno] && !ast_test_flag(iaxs[fr->callno], IAX_ALREADYGONE))
iax2_queue_frame(fr->callno, &fr->af);
/* And don't run again */
return 0;
}
{
/* Locking version of __do_deliver */
int callno = fr->callno;
int res;
ast_mutex_lock(&iaxsl[callno]);
ast_mutex_unlock(&iaxsl[callno]);
static int do_deliver(void *data)
{
#ifdef SCHED_MULTITHREADED
if (schedule_action(__do_deliver, data))
#endif
__real_do_deliver(data);
return 0;
}
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
static int handle_error(void)
{
/* XXX Ideally we should figure out why an error occured and then abort those
rather than continuing to try. Unfortunately, the published interface does
not seem to work XXX */
#if 0
struct sockaddr_in *sin;
int res;
struct msghdr m;
struct sock_extended_err e;
m.msg_name = NULL;
m.msg_namelen = 0;
m.msg_iov = NULL;
m.msg_control = &e;
m.msg_controllen = sizeof(e);
m.msg_flags = 0;
res = recvmsg(netsocket, &m, MSG_ERRQUEUE);
if (res < 0)
ast_log(LOG_WARNING, "Error detected, but unable to read error: %s\n", strerror(errno));
else {
if (m.msg_controllen) {
sin = (struct sockaddr_in *)SO_EE_OFFENDER(&e);
if (sin)
Mark Spencer
committed
ast_log(LOG_WARNING, "Receive error from %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr));
else
ast_log(LOG_WARNING, "No address detected??\n");
} else {
ast_log(LOG_WARNING, "Local error: %s\n", strerror(e.ee_errno));
}
}
#endif
return 0;
}
static int transmit_trunk(struct iax_frame *f, struct sockaddr_in *sin, int sockfd)
{
int res;
res = sendto(sockfd, f->data, f->datalen, 0,(struct sockaddr *)sin,
sizeof(*sin));
if (res < 0) {
if (option_debug)
ast_log(LOG_DEBUG, "Received error: %s\n", strerror(errno));
handle_error();
} else
res = 0;
return res;
}
char iabuf[INET_ADDRSTRLEN];
/* Don't send if there was an error, but return error instead */
if (!callno || !iaxs[callno] || iaxs[callno]->error)
return -1;
Kevin P. Fleming
committed
if (option_debug > 2 && iaxdebug)
ast_log(LOG_DEBUG, "Sending %d on %d/%d to %s:%d\n", f->ts, callno, iaxs[callno]->peercallno, ast_inet_ntoa(iabuf, sizeof(iabuf), iaxs[callno]->addr.sin_addr), ntohs(iaxs[callno]->addr.sin_port));
iax_showframe(f, NULL, 0, &iaxs[callno]->transfer, f->datalen - sizeof(struct ast_iax2_full_hdr));
res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->transfer,
sizeof(iaxs[callno]->transfer));
iax_showframe(f, NULL, 0, &iaxs[callno]->addr, f->datalen - sizeof(struct ast_iax2_full_hdr));
res = sendto(iaxs[callno]->sockfd, f->data, f->datalen, 0,(struct sockaddr *)&iaxs[callno]->addr,
sizeof(iaxs[callno]->addr));
Kevin P. Fleming
committed
if (option_debug && iaxdebug)
ast_log(LOG_DEBUG, "Received error: %s\n", strerror(errno));
handle_error();
} else
res = 0;
return res;
}
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
static void iax2_destroy_helper(struct chan_iax2_pvt *pvt)
{
/* No more pings or lagrq's */
if (pvt->pingid > -1)
ast_sched_del(sched, pvt->pingid);
pvt->pingid = -1;
if (pvt->lagid > -1)
ast_sched_del(sched, pvt->lagid);
pvt->lagid = -1;
if (pvt->autoid > -1)
ast_sched_del(sched, pvt->autoid);
pvt->autoid = -1;
if (pvt->authid > -1)
ast_sched_del(sched, pvt->authid);
pvt->authid = -1;
if (pvt->initid > -1)
ast_sched_del(sched, pvt->initid);
pvt->initid = -1;
#ifdef NEWJB
if (pvt->jbid > -1)
ast_sched_del(sched, pvt->jbid);
pvt->jbid = -1;
#endif
}
static int iax2_predestroy(int callno)
{
struct ast_channel *c;
struct chan_iax2_pvt *pvt;
ast_mutex_lock(&iaxsl[callno]);
ast_mutex_unlock(&iaxsl[callno]);
if (!ast_test_flag(pvt, IAX_ALREADYGONE)) {
iax2_destroy_helper(pvt);
ast_set_flag(pvt, IAX_ALREADYGONE);
}
c = pvt->owner;
if (c) {
c->_softhangup |= AST_SOFTHANGUP_DEV;
c->tech_pvt = NULL;
Mark Spencer
committed
ast_queue_hangup(c);
ast_mutex_lock(&usecnt_lock);
usecnt--;
if (usecnt < 0)
ast_log(LOG_WARNING, "Usecnt < 0???\n");
ast_mutex_unlock(&usecnt_lock);
ast_mutex_unlock(&iaxsl[callno]);
return 0;
}
static int iax2_predestroy_nolock(int callno)
{
int res;
ast_mutex_unlock(&iaxsl[callno]);
ast_mutex_lock(&iaxsl[callno]);
return res;
}
static void iax2_destroy(int callno)
{
struct chan_iax2_pvt *pvt;
struct ast_channel *owner;
retry:
ast_mutex_lock(&iaxsl[callno]);
gettimeofday(&lastused[callno], NULL);
if (ast_mutex_trylock(&owner->lock)) {
ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n");
ast_mutex_unlock(&iaxsl[callno]);
usleep(1);
goto retry;
}
}
if (!owner)
iaxs[callno] = NULL;
if (!owner)
pvt->owner = NULL;
iax2_destroy_helper(pvt);
ast_set_flag(pvt, IAX_ALREADYGONE);
if (owner) {
/* If there's an owner, prod it to give up */
owner->_softhangup |= AST_SOFTHANGUP_DEV;
Mark Spencer
committed
ast_queue_hangup(owner);
}
for (cur = iaxq.head; cur ; cur = cur->next) {
/* Cancel any pending transmissions */
if (cur->callno == pvt->callno)
cur->retries = -1;
}
Mark Spencer
committed
#ifdef NEWJB
{
jb_frame frame;
while(jb_getall(pvt->jb,&frame) == JB_OK)
iax2_frame_free(frame.data);
jb_destroy(pvt->jb);
}
#endif
free(pvt);
ast_mutex_unlock(&owner->lock);
ast_mutex_unlock(&iaxsl[callno]);
if (callno & 0x4000)
update_max_trunk();
}
static void iax2_destroy_nolock(int callno)
{
/* Actually it's easier to unlock, kill it, and relock */
ast_mutex_unlock(&iaxsl[callno]);
ast_mutex_lock(&iaxsl[callno]);
{
/* Called with iaxsl lock held, and iaxs[callno] non-NULL */
struct ast_iax2_full_hdr *fh = f->data;
/* Mark this as a retransmission */
fh->dcallno = ntohs(IAX_FLAG_RETRANS | f->dcallno);
/* Update iseqno */
f->iseqno = iaxs[f->callno]->iseqno;
static int attempt_transmit(void *data);
static void __attempt_transmit(void *data)
{
/* Attempt to transmit the frame to the remote peer...
Called without iaxsl held. */
int freeme=0;
int callno = f->callno;
char iabuf[INET_ADDRSTRLEN];
/* Make sure this call is still active */
if (callno)
ast_mutex_lock(&iaxsl[callno]);
if ((f->retries < 0) /* Already ACK'd */ ||
(f->retries >= max_retries) /* Too many attempts */) {
/* Record an error if we've transmitted too many times */
if (f->retries >= max_retries) {
if (f->transfer) {
/* Transfer timeout */
send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
} else if (f->final) {
if (f->final)
Mark Spencer
committed
ast_log(LOG_WARNING, "Max retries exceeded to host %s on %s (type = %d, subclass = %d, ts=%d, seqno=%d)\n", ast_inet_ntoa(iabuf, sizeof(iabuf), iaxs[f->callno]->addr.sin_addr),iaxs[f->callno]->owner->name , f->af.frametype, f->af.subclass, f->ts, f->oseqno);
iaxs[callno]->error = ETIMEDOUT;
if (iaxs[callno]->owner) {
struct ast_frame fr = { 0, };
/* Hangup the fd */
fr.frametype = AST_FRAME_CONTROL;
fr.subclass = AST_CONTROL_HANGUP;
/* Remember, owner could disappear */
if (iaxs[callno]->owner)
iaxs[callno]->owner->hangupcause = AST_CAUSE_DESTINATION_OUT_OF_ORDER;
if (iaxs[callno]->reg) {
memset(&iaxs[callno]->reg->us, 0, sizeof(iaxs[callno]->reg->us));
iaxs[callno]->reg->regstate = REG_STATE_TIMEOUT;
iaxs[callno]->reg->refresh = IAX_DEFAULT_REG_EXPIRE;
}
}
}
freeme++;
} else {
/* Update it if it needs it */
update_packet(f);
/* Attempt transmission */
send_packet(f);
f->retries++;
/* Try again later after 10 times as long */
f->retrytime *= 10;
if (f->retrytime > MAX_RETRY_TIME)
f->retrytime = MAX_RETRY_TIME;
/* Transfer messages max out at one second */
if (f->transfer && (f->retrytime > 1000))
f->retrytime = 1000;
f->retrans = ast_sched_add(sched, f->retrytime, attempt_transmit, f);
}
} else {
/* Make sure it gets freed */
f->retries = -1;
freeme++;
}
if (callno)
ast_mutex_unlock(&iaxsl[callno]);
/* Do not try again */
if (freeme) {
/* Don't attempt delivery, just remove it from the queue */
ast_mutex_lock(&iaxq.lock);
if (f->prev)
f->prev->next = f->next;
else
iaxq.head = f->next;
if (f->next)
f->next->prev = f->prev;
else
iaxq.tail = f->prev;
iaxq.count--;
ast_mutex_unlock(&iaxq.lock);
}
static int attempt_transmit(void *data)
{
#ifdef SCHED_MULTITHREADED
if (schedule_action(__attempt_transmit, data))
#endif
__attempt_transmit(data);
return 0;
}
static int iax2_set_jitter(int fd, int argc, char *argv[])
{
Mark Spencer
committed
#ifdef NEWJB
ast_cli(fd, "sorry, this command is deprecated\n");
return RESULT_SUCCESS;
#else
if ((argc != 4) && (argc != 5))
return RESULT_SHOWUSAGE;
if (argc == 4) {
max_jitter_buffer = atoi(argv[3]);
if (max_jitter_buffer < 0)
max_jitter_buffer = 0;
} else {
if (argc == 5) {
int callno = atoi(argv[3]);
if ((callno >= 0) && (callno < IAX_MAX_CALLS)) {
if (iaxs[callno]) {
iaxs[callno]->jitterbuffer = atoi(argv[4]);
if (iaxs[callno]->jitterbuffer < 0)
iaxs[callno]->jitterbuffer = 0;
ast_cli(fd, "%d is not a valid call number\n", callno);
Mark Spencer
committed
#endif
}
static char jitter_usage[] =
"Usage: iax set jitter [callid] <value>\n"
" If used with a callid, it sets the jitter buffer to the given static\n"
"value (until its next calculation). If used without a callid, the value is used\n"
"to establish the maximum excess jitter buffer that is permitted before the jitter\n"
"buffer size is reduced.";
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
static int iax2_prune_realtime(int fd, int argc, char *argv[])
{
struct iax2_peer *peer;
if (argc != 4)
return RESULT_SHOWUSAGE;
if (!strcmp(argv[3],"all")) {
reload_config();
ast_cli(fd, "OK cache is flushed.\n");
} else if ((peer = find_peer(argv[3], 0))) {
if(ast_test_flag(peer, IAX_RTCACHEFRIENDS)) {
ast_set_flag(peer, IAX_RTAUTOCLEAR);
expire_registry(peer);
ast_cli(fd, "OK peer %s was removed from the cache.\n", argv[3]);
} else {
ast_cli(fd, "SORRY peer %s is not eligible for this operation.\n", argv[3]);
}
} else {
ast_cli(fd, "SORRY peer %s was not found in the cache.\n", argv[3]);
}
return RESULT_SUCCESS;
}
Mark Spencer
committed
static int iax2_test_losspct(int fd, int argc, char *argv[])
{
if (argc != 4)
return RESULT_SHOWUSAGE;
test_losspct = atoi(argv[3]);
return RESULT_SUCCESS;
}
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
#ifdef IAXTESTS
static int iax2_test_late(int fd, int argc, char *argv[])
{
if (argc != 4)
return RESULT_SHOWUSAGE;
test_late = atoi(argv[3]);
return RESULT_SUCCESS;
}
static int iax2_test_resync(int fd, int argc, char *argv[])
{
if (argc != 4)
return RESULT_SHOWUSAGE;
test_resync = atoi(argv[3]);
return RESULT_SUCCESS;
}
static int iax2_test_jitter(int fd, int argc, char *argv[])
{
if (argc < 4 || argc > 5)
return RESULT_SHOWUSAGE;
test_jit = atoi(argv[3]);
if (argc == 5)
test_jitpct = atoi(argv[4]);
return RESULT_SUCCESS;
}
#endif /* IAXTESTS */
/*! \brief peer_status: Report Peer status in character string */