Пример #1
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;
            }
        }
    }
}
Пример #2
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()
{
    struct uftp_h *header;
    struct uftp2_h *v2_header;
    unsigned char *buf, *decrypted, *message;
    unsigned int decryptlen, meslen, expectedlen;
    int packetlen, listidx, i;
    uint8_t version, *func;
    uint32_t v2func;
    struct sockaddr_in src;
    struct timeval *tv;
    const int bsize = 9000;  // Roughly size of ethernet jumbo frame

    log0(0, 0, "%s", VERSIONSTR);
    for (i = 0; i < key_count; i++) {
        log1(0, 0, "Loaded key with fingerprint %s",
                  print_key_fingerprint(privkey[i]));
    }

    buf = calloc(bsize, 1);
    decrypted = calloc(bsize, 1);
    if ((buf == NULL) || (decrypted == NULL)){
        syserror(0, 0, "calloc failed!");
        exit(1);
    }
    header = (struct uftp_h *)buf;
    v2_header = (struct uftp2_h *)buf;

    while (1) {
        tv = getrecenttimeout();
        if (read_packet(listener, &src, buf, &packetlen,
                        bsize, tv) <= 0) {
            continue;
        }

        if ((header->uftp_id == UFTP_VER_NUM) ||
                (header->uftp_id == UFTP_3_0_VER)) {
            version = header->uftp_id;
            expectedlen = sizeof(struct uftp_h) + ntohs(header->blsize);
            listidx = find_file(ntohl(header->group_id));
        } else if (ntohl(v2_header->uftp_id) == V2_UFTP_ID) {
            version = UFTP_V2_VER;
            expectedlen = V2_PACKETSIZE;
            listidx = find_file(ntohl(v2_header->tx_id));
            v2func = ntohl(v2_header->func);
        } else {
            log(0, 0, "Invalid message from %s: not uftp packet "
                      "or invalid version", inet_ntoa(src.sin_addr));
            continue;
        }
        if (packetlen != expectedlen) {
            log(0, 0, "Invalid packet size from %s: got %d, expected %d",
                      inet_ntoa(src.sin_addr), packetlen, expectedlen);
            continue;
        }

        if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
                (header->func == ENCRYPTED) && (listidx != -1) &&
                (group_list[listidx].keytype != KEY_NONE)) {
            if (group_list[listidx].phase == PHASE_REGISTERED) {
                log(ntohl(header->group_id), 0, "Got encrypted packet from %s "
                        "but keys not established", inet_ntoa(src.sin_addr));
            }

            if (!validate_and_decrypt(buf, &decrypted, &decryptlen,
                    group_list[listidx].mtu, group_list[listidx].keytype,
                    group_list[listidx].groupkey, group_list[listidx].groupsalt,
                    group_list[listidx].ivlen, group_list[listidx].hashtype,
                    group_list[listidx].grouphmackey,
                    group_list[listidx].hmaclen, group_list[listidx].sigtype,
                    group_list[listidx].serverkey,
                    group_list[listidx].server_keylen)) {
                log(ntohl(header->group_id), 0, "Rejecting message from %s: "
                        "decrypt/validate failed", inet_ntoa(src.sin_addr));
                continue;
            }
            func = (uint8_t *)decrypted;
            message = decrypted;
            meslen = decryptlen;
        } else if ((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) {
            if ((listidx != -1) && (group_list[listidx].keytype != KEY_NONE) &&
                    ((header->func == FILEINFO) || (header->func == FILESEG) ||
                     (header->func == DONE) || (header->func == DONE_CONF) ||
                     ((header->func == ABORT) &&
                         (group_list[listidx].phase != PHASE_REGISTERED)))) {
                log(0, 0, "Rejecting %s message from %s: not encrypted",
                        func_name(header->func), inet_ntoa(src.sin_addr));
                continue;
            }
            func = (uint8_t *)&header->func;
            message = buf + sizeof(struct uftp_h);
            meslen = ntohs(header->blsize);
        } else {
            // Version 2
            message = buf;
            meslen = V2_PACKETSIZE;
        }

        if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
                (header->func == PROXY_KEY)) {
            handle_proxy_key(&src, buf);
            continue;
        }
        if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
                (header->func == HB_RESP)) {
            handle_hb_response(listener, &src, buf, hb_hosts, hbhost_count,
                               noname, privkey[0]);
            continue;
        }
        if ((((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
                 (header->func == ANNOUNCE)) ||
                ((version == UFTP_V2_VER) && (v2func == V2_ANNOUNCE))) {
            // Ignore any ANNOUNCE for a group we're already handling
            if (listidx == -1) {
                handle_announce(src, version, buf);
            } else if (group_list[listidx].phase == PHASE_MIDGROUP) {
                // Make sure we don't time out while waiting for other
                // clients to register with the server.
                set_timeout(listidx);
            }
        } else {
            if (listidx == -1) {
                // group / file ID not in list
                continue;
            }
            if (group_list[listidx].version != version) {
                log(group_list[listidx].group_id, group_list[listidx].file_id,
                        "Version mismatch");
                continue;
            }
            if ((((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
                     (*func == ABORT)) ||
                    ((version == UFTP_V2_VER) && (v2func == V2_ABORT))) {
                handle_abort(listidx, message, meslen);
                continue;
            }
            switch (group_list[listidx].phase) {
            case PHASE_REGISTERED:
                if (version == UFTP_V2_VER) {
                    // This is repeated in one of the cases below, but it's
                    // separated out anyway to keep the V2 stuff separate.
                    handle_regconf(listidx, message, meslen);
                } else if (group_list[listidx].keytype != KEY_NONE) {
                    if (*func == KEYINFO) {
                        handle_keyinfo(listidx, buf);
                    } else {
                        log(group_list[listidx].group_id,
                                group_list[listidx].file_id,
                                "Expected KEYINFO, got %s", func_name(*func));
                    }
                } else if (group_list[listidx].keytype == KEY_NONE) {
                    if (*func == REG_CONF) {
                        handle_regconf(listidx, message, meslen);
                    } else if (*func == FILEINFO) {
                        handle_fileinfo(listidx, message, meslen);
                    } else {
                        log(group_list[listidx].group_id,
                                group_list[listidx].file_id,
                                "Expected REG_CONF, got %s", func_name(*func));
                    }
                }
                break;
            case PHASE_MIDGROUP:
                if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
                        (*func == FILEINFO)) {
                    handle_fileinfo(listidx, message, meslen);
                } else if (((version == UFTP_VER_NUM) ||
                            (version == UFTP_3_0_VER)) && (*func == KEYINFO)) {
                    handle_keyinfo(listidx, buf);
                } else if (((version == UFTP_V2_VER) && (v2func == V2_DONE)) ||
                        (((version == UFTP_VER_NUM) ||
                          (version == UFTP_3_0_VER)) && (*func == DONE))) {
                    handle_done(listidx, 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(listidx);
                }
                break;
            case PHASE_RECEIVING:
                if (((version == UFTP_VER_NUM) || (version == UFTP_3_0_VER)) &&
                        (*func == FILEINFO)) {
                    handle_fileinfo(listidx, message, meslen);
                } else if (((version == UFTP_V2_VER) &&
                            (v2func == V2_FILESEG)) ||
                        (((version == UFTP_VER_NUM) ||
                          (version == UFTP_3_0_VER)) && (*func == FILESEG))) {
                    handle_fileseg(listidx, message, meslen);
                } else if (((version == UFTP_V2_VER) && (v2func == V2_DONE)) ||
                        (((version == UFTP_VER_NUM) ||
                          (version == UFTP_3_0_VER)) && (*func == DONE))) {
                    handle_done(listidx, message, meslen);
                }
                break;
            case PHASE_COMPLETE:
                if (((version == UFTP_V2_VER) && (v2func == V2_DONE_CONF)) ||
                        (((version == UFTP_VER_NUM) || 
                          (version == UFTP_3_0_VER)) && (*func == DONE_CONF))) {
                    handle_done_conf(listidx, message, meslen);
                }
                break;
            }
        }
    }
}