Exemple #1
0
/**
 * 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);
}
Exemple #2
0
/**
 * Build the NAK list for a given section.  Returns the NAK count.
 */
unsigned int get_naks(struct group_list_t *group, 
                      unsigned int section, unsigned char **naks)
{
    unsigned int section_offset, blocks_this_sec, i;
    unsigned int nakidx, naklistidx, numnaks;
    
    if ((group->phase == PHASE_MIDGROUP) || (group->file_id == 0) ||
        (group->fileinfo.section_done[section])) {
        *naks = NULL;
        return 0;
    }

    flush_disk_cache(group);
    if (section >= group->fileinfo.big_sections) {
        section_offset = (group->fileinfo.big_sections *
                    group->fileinfo.secsize_big) +
                ((section - group->fileinfo.big_sections) *
                    group->fileinfo.secsize_small);
        blocks_this_sec = group->fileinfo.secsize_small;
    } else {
        section_offset = section * group->fileinfo.secsize_big;
        blocks_this_sec = group->fileinfo.secsize_big;
    }
    glog3(group, "getting naks: section: %d, offset: %d, blocks: %d",
                 section, section_offset, blocks_this_sec);

    *naks = safe_calloc(group->blocksize, 1);

    // Build NAK list
    numnaks = 0;
    for (i = 0; i < blocks_this_sec; i++) {
        nakidx = i + section_offset;
        naklistidx = i;
        if (group->fileinfo.naklist[nakidx]) {
            glog4(group, "NAK for %d", nakidx);
            // Each bit represents a NAK; set each one we have a NAK for
            // Simplified: *naks[naklistidx / 8] |= (1 << (naklistidx % 8))
            (*naks)[naklistidx >> 3] |= (1 << (naklistidx & 7));
            numnaks++;
        }
    }
Exemple #3
0
/**
 * Process an incoming FILEINFO message.
 * Expected in the middle of a group with no current file.
 */
void handle_fileinfo(struct group_list_t *group, const unsigned char *message,
                     unsigned meslen, struct timeval rxtime)
{
    stat_struct statbuf;
    int found_dir;

    if (!read_fileinfo(group, message, meslen, rxtime)) {
        return;
    }

    glog2(group, "Name of file to receive: %s", group->fileinfo.name);
    switch (group->fileinfo.ftype) {
    case FTYPE_REG:
        glog2(group, "Bytes: %s, Blocks: %d, Sections: %d",
                     printll(group->fileinfo.size),
                     group->fileinfo.blocks, group->fileinfo.sections);
        glog3(group, "small section size: %d, "
                     "big section size: %d, # big sections: %d",
                     group->fileinfo.secsize_small, group->fileinfo.secsize_big,
                     group->fileinfo.big_sections);
        break;
    case FTYPE_DIR:
        glog2(group, "Empty directory");
        break;
    case FTYPE_LINK:
        glog2(group, "Symbolic link to %s", group->fileinfo.linkname);
        break;
    case FTYPE_DELETE:
        glog2(group, "Deleting file/directory");
        break;
    case FTYPE_FREESPACE:
        glog2(group, "Get free space for path");
        break;
    default:
        glog1(group, "Invalid file type: %d", group->fileinfo.ftype);
        send_abort(group, "Invalid file type");
        return;
    }

    if (!setup_dest_file(group)) {
        // A rejected file is still a success because we responded with a
        // COMPLETE with status=rejected instead of with an ABORT
        return;
    }

    // Make sure the path to the destination file exists and
    // remove or back up any existing file
    if (!create_path_to_file(group, group->fileinfo.filepath)) {
        glog0(group, "Error creating path to data file");
        early_complete(group, COMP_STAT_REJECTED, 0);
        return;
    }
    found_dir = 0;
    if (tempfile && !group->sync_preview) {
        clear_path(group->fileinfo.temppath, group);
    }
    if ((group->fileinfo.ftype != FTYPE_DELETE) ||
            (group->fileinfo.ftype != FTYPE_FREESPACE)) {
        // Don't do path checks for metafile commands
    } else if (lstat_func(group->fileinfo.filepath, &statbuf) != -1) {
        glog3(group, "checking existing file");
        if ((group->fileinfo.ftype != FTYPE_DIR) || !S_ISDIR(statbuf.st_mode)) {
            if ((group->fileinfo.ftype != FTYPE_REG) ||
                    !S_ISREG(statbuf.st_mode) ||
                    ((!group->restart) && (!group->sync_mode))) {
                // Don't clear/backup if we're receiving a regular file
                // and we're in either restart mode or sync mode
                glog3(group, "calling move_to_backup");
                if (!tempfile) {
                    move_to_backup(group);
                }
            }
        } else {
            glog3(group, "found dir");
            found_dir = 1;
        }
    } else if (errno != ENOENT) {
        gsyserror(group, "Error checking file %s",group->fileinfo.filepath);
    }

    switch (group->fileinfo.ftype) {
    case FTYPE_REG:
        handle_fileinfo_regular(group);
        break;
    case FTYPE_DIR:
        handle_fileinfo_dir(group, found_dir);
        break;
    case FTYPE_LINK:
        handle_fileinfo_link(group);
        break;
    case FTYPE_DELETE:
        handle_fileinfo_delete(group);
        break;
    case FTYPE_FREESPACE:
        handle_fileinfo_freespace(group);
        break;
    default:
        glog0(group, "Error handling FILEINFO: shouldn't get here!");
    }
}
Exemple #4
0
/**
 * Gets the current timeout value to use for the main loop
 *
 * First check to see if any active groups have an expired timeout, and
 * handle that timeout.  Once all expired timeouts have been handled, find
 * the active group with the earliest timeout and return the time until that
 * timeout.  If there are no active groups, return NULL.
 */
struct timeval *getrecenttimeout(void)
{
    static struct timeval tv = {0,0};
    struct timeval current_timestamp, min_timestamp;
    int i, found_timeout, done, sent_naks;
    struct group_list_t *group;
    unsigned int section, nak_count;
    unsigned char *naks;

    gettimeofday(&current_timestamp, NULL);
    done = 0;
    while (!done) {
        found_timeout = 0;
        done = 1;
        for (i = 0; i < MAXLIST; i++) {
            group = &group_list[i];
            if (group->group_id != 0) {
                if (cmptimestamp(current_timestamp, group->timeout_time) >= 0) {
                    switch (group->phase) {
                    case PHASE_REGISTERED:
                        send_register(group);
                        break;
                    case PHASE_RECEIVING:
                    case PHASE_MIDGROUP:
                        glog1(group, "Transfer timed out");
                        send_abort(group, "Transfer timed out");
                        break;
                    case PHASE_COMPLETE:
                        send_complete(group, 0);
                        break;
                    }
                    done = 0;
                } else if ((!found_timeout) ||
                           (cmptimestamp(group->timeout_time,
                                         min_timestamp) < 0)) {
                    glog5(group, "found min timeout time: %d:%06d",
                                 group->timeout_time.tv_sec,
                                 group->timeout_time.tv_usec);
                    min_timestamp = group->timeout_time;
                    found_timeout = 1;
                }
                // Check for a NAK timeout for sending a STATUS or COMPLETE
                if ((group->fileinfo.nak_time.tv_sec != 0) &&
                        cmptimestamp(current_timestamp,
                                     group->fileinfo.nak_time) >= 0) {
                    group->fileinfo.nak_time.tv_sec = 0;
                    group->fileinfo.nak_time.tv_usec = 0;
                    // Send NAKs
                    sent_naks = 0;
                retry_naks:
                    for (section = group->fileinfo.nak_section_first;
                            section < group->fileinfo.nak_section_last;
                            section++) {
                        naks = NULL;
                        nak_count = get_naks(group, section, &naks);
                        glog3(group, "read %d NAKs for section %d",
                                     nak_count, section);
                        if (nak_count > 0) {
                            send_status(group, section, naks, nak_count);
                            sent_naks = 1;
                        }
                        free(naks);
                        naks = NULL;
                    }
                    if (file_done(group, 1)) {
                        glog2(group, "File transfer complete");
                        send_complete(group, 0);
                        file_cleanup(group, 0);
                    } else if (group->fileinfo.got_done && !sent_naks) {
                        // We didn't send any NAKs since the last time
                        // but the server is asking for some,
                        // so check all prior sections
                        group->fileinfo.nak_section_last = 
                                group->fileinfo.nak_section_first;
                        group->fileinfo.nak_section_first = 0;
                        group->fileinfo.got_done = 0;
                        goto retry_naks;
                    }
                } else if ((group->fileinfo.nak_time.tv_sec != 0) &&
                           ((!found_timeout) ||
                            (cmptimestamp(group->fileinfo.nak_time,
                                          min_timestamp) < 0))) {
                    glog5(group, "found min nak time: %d:%06d",
                         group->fileinfo.nak_time.tv_sec,
                         group->fileinfo.nak_time.tv_usec);
                    min_timestamp = group->fileinfo.nak_time;
                    found_timeout = 1;
                }
                // Check congestion control feedback timer
                if (!group->isclr) {
                    if ((group->cc_time.tv_sec != 0) &&
                            (cmptimestamp(current_timestamp,
                                          group->cc_time) >= 0)) {
                        send_cc_ack(group);
                    } else if ((group->cc_time.tv_sec != 0) &&
                               ((!found_timeout) ||
                                (cmptimestamp(group->cc_time,
                                              min_timestamp) < 0))) {
                        glog5(group, "found min CC time: %d:%06d",
                             group->cc_time.tv_sec, group->cc_time.tv_usec);
                        min_timestamp = group->cc_time;
                        found_timeout = 1;
                    }
                }
            }
        }
        // Check timeout for proxy key request
        if (has_proxy && (proxy_pubkey.key == 0)) {
            if (cmptimestamp(current_timestamp, next_keyreq_time) >= 0) {
                send_key_req();
                done = 0;
            } else if ((!found_timeout) ||
                       (cmptimestamp(next_keyreq_time, min_timestamp) < 0)) {
                min_timestamp = next_keyreq_time;
                found_timeout = 1;
            }
        }
        // Check timeout for sending heartbeat
        if (hbhost_count) {
            if (cmptimestamp(current_timestamp, next_hb_time) >= 0) {
                send_hb_request(listener, hb_hosts, hbhost_count,
                                &next_hb_time, hb_interval, uid);
                done = 0;
            } else if ((!found_timeout) ||
                       (cmptimestamp(next_hb_time, min_timestamp) < 0)) {
                min_timestamp = next_hb_time;
                found_timeout = 1;
            }
        }

    }
    if (found_timeout) {
        tv = diff_timeval(min_timestamp, current_timestamp);
        return &tv;
    } else {
        return NULL;
    }
}
Exemple #5
0
/**
 * This is the main message reading loop.  Messages are read, validated,
 * decrypted if necessary, then passed to the appropriate routine for handling.
 */
void mainloop(void)
{
    struct uftp_h *header;
    struct group_list_t *group;
    unsigned char *buf, *decrypted, *message;
    char rxname[INET6_ADDRSTRLEN];
    unsigned int decryptlen, meslen;
    int packetlen, rval, i, ecn;
    uint8_t version, *func, tos;
    uint16_t txseq;
    union sockaddr_u src;
    struct timeval *tv, rxtime;
    double new_grtt;

    log2(0, 0, 0, "%s", VERSIONSTR);
    for (i = 0; i < key_count; i++) {
        if (privkey_type[i] == KEYBLOB_RSA) {
            log2(0, 0, 0, "Loaded %d bit RSA key with fingerprint %s",
                  RSA_keylen(privkey[i].rsa) * 8,
                  print_key_fingerprint(privkey[i], KEYBLOB_RSA));
        } else {
            log2(0, 0, 0, "Loaded ECDSA key with curve %s and fingerprint %s",
                  curve_name(get_EC_curve(privkey[i].ec)),
                  print_key_fingerprint(privkey[i], KEYBLOB_EC));
        }
    }

    buf = safe_calloc(MAXMTU, 1);
    decrypted = safe_calloc(MAXMTU, 1);
    header = (struct uftp_h *)buf;

    while (1) {
        tv = getrecenttimeout();
        if (tv) {
            log5(0, 0, 0, "read timeout: %d.%06d", tv->tv_sec, tv->tv_usec);
        }
        if (read_packet(listener, &src, buf, &packetlen,
                        MAXMTU, tv, &tos) <= 0) {
            continue;
        }
        gettimeofday(&rxtime, NULL);

        if ((rval = getnameinfo((struct sockaddr *)&src, family_len(src),
                rxname, sizeof(rxname), NULL, 0, NI_NUMERICHOST)) != 0) {
            log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval));
        }

        if (header->version == UFTP_VER_NUM) {
            version = header->version;
            group = find_group(ntohl(header->group_id), header->group_inst);
        } else {
            log1(0, 0, 0, "Invalid message from %s: not uftp packet "
                          "or invalid version", rxname);
            continue;
        }
        if (packetlen < sizeof(struct uftp_h) + 4) {
            log1(0, 0, 0, "Invalid packet size from %s: %d", rxname, packetlen);
            continue;
        }

        txseq = htons(header->seq);
        // A KEY_INFO or ABORT could come from a proxy, so don't check the seq
        // TODO: need to account for these in the loss history
        if ((group != NULL) && (header->func != KEYINFO) &&
                (header->func != ABORT)) {
            if ((int16_t)(group->max_txseq - txseq) > MAXMISORDER) {
                glog3(group, "seq out of range, dropping");
                continue;
            }
            if (group->cc_type != CC_NONE) {
                ecn = ((tos & 0x3) == 3);
                update_loss_history(group, txseq, packetlen, ecn);
            } else if ((int16_t)(txseq - group->max_txseq) > 0) {
                group->max_txseq = txseq;
            }
        }

        if ((header->func == ENCRYPTED) && (group != NULL) &&
                (group->keytype != KEY_NONE)) {
            if (group->phase == PHASE_REGISTERED) {
                glog1(group, "Got encrypted packet from %s "
                             "but keys not established", rxname);
            }

            if (!validate_and_decrypt(buf, packetlen, &decrypted, &decryptlen,
                    group->keytype, group->groupkey, group->groupsalt,
                    group->ivlen, group->hashtype, group->grouphmackey,
                    group->hmaclen, group->sigtype, group->keyextype,
                    group->server_pubkey, group->server_pubkeylen)) {
                glog1(group, "Rejecting message from %s: "
                             "decrypt/validate failed", rxname);
                continue;
            }
            func = (uint8_t *)decrypted;
            message = decrypted;
            meslen = decryptlen;
        } else {
            if ((group != NULL) && (group->keytype != KEY_NONE) &&
                    ((header->func == FILEINFO) || (header->func == FILESEG) ||
                     (header->func == DONE) || (header->func == DONE_CONF) ||
                     ((header->func == ABORT) &&
                         (group->phase != PHASE_REGISTERED)))) {
                glog1(group, "Rejecting %s message from %s: not encrypted",
                             func_name(header->func), rxname);
                continue;
            }
            func = (uint8_t *)&header->func;
            message = buf + sizeof(struct uftp_h);
            meslen = packetlen - sizeof(struct uftp_h);
        }

        if (group != NULL) {
            new_grtt = unquantize_grtt(header->grtt);
            if (fabs(new_grtt - group->grtt) > 0.001) {
                group->grtt = new_grtt;
                set_timeout(group, 1);
            }
            group->gsize = unquantize_gsize(header->gsize);
            glog5(group, "grtt: %.3f", group->grtt);
        }

        if (header->func == PROXY_KEY) {
            handle_proxy_key(&src, message, meslen);
            continue;
        }
        if (header->func == HB_RESP) {
            handle_hb_response(listener, &src, message, meslen, hb_hosts,
                               hbhost_count, privkey[0], privkey_type[0], uid);
            continue;
        }
        if (header->func == ANNOUNCE) {
            // Ignore any ANNOUNCE for a group we're already handling
            if (group == NULL) {
                handle_announce(&src, buf, packetlen, rxtime);
            } else if (group->phase == PHASE_MIDGROUP) {
                // Make sure we don't time out while waiting for other
                // clients to register with the server.
                set_timeout(group, 0);
            }
        } else {
            if (group == NULL) {
                // group / file ID not in list
                continue;
            }
            if (group->version != version) {
                glog1(group, "Version mismatch");
                continue;
            }
            if (group->src_id != header->src_id) {
                glog1(group, "Source ID mismatch");
                continue;
            }
            if (*func == ABORT) {
                handle_abort(group, message, meslen);
                continue;
            }
            switch (group->phase) {
            case PHASE_REGISTERED:
                if (group->keytype != KEY_NONE) {
                    if (*func == KEYINFO) {
                        handle_keyinfo(group, message, meslen, header->src_id);
                    } else {
                        glog1(group, "Expected KEYINFO, got %s",
                                     func_name(*func));
                    }
                } else if (group->keytype == KEY_NONE) {
                    if (*func == REG_CONF) {
                        handle_regconf(group, message, meslen);
                    } else if (*func == FILEINFO) {
                        handle_fileinfo(group, message, meslen, rxtime);
                    } else {
                        glog1(group, "Expected REG_CONF, got %s",
                                     func_name(*func));
                    }
                }
                break;
            case PHASE_MIDGROUP:
                if (*func == FILEINFO) {
                    handle_fileinfo(group, message, meslen, rxtime);
                } else if (*func == KEYINFO) {
                    handle_keyinfo(group, message, meslen, header->src_id);
                } else if (*func == DONE) {
                    handle_done(group, message, meslen);
                } else {
                    // Other clients may be still getting earlier files or
                    // setting up, so silently ignore anything unexpected
                    // and reset the timeout.
                    set_timeout(group, 0);
                }
                break;
            case PHASE_RECEIVING:
                if (*func == FILEINFO) {
                    handle_fileinfo(group, message, meslen, rxtime);
                } else if (*func == FILESEG) {
                    handle_fileseg(group, message, meslen, txseq);
                } else if (*func == DONE) {
                    handle_done(group, message, meslen);
                } else if (*func == CONG_CTRL) {
                    handle_cong_ctrl(group, message, meslen, rxtime);
                }
                break;
            case PHASE_COMPLETE:
                if (*func == DONE_CONF) {
                    handle_done_conf(group, message, meslen);
                }
                break;
            }
        }
    }
}
Exemple #6
0
/**
 * 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);
}
Exemple #7
0
/**
 * Processes a new incoming ANNOUNCE
 */
