📄 chan_iax2.c
字号:
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 = (struct ast_iax2_firmware_header*)mmap(NULL, stbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (fwh == (void *) -1) { 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((void*)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((void*)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((void*)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) { 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(int unload){ 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 */ if (!unload) { snprintf(dir, sizeof(dir), "%s/firmware/iax", (char *)ast_config_AST_DATA_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);}/*! * \note This function assumes that iaxsl[callno] is locked when called. * * \note IMPORTANT NOTE!!! Any time this function is used, even if iaxs[callno] * was valid before calling it, it may no longer be valid after calling it. * This function calls iax2_queue_frame(), which may unlock and lock the mutex * associated with this callno, meaning that another thread may grab it and destroy the call. */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. */ struct iax_frame *fr = data; fr->retrans = -1; ast_clear_flag(&fr->af, AST_FRFLAG_HAS_TIMING_INFO); if (iaxs[fr->callno] && !ast_test_flag(iaxs[fr->callno], IAX_ALREADYGONE)) iax2_queue_frame(fr->callno, &fr->af); /* Free our iax frame */ iax2_frame_free(fr); /* And don't run again */ return 0;}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(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;}static int send_packet(struct iax_frame *f){ int res; 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 */ 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(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) { if (option_debug && iaxdebug) ast_log(LOG_DEBUG, "Received error: %s\n", strerror(errno)); handle_error(); } else res = 0; return res;}/*! * \note Since this function calls iax2_queue_hangup(), the pvt struct * for the given call number may disappear during its execution. */static int iax2_predestroy(int callno){ struct ast_channel *c; struct chan_iax2_pvt *pvt = iaxs[callno]; if (!pvt) return -1; if (!ast_test_flag(pvt, IAX_ALREADYGONE)) { iax2_destroy_helper(pvt); ast_set_flag(pvt, IAX_ALREADYGONE); } c = pvt->owner; if (c) { c->tech_pvt = NULL; iax2_queue_hangup(callno); pvt->owner = NULL; ast_module_unref(ast_module_info->self); } return 0;}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; return 0;}static int attempt_transmit(const void *data);static void __attempt_transmit(const void *data){ /* Attempt to transmit the frame to the remote peer... Called without iaxsl held. */ struct iax_frame *f = (struct iax_frame *)data; int freeme=0; int callno = f->callno; /* Make sure this call is still active */ if (callno) ast_mutex_lock(&iaxsl[callno]); 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 */ send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_TXREJ, 0, NULL, 0, -1); } else if (f->final) { if (f->final) iax2_destroy(callno); } else { 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(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; iax2_queue_frame(callno, &fr); // XXX /* Remember, owner could disappear */ if (iaxs[callno] && iaxs[callno]->owner) iaxs[callno]->owner->hangupcause = AST_CAUSE_DESTINATION_OUT_OF_ORDER; } else { 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; } iax2_destroy(callno); } } } 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 = iax2_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_LIST_LOCK(&iaxq.queue); AST_LIST_REMOVE(&iaxq.queue, f, list); iaxq.count--; AST_LIST_UNLOCK(&iaxq.queue); f->retrans = -1; /* Free the IAX frame */ iax2_frame_free(f); }}static int attempt_transmit(const void *data){#ifdef SCHED_MULTITHREADED if (schedule_action(__attempt_transmit, data))#endif __attempt_transmit(data); return 0;}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 = fin
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -