Skip to content
Snippets Groups Projects
chan_iax2.c 321 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				destroy_peer(peer);
    
    Russell Bryant's avatar
    Russell Bryant committed
    			res = 1;
    
    Russell Bryant's avatar
    Russell Bryant committed
    
    
    static struct chan_iax2_pvt *new_iax(struct sockaddr_in *sin, int lockpeer, const char *host)
    
    {
    	struct chan_iax2_pvt *tmp;
    
    
    	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));
    
    		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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct iax_frame *iaxfrdup2(struct iax_frame *fr)
    
    {
    	/* Malloc() a copy of a frame */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct iax_frame *new = iax_frame_new(DIRECTION_INGRESS, fr->af.datalen);
    
    	if (new) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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;
    }
    
    
    Russell Bryant's avatar
    Russell Bryant committed
    #define NEW_PREVENT 	0
    
    #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;
    
    		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;
    
    		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++) {
    
    		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);
    
    			if (locked)
    
    			res = x;
    			if (!locked)
    
    	if (x >= IAX_MAX_CALLS - 1) {
    
    		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)
    
    	int res = 0;
    
    	struct timeval now;
    
    	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++) {
    
    			if (iaxs[x]) {
    				/* Look for an exact match */
    				if (match(sin, callno, dcallno, iaxs[x])) {
    					res = x;
    				}
    			}
    
    		}
    		for (x=TRUNK_CALL_START;(res < 1) && (x<maxtrunkcall);x++) {
    
    			if (iaxs[x]) {
    				/* Look for an exact match */
    				if (match(sin, callno, dcallno, iaxs[x])) {
    					res = 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 */
    
    			if (!iaxs[x] && ((now.tv_sec - lastused[x].tv_sec) > MIN_REUSE_TIME)) break;
    
    		}
    		/* 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);
    
    		update_max_nontrunk();
    
    		if (iaxs[x]) {
    
    				ast_log(LOG_DEBUG, "Creating new call structure %d\n", x);
    
    			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;
    
    			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);
    
    			iaxs[x]->amaflags = amaflags;
    
    			ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);	
    
    			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]);
    
    			return 0;
    
    		ast_mutex_unlock(&iaxsl[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    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 */
    
    				usleep(1);
    
    			} else {
    
    				ast_queue_frame(iaxs[callno]->owner, f);
    
    				ast_mutex_unlock(&iaxs[callno]->owner->lock);
    
    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;
    
    	int fd;
    	int res;
    
    	struct ast_iax2_firmware_header *fwh, fwh2;
    	struct MD5Context md5;
    	unsigned char sum[16];
    
    	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;
    
    	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;
    
    		ast_log(LOG_WARNING, "Cannot open '%s': %s\n", s, strerror(errno));
    		return -1;
    	}
    
    	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;
    	}
    
    	if (fwh2.devname[sizeof(fwh2.devname) - 1] || ast_strlen_zero((char *)fwh2.devname)) {
    
    		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) {
    
    		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)))) {
    
    			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;
    
    		ast_mutex_lock(&waresl.lock);
    		cur = waresl.wares;
    		while(cur) {
    
    			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) {
    
    			if (!strcmp((char *)dev, (char *)cur->fwh->devname)) {
    
    				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));
    
    
    	/* 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. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct iax_frame *fr = data;
    
    	fr->retrans = -1;
    
    	if (iaxs[fr->callno] && !ast_test_flag(iaxs[fr->callno], IAX_ALREADYGONE))
    
    		iax2_queue_frame(fr->callno, &fr->af);
    
    	/* Free our iax frame */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	iax2_frame_free(fr);
    
    	/* And don't run again */
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int __real_do_deliver(void *data)
    
    {
    	/* Locking version of __do_deliver */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct iax_frame *fr = data;
    
    	int callno = fr->callno;
    	int res;
    
    	res = __do_deliver(data);
    
    	return res;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int do_deliver(void *data)
    {
    #ifdef SCHED_MULTITHREADED
    	if (schedule_action(__do_deliver, data))
    #endif		
    		__real_do_deliver(data);
    	return 0;
    }
    
    #endif /* NEWJB */
    
    
    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) 
    
    				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)
    
    	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;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int send_packet(struct iax_frame *f)
    
    	char iabuf[INET_ADDRSTRLEN];
    
    	int callno = f->callno;
    
    
    	/* Don't send if there was an error, but return error instead */
    	if (!callno || !iaxs[callno] || iaxs[callno]->error)
    	    return -1;
    
    	/* Called with iaxsl held */
    
    		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));
    
    	if (f->transfer) {
    		if (iaxdebug)
    
    			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));
    
    	} else {
    		if (iaxdebug)
    
    			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));
    
    	}
    	if (res < 0) {
    
    			ast_log(LOG_DEBUG, "Received error: %s\n", strerror(errno));
    		handle_error();
    	} else
    		res = 0;
    	return res;
    }
    
    
    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;
    
    	pvt = iaxs[callno];
    	if (!pvt) {
    
    	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;
    
    		pvt->owner = NULL;
    
    		usecnt--;
    		if (usecnt < 0) 
    			ast_log(LOG_WARNING, "Usecnt < 0???\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_update_use_count();
    
    	return 0;
    }
    
    static int iax2_predestroy_nolock(int callno)
    {
    	int res;
    
    	res = iax2_predestroy(callno);
    
    	return res;
    }
    
    static void iax2_destroy(int callno)
    {
    	struct chan_iax2_pvt *pvt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct iax_frame *cur;
    
    	struct ast_channel *owner;
    
    retry:
    
    	pvt = iaxs[callno];
    
    	gettimeofday(&lastused[callno], NULL);
    
    Russell Bryant's avatar
    Russell Bryant committed
    	
    	owner = pvt ? pvt->owner : NULL;
    
    		if (ast_mutex_trylock(&owner->lock)) {
    
    			ast_log(LOG_NOTICE, "Avoiding IAX destroy deadlock\n");
    
    			usleep(1);
    			goto retry;
    		}
    	}
    
    	if (pvt) {
    
    		iax2_destroy_helper(pvt);
    
    		if (pvt->bridgetrans)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_translator_free_path(pvt->bridgetrans);
    
    		pvt->bridgetrans = NULL;
    
    
    		/* Already gone */
    
    		ast_set_flag(pvt, IAX_ALREADYGONE);	
    
    
    		if (owner) {
    			/* If there's an owner, prod it to give up */
    			owner->_softhangup |= AST_SOFTHANGUP_DEV;
    
    		}
    
    		for (cur = iaxq.head; cur ; cur = cur->next) {
    			/* Cancel any pending transmissions */
    			if (cur->callno == pvt->callno) 
    				cur->retries = -1;
    		}
    
    Russell Bryant's avatar
    Russell Bryant committed
    		if (pvt->reg)
    
    			pvt->reg->callno = 0;
    
    		if (!owner) {
    			if (pvt->vars) {
    
    				ast_variables_destroy(pvt->vars);
    
    				pvt->vars = NULL;
    			}
    
    #ifdef NEWJB
     			{
                                jb_frame frame;
                                while(jb_getall(pvt->jb,&frame) == JB_OK)
    				iax2_frame_free(frame.data);
                                jb_destroy(pvt->jb);
                            }
    #endif
    
    	}
    	if (owner) {
    
    	if (callno & 0x4000)
    		update_max_trunk();
    
    }
    static void iax2_destroy_nolock(int callno)
    {	
    	/* Actually it's easier to unlock, kill it, and relock */
    
    	iax2_destroy(callno);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int update_packet(struct iax_frame *f)
    
    {
    	/* 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;
    
    	fh->iseqno = f->iseqno;
    
    Mark Spencer's avatar
    Mark Spencer committed
    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. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct iax_frame *f = data;
    
    	int freeme=0;
    	int callno = f->callno;
    
    	char iabuf[INET_ADDRSTRLEN];
    
    	/* Make sure this call is still active */
    	if (callno) 
    
    Russell Bryant's avatar
    Russell Bryant committed
    	if (callno && iaxs[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 */
    
    Russell Bryant's avatar
    Russell Bryant committed
    						send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1);
    
    					} else if (f->final) {
    						if (f->final) 
    
    Russell Bryant's avatar
    Russell Bryant committed
    							iax2_destroy_nolock(callno);
    
    					} else {
    
    Russell Bryant's avatar
    Russell Bryant committed
    						if (iaxs[callno]->owner)
    
    							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);
    
    Russell Bryant's avatar
    Russell Bryant committed
    						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;
    
    Russell Bryant's avatar
    Russell Bryant committed
    							iax2_queue_frame(callno, &fr);
    
    							/* Remember, owner could disappear */
    
    Russell Bryant's avatar
    Russell Bryant committed
    							if (iaxs[callno]->owner)
    								iaxs[callno]->owner->hangupcause = AST_CAUSE_DESTINATION_OUT_OF_ORDER;
    
    						} else {
    
    Russell Bryant's avatar
    Russell Bryant committed
    							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;
    
    Russell Bryant's avatar
    Russell Bryant committed
    							iax2_destroy_nolock(callno);
    
    			/* 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)
    
    	/* Do not try again */
    	if (freeme) {
    		/* Don't attempt delivery, just remove it from the queue */
    
    		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--;
    
    		f->retrans = -1;
    
    		/* Free the IAX frame */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		iax2_frame_free(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    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[])
    {
    
    #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) {
    
    Russell Bryant's avatar
    Russell Bryant committed
    			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;
    
    Russell Bryant's avatar
    Russell Bryant committed
    					ast_cli(fd, "No such call '%d'\n", callno);
    
    Russell Bryant's avatar
    Russell Bryant committed
    				ast_cli(fd, "%d is not a valid call number\n", callno);
    
    		}
    	}
    	return RESULT_SUCCESS;
    
    }
    
    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.";
    
    
    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;
    }
    
    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;
    }
    
    
    #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 */