void handle_announce(union sockaddr_u *src, unsigned char *packet,
                     unsigned packetlen, struct timeval rxtime)
{
    struct uftp_h *header;
    struct announce_h *announce;
    uint32_t *addrlist;
    int addrlen, rval;
    struct group_list_t *group;
    time_t t;
    struct tm *start_time;
    char privname[INET6_ADDRSTRLEN], srcname[INET6_ADDRSTRLEN];
    char srcfqdn[DESTNAME_LEN];

    header = (struct uftp_h *)packet;
    announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
    addrlist = (uint32_t *)((unsigned char *)announce + (announce->hlen * 4));
    addrlen = (packetlen - sizeof(struct uftp_h) - (announce->hlen * 4)) / 4;

    if ((packetlen < sizeof(struct uftp_h) + (announce->hlen * 4U)) ||
            ((announce->hlen * 4U) < sizeof(struct announce_h))) {
        log1(ntohl(header->group_id), header->group_inst, 0, 
                "Rejecting ANNOUNCE from %08X: invalid message size",
                ntohl(header->src_id));
        return;
    }

    if ((addrlen != 0) && (!uid_in_list(addrlist, addrlen))) {
        log1(ntohl(header->group_id), header->group_inst, 0,
                "Name not in host list");
        return;
    }

    if ((group = find_open_slot()) == NULL ) {
        log0(ntohl(header->group_id), header->group_inst, 0,
             "Error: maximum number of incoming files exceeded: %d\n", MAXLIST);
        return;
    }

    t = time(NULL);
    start_time = localtime(&t);
    snprintf(group->start_date, sizeof(group->start_date), "%04d%02d%02d",
            start_time->tm_year + 1900,
            start_time->tm_mon + 1, start_time->tm_mday);
    snprintf(group->start_time, sizeof(group->start_time), "%02d%02d%02d",
            start_time->tm_hour, start_time->tm_min, start_time->tm_sec);

    if (!read_announce(group, packet, src, rxtime, packetlen)) {
        return;
    }

    if ((rval = getnameinfo((struct sockaddr *)src, family_len(*src),
            srcname, sizeof(srcname), NULL, 0, NI_NUMERICHOST)) != 0) {
        glog1(group, "getnameinfo failed: %s", gai_strerror(rval));
    }
    if (!noname) {
        if ((rval = getnameinfo((struct sockaddr *)src, family_len(*src),
                srcfqdn, sizeof(srcfqdn), NULL, 0, 0)) != 0) {
            glog1(group, "getnameinfo failed: %s", gai_strerror(rval));
        }
    } else {
        strncpy(srcfqdn, srcname, sizeof(srcfqdn) - 1);
    }
    if ((rval = getnameinfo((struct sockaddr *)&group->multi,
            family_len(group->multi), privname, sizeof(privname),
            NULL, 0, NI_NUMERICHOST)) != 0) {
        glog1(group, "getnameinfo failed: %s", gai_strerror(rval));
    }

    glog2(group, "Received request from %08X at %s (%s)",
                             ntohl(group->src_id), srcfqdn, srcname);
    glog2(group, "Using private multicast address %s", privname);
    glog3(group, "grtt = %.6f", group->grtt);
    glog3(group, "send time: %d.%06d", group->last_server_ts.tv_sec,
                 group->last_server_ts.tv_usec);
    glog3(group, "receive time: %d.%06d", group->last_server_rx_ts.tv_sec,
                 group->last_server_rx_ts.tv_usec);

    if (status_file) {
        fprintf(status_file,
                "CONNECT;%04d/%02d/%02d-%02d:%02d:%02d;%08X;%08X;%s;%s\n",
                start_time->tm_year + 1900, start_time->tm_mon + 1,
                start_time->tm_mday, start_time->tm_hour,
                start_time->tm_min, start_time->tm_sec,
                ntohl(group->src_id), group->group_id, srcname, srcfqdn);
        fflush(status_file);
    }

    if (group->restart) {
        if (group->sync_mode) {
            glog1(group, "Sync mode and restart mode incompatible");
            send_abort(group, "Sync mode and restart mode incompatible");
            return;
        }
    }

    if (!addr_blank(&group->multi)) {
        if (server_count > 0) {
            if (!is_multicast(&group->multi, 1)) {
                glog1(group, "Invalid source specific multicast address: %s",
                             privname);
                send_abort(group, "Invalid source specific multicast address");
                return;
            }
            if (!other_mcast_users(group)) {
                if (!multicast_join(listener, group->group_id, &group->multi,
                        m_interface, interface_count,
                        server_keys, server_count)) {
                    send_abort(group, "Error joining multicast group");
                    return;
                }
                if (has_proxy) {
                    if (!multicast_join(listener,group->group_id, &group->multi,
                            m_interface, interface_count, &proxy_info, 1)) {
                        send_abort(group, "Error joining multicast group");
                        return;
                    }
                }
            }
        } else {
            if (!is_multicast(&group->multi, 0)) {
                glog1(group, "Invalid multicast address: %s", privname);
                send_abort(group, "Invalid multicast address");
                return;
            }
            if (!other_mcast_users(group)) {
                if (!multicast_join(listener, group->group_id,
                        &group->multi, m_interface, interface_count, NULL, 0)) {
                    send_abort(group, "Error joining multicast group");
                    return;
                }
            }
        }
        group->multi_join = 1;
    }

    send_register(group);
}
Exemple #8
0
/**
 * Sends a FILEINFO_ACK in response to a FILEINFO
 */
