/** * Sets the timeout time for a given group list member */ void set_timeout(struct pr_group_list_t *group, int pending_reset, int rescale) { int pending, i; if (group->phase == PR_PHASE_READY) { if (!rescale) { gettimeofday(&group->start_phase_timeout_time, NULL); } group->phase_timeout_time = group->start_phase_timeout_time; add_timeval_d(&group->phase_timeout_time, 2 * group->grtt); } glog5(group, "set timeout: pending_reset=%d", pending_reset); for (pending = 0, i = 0; (i < MAX_PEND) && !pending; i++) { if (group->pending[i].msg != 0) { glog5(group, "set timeout: found pending %s", func_name(group->pending[i].msg)); pending = group->pending[i].msg; } } if (pending) { if (pending_reset) { if (!rescale) { gettimeofday(&group->start_timeout_time, NULL); } group->timeout_time = group->start_timeout_time; add_timeval_d(&group->timeout_time, 1 * group->grtt); } } else { if (!rescale) { gettimeofday(&group->start_timeout_time, NULL); } group->timeout_time = group->start_timeout_time; if (group->robust * group->grtt < 1.0) { add_timeval_d(&group->timeout_time, 1.0); } else { add_timeval_d(&group->timeout_time, group->robust * group->grtt); } } }
/** * Sets the timeout time for a given group list member */ void set_timeout(struct group_list_t *group, int rescale) { if (!rescale) { gettimeofday(&group->start_timeout_time, NULL); } group->timeout_time = group->start_timeout_time; switch (group->phase) { case PHASE_REGISTERED: add_timeval_d(&group->timeout_time, 4 * group->grtt); break; case PHASE_RECEIVING: case PHASE_MIDGROUP: if (group->robust * group->grtt < 1.0) { add_timeval_d(&group->timeout_time, 1.0); } else { add_timeval_d(&group->timeout_time, group->robust * group->grtt); } break; case PHASE_COMPLETE: add_timeval_d(&group->timeout_time, 4 * group->grtt); break; } }
/** * Starts a new feedback round under TFMCC */ void init_tfmcc_fb_round(struct group_list_t *group, uint16_t new_ccseq) { double urand, backoff; group->ccseq = new_ccseq; urand = (double)(rand32() + 0.0) / 0xFFFFFFFF; if (urand == 0.0) urand = 1.0; backoff = 6 * group->grtt * (1 + log(urand) / log(group->gsize)); if (backoff < 0) backoff = 0.0; gettimeofday(&group->cc_time, NULL); add_timeval_d(&group->cc_time, (backoff > 0) ? backoff : 0); group->initrate = current_cc_rate(group); glog3(group, "Starting feedback round %d: backoff = %.3f, initrate = %d", new_ccseq, backoff, group->initrate); }
/** * Reads an expected FILESEG and writes it to the proper place in the file */ void handle_fileseg(struct group_list_t *group, const unsigned char *message, unsigned meslen, uint16_t txseq) { const struct fileseg_h *fileseg; const struct tfmcc_data_info_he *tfmcc; const unsigned char *data; const uint8_t *he; int datalen, section, cache_offset, status_idx; uint32_t seq, i; unsigned extlen; if (group->fileinfo.ftype != FTYPE_REG) { glog2(group, "Rejecting FILESEG: not a regular file"); return; } fileseg = (const struct fileseg_h *)message; data = message + (fileseg->hlen * 4); datalen = meslen - (fileseg->hlen * 4); if ((meslen < (fileseg->hlen * 4U)) || ((fileseg->hlen * 4U) < sizeof(struct fileseg_h))) { glog2(group, "Rejecting FILESEG: invalid message size"); return; } if (ntohs(fileseg->file_id) != group->file_id) { glog2(group, "Rejecting FILESEG: got incorrect file_id %04X", ntohs(fileseg->file_id)); return; } tfmcc = NULL; if (fileseg->hlen * 4U > sizeof(struct fileseg_h)) { he = (const uint8_t *)fileseg + sizeof(struct fileseg_h); if (*he == EXT_TFMCC_DATA_INFO) { tfmcc = (const struct tfmcc_data_info_he *)he; extlen = tfmcc->extlen * 4U; if ((extlen > (fileseg->hlen * 4U) - sizeof(struct fileseg_h)) || extlen < sizeof(struct tfmcc_data_info_he)) { glog2(group, "Rejecting FILESEG: invalid extension size"); return; } } } section = ntohs(fileseg->section); if (section >= group->fileinfo.big_sections) { seq = (group->fileinfo.big_sections * group->fileinfo.secsize_big) + ((section - group->fileinfo.big_sections) * group->fileinfo.secsize_small) + ntohs(fileseg->sec_block); } else { seq = (section * group->fileinfo.secsize_big) + ntohs(fileseg->sec_block); } if ((datalen != group->blocksize) && (seq != group->fileinfo.blocks - 1)) { glog2(group, "Rejecting FILESEG: invalid data size %d", datalen); return; } if (log_level >= 5) { glog5(group, "Got packet %d", seq); } else if (log_level == 4) { if (seq != group->fileinfo.last_block + 1) { glog4(group, "Got packet %d, last was %d", seq, group->fileinfo.last_block); } } if ((group->cc_type == CC_TFMCC) && tfmcc) { handle_tfmcc_data_info(group, tfmcc); } group->fileinfo.got_data = 1; group->fileinfo.last_block = seq; if (txseq == group->max_txseq) { if ((section > group->fileinfo.last_section) && (group->fileinfo.nak_time.tv_sec == 0)) { // Start timer to send NAKs gettimeofday(&group->fileinfo.nak_time, NULL); add_timeval_d(&group->fileinfo.nak_time, 1 * group->grtt); group->fileinfo.nak_section_first = group->fileinfo.last_section; group->fileinfo.nak_section_last = section; group->fileinfo.got_done = 0; glog3(group, "New section, set NAK timer for sections %d - %d", group->fileinfo.nak_section_first, group->fileinfo.nak_section_last); } group->fileinfo.last_section = section; } if (group->fileinfo.naklist[seq]) { if ((seq >= group->fileinfo.cache_start) && (seq <= group->fileinfo.cache_end + MAXMISORDER)) { cache_offset=(seq - group->fileinfo.cache_start) * group->blocksize; if (seq > group->fileinfo.cache_end) { if ((cache_offset + datalen) > cache_len) { glog4(group, "Disk cache full, flushing"); if (!flush_disk_cache(group)) { return; } cache_offset = (seq - group->fileinfo.cache_start) * group->blocksize; } else { for (i = group->fileinfo.cache_end; i <= seq; i++) { if (!group->fileinfo.naklist[i]) { glog3(group, "Cache gap seq %d " "already received, flushing", i); if (!flush_disk_cache(group)) { return; } group->fileinfo.cache_start = seq; cache_offset = 0; break; } } group->fileinfo.cache_end = seq; } } } else { if (group->fileinfo.cache_len != 0) { glog3(group, "Seq %d out of cache range, flushing", seq); if (!flush_disk_cache(group)) { return; } } cache_offset = 0; group->fileinfo.cache_start = seq; group->fileinfo.cache_end = seq; } group->fileinfo.cache_len = ((group->fileinfo.cache_end - group->fileinfo.cache_start) * group->blocksize) + datalen; status_idx = seq - group->fileinfo.cache_start; if (group->fileinfo.cache_len > cache_len) { glog0(group, "Cache overrun: " "current cache len = %d, status_idx = %d", group->fileinfo.cache_len, status_idx); } group->fileinfo.cache_status[status_idx] = 1; memcpy(&group->fileinfo.cache[cache_offset], data, datalen); } set_timeout(group, 0); }
/** * Read in the contents of an ANNOUNCE. */ int read_announce(struct group_list_t *group, unsigned char *packet, union sockaddr_u *src, struct timeval rxtime, int packetlen) { struct uftp_h *header; struct announce_h *announce; struct enc_info_he *encinfo; uint8_t *publicmcast, *privatemcast; uint8_t *he; unsigned int iplen, extlen; header = (struct uftp_h *)packet; announce = (struct announce_h *)(packet + sizeof(struct uftp_h)); encinfo = NULL; group->phase = PHASE_REGISTERED; group->version = header->version; group->group_id = ntohl(header->group_id); group->group_inst = header->group_inst; group->src_id = header->src_id; if (has_proxy) { group->replyaddr = proxy_info.addr; } else { group->replyaddr = *src; } group->grtt = unquantize_grtt(header->grtt); group->rtt = 0; group->robust = announce->robust; group->cc_type = announce->cc_type; group->gsize = unquantize_gsize(header->gsize); group->blocksize = ntohs(announce->blocksize); group->last_server_ts.tv_sec = ntohl(announce->tstamp_sec); group->last_server_ts.tv_usec = ntohl(announce->tstamp_usec); group->last_server_rx_ts = rxtime; group->restart = ((group->group_inst != 0) && (strcmp(tempdir, ""))); group->sync_preview = ((announce->flags & FLAG_SYNC_PREVIEW) != 0); group->sync_mode = group->sync_preview || ((announce->flags & FLAG_SYNC_MODE) != 0); iplen = ((announce->flags & FLAG_IPV6) != 0) ? sizeof(struct in6_addr) : sizeof(struct in_addr); publicmcast = ((uint8_t *)announce) + sizeof(struct announce_h); privatemcast = publicmcast + iplen; if ((announce->flags & FLAG_IPV6) != 0) { group->multi.sin6.sin6_family = AF_INET6; #ifdef SOCKADDR_LEN group->multi.sin6.sin6_len = sizeof(struct sockaddr_in6); #endif memcpy(&group->multi.sin6.sin6_addr.s6_addr, privatemcast, iplen); } else { group->multi.sin.sin_family = AF_INET; #ifdef SOCKADDR_LEN group->multi.sin.sin_len = sizeof(struct sockaddr_in); #endif memcpy(&group->multi.sin.sin_addr.s_addr, privatemcast, iplen); } group->fileinfo.fd = -1; if ((announce->hlen * 4U) < sizeof(struct announce_h) + (2U * iplen)) { glog1(group, "Rejecting ANNOUNCE from %08X: invalid header size", ntohl(group->src_id)); send_abort(group, "Invalid header size"); return 0; } if ((announce->hlen * 4U) > sizeof(struct announce_h) + (2U * iplen)) { he = (unsigned char *)announce + sizeof(struct announce_h) + (2U * iplen); if (*he == EXT_ENC_INFO) { encinfo = (struct enc_info_he *)he; extlen = encinfo->extlen * 4U; if ((extlen > ((announce->hlen * 4U) - sizeof(struct announce_h))) || (extlen < sizeof(struct enc_info_he)) || (extlen != (sizeof(struct enc_info_he) + ntohs(encinfo->keylen) + ntohs(encinfo->dhlen) + ntohs(encinfo->siglen)))) { glog1(group, "Rejecting ANNOUNCE from %08X: " "invalid extension size", ntohl(group->src_id)); send_abort(group, "Invalid extension size"); return 0; } } } if (encinfo != NULL) { if (!read_announce_encryption(group, encinfo, packet, packetlen)) { return 0; } } else if (encrypted_only) { glog1(group, "No unencrypted transfers allowed"); send_abort(group, "No unencrypted transfers allowed"); return 0; } else { group->keyextype = KEYEX_NONE; group->keytype = KEY_NONE; group->hashtype = HASH_NONE; group->sigtype = SIG_NONE; group->client_auth = 0; } gettimeofday(&group->expire_time, NULL); if (4 * group->robust * group->grtt < 1.0) { add_timeval_d(&group->expire_time, 1.0); } else { add_timeval_d(&group->expire_time, 4 * group->robust * group->grtt); } group->fileinfo.nak_time.tv_sec = 0; group->fileinfo.nak_time.tv_usec = 0; // Size of data packet, used in transmission speed calculations group->datapacketsize = group->blocksize + sizeof(struct fileseg_h); if (group->cc_type == CC_TFMCC) { group->datapacketsize += sizeof(struct tfmcc_data_info_he); } if (group->keytype != KEY_NONE) { group->datapacketsize += ((group->sigtype == SIG_KEYEX) ? group->server_pubkeylen : (group->sigtype == SIG_HMAC) ? group->hmaclen : 0) + KEYBLSIZE + sizeof(struct encrypted_h); } // 8 = UDP size, 20 = IPv4 size, 40 = IPv6 size if ((announce->flags & FLAG_IPV6) != 0) { group->datapacketsize += sizeof(struct uftp_h) + 8 + 40; } else { group->datapacketsize += sizeof(struct uftp_h) + 8 + 20; } if (group->cc_type != CC_NONE) { group->loss_history= safe_calloc(0x10000,sizeof(struct loss_history_t)); group->slowstart = 1; group->seq_wrap = 0; group->start_txseq = ntohs(header->seq); group->max_txseq = group->start_txseq; group->loss_history[group->start_txseq].found = 1; group->loss_history[group->start_txseq].t = rxtime; group->loss_history[group->start_txseq].size = packetlen; } return 1; }