/** * 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; } } } }
/** * 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; } } } }