void send_fileinfo_ack(struct group_list_t *group, int restart)
{
    unsigned char *buf, *encrypted, *outpacket;
    struct uftp_h *header;
    struct fileinfoack_h *fileinfo_ack;
    struct timeval now, send_time;
    unsigned int payloadlen;
    int enclen;

    buf = safe_calloc(MAXMTU, 1);

    header = (struct uftp_h *)buf;
    fileinfo_ack = (struct fileinfoack_h *)(buf + sizeof(struct uftp_h));

    payloadlen = sizeof(struct fileinfoack_h);
    set_uftp_header(header, FILEINFO_ACK, group);
    fileinfo_ack->func = FILEINFO_ACK;
    fileinfo_ack->hlen = sizeof(struct fileinfoack_h) / 4;
    fileinfo_ack->file_id = htons(group->file_id);
    if (restart) {
        fileinfo_ack->flags |= FLAG_PARTIAL;
    }
    gettimeofday(&now, NULL);
    if (cmptimestamp(now, group->last_server_rx_ts) <= 0) {
        send_time = group->last_server_ts;
    } else {
        send_time = add_timeval(group->last_server_ts,
                diff_timeval(now, group->last_server_rx_ts));
    }
    fileinfo_ack->tstamp_sec = htonl((uint32_t)send_time.tv_sec);
    fileinfo_ack->tstamp_usec = htonl((uint32_t)send_time.tv_usec);
    if (group->keytype != KEY_NONE) {
        encrypted = NULL;
        if (!encrypt_and_sign(buf, &encrypted, payloadlen, &enclen,
                group->keytype, group->groupkey, group->groupsalt,&group->ivctr,
                group->ivlen, group->hashtype, group->grouphmackey,
                group->hmaclen, group->sigtype, group->keyextype,
                group->client_privkey, group->client_privkeylen)) {
            glog0(group, "Error encrypting FILEINFO_ACK");
            free(buf);
            return;
        }
        outpacket = encrypted;
        payloadlen = enclen;
    } else {
        encrypted = NULL;
        outpacket = buf;
    }
    payloadlen += sizeof(struct uftp_h);

    if (nb_sendto(listener, outpacket, payloadlen, 0,
               (struct sockaddr *)&(group->replyaddr),
               family_len(group->replyaddr)) == SOCKET_ERROR) {
        gsockerror(group, "Error sending FILEINFO_ACK");
    } else {
        glog2(group, "FILEINFO_ACK sent");
    }
    glog3(group, "send time: %d.%06d", send_time.tv_sec, send_time.tv_usec);
    free(encrypted);
    free(buf);
}
Exemple #9
0
/**
 * Sends a REGISTER message in response to an ANNOUNCE or on timeout when
 * waiting for a KEYINFO or REG_CONF.  If the register timeout expired, abort.
 */
void send_register(struct group_list_t *group)
{
    struct uftp_h *header;
    struct register_h *reg;
    unsigned char *buf, *keydata;
    struct timeval now, send_time;
    unsigned int len, meslen;
    union key_t key;

    gettimeofday(&now, NULL);
    if (cmptimestamp(now, group->expire_time) >= 0) {
        glog1(group, "Registration unconfirmed by server");
        send_abort(group, "Registration unconfirmed");
        return;
    }

    buf = safe_calloc(MAXMTU, 1);

    header = (struct uftp_h *)buf;
    reg = (struct register_h *)(buf + sizeof(struct uftp_h));
    keydata = (unsigned char *)reg + sizeof(struct register_h);
    set_uftp_header(header, REGISTER, group);
    reg->func = REGISTER;
    if (group->keytype != KEY_NONE) {
        memcpy(reg->rand2, group->rand2, RAND_LEN);
        if (group->keyextype == KEYEX_RSA) {
            if (has_proxy) {
                key = proxy_pubkey;
            } else {
                key = group->server_pubkey;
            }
            if (!RSA_encrypt(key.rsa, group->premaster, group->premaster_len,
                             keydata, &len)) {
                glog0(group, "Error encrypting premaster secret");
                send_abort(group, "Error encrypting premaster secret");
                free(buf);
                return;
            }
        } else {
            uint16_t keylen;
            if (!export_EC_key(group->client_dhkey.ec, keydata, &keylen)) {
                glog0(group, "Error exporting ECDH public key");
                send_abort(group, "Error exporting ECDH public key");
                free(buf);
                return;
            }
            len = keylen;
        }
        reg->keyinfo_len = htons(len); 
    } else {
        len = 0;
    }
    gettimeofday(&now, NULL);
    if (cmptimestamp(now, group->last_server_rx_ts) <= 0) {
        send_time = group->last_server_ts;
    } else {
        send_time = add_timeval(group->last_server_ts,
                diff_timeval(now, group->last_server_rx_ts));
    }
    reg->tstamp_sec = htonl((uint32_t)send_time.tv_sec);
    reg->tstamp_usec = htonl((uint32_t)send_time.tv_usec);
    reg->hlen = (sizeof(struct register_h) + len) / 4;
    meslen = sizeof(struct uftp_h) + (reg->hlen * 4);

    if (nb_sendto(listener, buf, meslen, 0,
               (struct sockaddr *)&(group->replyaddr),
               family_len(group->replyaddr)) == SOCKET_ERROR) {
        gsockerror(group, "Error sending REGISTER");
    } else {
        glog2(group, "REGISTER sent");
    }
    glog3(group, "send time: %d.%06d", send_time.tv_sec, send_time.tv_usec);

    set_timeout(group, 0);
    if (group->client_auth) {
        send_client_key(group);
    }
    free(buf);
}
Exemple #10
0
/**
 * Reads in the contents of the restart file.
 */
void read_restart_file(struct group_list_t *group)
{
    struct client_restart_t *restart;
    char restart_name[MAXPATHNAME];
    int fd, i, rval;

    // Don't bother if we're not using a temp directory.
    if (!strcmp(tempdir, "")) {
        return;
    }

    // First abort any prior session with the same group_id.
    // This creates the restart file.
    for (i = 0; i < MAXLIST; i++) {
        if ((group_list[i].group_id == group->group_id) &&
                (group_list[i].group_inst < group->group_inst)) {
            file_cleanup(&group_list[i], 1);
        }
    }

    glog2(group, "Reading restart file");
    snprintf(restart_name, sizeof(restart_name), "%s%c_group_%08X_restart",
             tempdir, PATH_SEP, group->group_id);
    if ((fd = open(restart_name, OPENREAD, 0644)) == -1) {
        gsyserror(group, "Failed to read restart file");
        return;
    }

    // Read header
    restart = safe_calloc(sizeof(struct client_restart_t), 1);
    if ((rval = file_read(fd, restart, sizeof(struct client_restart_t),
                          0)) == -1) {
        glog0(group, "Failed to read header for restart file");
        goto err1;
    }
    if (rval != sizeof(struct client_restart_t)) {
        glog0(group, "Failed to read header for restart file "
                "(read %d, expected %d)", rval,sizeof(struct client_restart_t));
        goto err1;
    }

    // Read NAK list
    if (restart->blocks) {
        restart->naklist = safe_calloc(restart->blocks, 1);
        if (file_read(fd, restart->naklist, restart->blocks, 0) == -1) {
            glog0(group, "Failed to read NAK list for restart file");
            goto err2;
        }
    }

    // Read section_done list
    if (restart->sections) {
        restart->section_done = safe_calloc(restart->sections, 1);
        if (file_read(fd, restart->section_done, restart->sections, 0) == -1) {
            glog0(group, "Failed to read section_done list for restart file");
            goto err3;
        }
    }
    close(fd);
    unlink(restart_name);
    group->restartinfo = restart;
    glog3(group, "Reading restart file done");
    return;

err3:
    free(restart->section_done);
err2:
    free(restart->naklist);
err1:
    free(restart);
    close(fd);
}
Exemple #11
0
/**
 * Puts the given message on the pending message list.  If it doesn't match
 * any pending message and there are no open slots, first send what's pending.
 * If the pending list is full after adding the given message, then send.
 */
void check_pending(struct pr_group_list_t *group, int hostidx,
                   const unsigned char *message)
{
    const struct fileinfoack_h *fileinfoack;
    const struct status_h *status;
    const struct complete_h *complete;
    const uint8_t *func;
    struct pr_pending_info_t *pending;
    int match, pendidx, hlen;

    func = message;
    fileinfoack = (const struct fileinfoack_h *)message;
    status = (const struct status_h *)message;
    complete = (const struct complete_h *)message;

    glog3(group, "check_timeout: looking for pending %s", func_name(*func));
    for (pendidx = 0; pendidx < MAX_PEND; pendidx++) {
        pending = &group->pending[pendidx];
        if (group->pending[pendidx].msg == 0) {
            glog3(group, "check_timeout: found empty slot %d", pendidx);
            match = 1;
            break;
        }

        match = (*func == pending->msg);
        switch (*func) {
        case REGISTER:
            // REGISTER always matches itself
            break;
        case FILEINFO_ACK:
            match = match && (ntohs(fileinfoack->file_id) == pending->file_id);
            break;
        case STATUS:
            match = match && ((ntohs(status->file_id) == pending->file_id) &&
                              (ntohs(status->section) == pending->section));
            break;
        case COMPLETE:
            match = match && ((ntohs(complete->file_id) == pending->file_id) &&
                              (complete->status == pending->comp_status));
            break;
        default:
            glog1(group, "Tried to check pending on invalid type %s",
                         func_name(*func));
            return;
        }
        if (match) {
            break;
        }
    }

    if (!match) {
        send_all_pending(group);
        pendidx = 0;
        pending = &group->pending[pendidx];
    }

    glog3(group, "check_timeout: found match at slot %d", pendidx);
    pending->msg = *func;
    if (group->destinfo[hostidx].pending != pendidx) {
        group->destinfo[hostidx].pending = pendidx;
        pending->count++;
    }

    switch (*func) {
    case REGISTER:
        hlen = sizeof(struct register_h);
        if (pending->count == 1) {
            gettimeofday(&pending->rx_tstamp, NULL);
            pending->tstamp = group->destinfo[hostidx].regtime;
            glog3(group, "send time = %d.%06d",
                         pending->tstamp.tv_sec, pending->tstamp.tv_usec);
            glog3(group, "rx time = %d.%06d",
                         pending->rx_tstamp.tv_sec, pending->rx_tstamp.tv_usec);
        }
        break;
    case FILEINFO_ACK:
        hlen = sizeof(struct fileinfoack_h);
        if (pending->count == 1) {
            pending->partial = 1;
            gettimeofday(&pending->rx_tstamp, NULL);
            pending->tstamp.tv_sec = ntohl(fileinfoack->tstamp_sec);
            pending->tstamp.tv_usec = ntohl(fileinfoack->tstamp_usec);
            glog3(group, "send time = %d.%06d",
                         pending->tstamp.tv_sec, pending->tstamp.tv_usec);
            glog3(group, "rx time = %d.%06d",
                         pending->rx_tstamp.tv_sec, pending->rx_tstamp.tv_usec);
        }
        pending->file_id = ntohs(fileinfoack->file_id);
        pending->partial = pending->partial &&
                            ((fileinfoack->flags & FLAG_PARTIAL) != 0);
        break;
    case STATUS:
        hlen = sizeof(struct status_h);
        pending->file_id = ntohs(status->file_id);
        pending->section = ntohs(status->section);
        if (!pending->naklist) {
            pending->naklist = safe_calloc(group->blocksize, 1);
        }
        add_naks_to_pending(group, pendidx, message);
        break;
    case COMPLETE:
        hlen = sizeof(struct complete_h);
        pending->file_id = ntohs(complete->file_id);
        pending->comp_status = complete->status;
        break;
    }

    if ((*func != STATUS) &&
            (pending->count == max_msg_dest(group, *func, hlen))) {
        send_pending(group, pendidx);
    } else {
        int total_pending, i;

        glog3(group, "check_timeout: getting pending count for %s",
                     func_name(*func));
        for (total_pending = 0, i = 0; i < MAX_PEND; i++) {
            glog3(group, "check_timeout: adding %d pending for %d",
                         group->pending[i].count, i);
            total_pending += group->pending[i].count;
        }
        if (total_pending == 1) {
            set_timeout(group, 1, 0);
        }
    }
}