/** * Send out DONE messages specifiying active clients that haven't yet responded. * Returns 1 on success, 0 on fail */ int send_done(const struct finfo_t *finfo, int attempt, int pass, int section) { unsigned char *buf; struct uftp_h *header; struct done_h *done; uint32_t *addrlist; int rval; buf = calloc(mtu, 1); if (buf == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)buf; done = (struct done_h *)(buf + sizeof(struct uftp_h)); addrlist = (uint32_t *)((char *)done + sizeof(struct done_h)); set_uftp_header(header, DONE, finfo, &receive_dest); done->func = DONE; done->file_id = htons(finfo->file_id); done->pass = pass; done->section = htons(section); rval = send_multiple(finfo, buf, DONE, attempt, addrlist, DEST_STATUS, &done->destcount, (keytype != KEY_NONE), &receive_dest, 0); free(buf); return rval; }
/** * Sends an ABORT message downstream to clients */ void send_downstream_abort(struct pr_group_list_t *group, uint32_t dest_id, const char *message, int current) { unsigned char *buf; struct uftp_h *header; struct abort_h *abort_hdr; int payloadlen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, group); header->seq = group->send_seq_down++; header->src_id = uid; abort_hdr->func = ABORT; abort_hdr->hlen = sizeof(struct abort_h) / 4; if ((dest_id == 0) && current) { abort_hdr->flags |= FLAG_CURRENT_FILE; } abort_hdr->host = dest_id; strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1); payloadlen = sizeof(struct uftp_h) + sizeof(struct abort_h); // Proxies should never need to send an encrypted ABORT if (nb_sendto(listener, buf, payloadlen, 0, (struct sockaddr *)&group->privatemcast, family_len(group->privatemcast)) == SOCKET_ERROR) { gsockerror(group, "Error sending ABORT"); } free(buf); }
/** * Send out DONE_CONF messages specifiying all completed clients. * Returns 1 on success, 0 on fail */ int send_doneconf(const struct finfo_t *finfo, int attempt) { unsigned char *buf; struct uftp_h *header; struct doneconf_h *doneconf; uint32_t *addrlist; int rval; if (finfo->file_id != 0) { return 1; } buf = calloc(mtu, 1); if (buf == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)buf; doneconf = (struct doneconf_h *)(buf + sizeof(struct uftp_h)); addrlist = (uint32_t *)((char *)doneconf + sizeof(struct doneconf_h)); set_uftp_header(header, DONE_CONF, finfo, &receive_dest); doneconf->func = DONE_CONF; doneconf->file_id = htons(finfo->file_id); rval = send_multiple(finfo, buf, DONE_CONF, attempt, addrlist, DEST_DONE, &doneconf->destcount, (keytype != KEY_NONE), &receive_dest, 0); free(buf); return rval; }
/** * Sends a KEYINFO_ACK in response to a KEYINFO */ void send_keyinfo_ack(struct group_list_t *group) { unsigned char *buf, *encrypted; struct uftp_h *header; struct keyinfoack_h *keyinfo_ack; unsigned char *verifydata, *verify_hash, *verify_val; unsigned int payloadlen, hashlen; int verifylen, enclen, len; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; keyinfo_ack = (struct keyinfoack_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, KEYINFO_ACK, group); keyinfo_ack->func = KEYINFO_ACK; keyinfo_ack->hlen = sizeof(struct keyinfoack_h) / 4; verifydata = build_verify_data(group, &verifylen); if (!verifydata) { glog0(group, "Error getting verify data"); send_abort(group, "Error getting verify data"); free(buf); return; } verify_hash = safe_calloc(group->hmaclen, 1); verify_val = safe_calloc(VERIFY_LEN + group->hmaclen, 1); hash(group->hashtype, verifydata, verifylen, verify_hash, &hashlen); PRF(group->hashtype, VERIFY_LEN, group->groupmaster, sizeof(group->groupmaster), "client finished", verify_hash, hashlen, verify_val, &len); memcpy(keyinfo_ack->verify_data, verify_val, VERIFY_LEN); free(verifydata); free(verify_hash); free(verify_val); payloadlen = sizeof(struct keyinfoack_h); 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 KEYINFO_ACK"); free(buf); return; } payloadlen = enclen + sizeof(struct uftp_h); if (nb_sendto(listener, encrypted, payloadlen, 0, (struct sockaddr *)&(group->replyaddr), family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending KEYINFO_ACK"); } else { glog2(group, "KEYINFO_ACK sent"); } free(encrypted); free(buf); }
/** * Sends back a CC_ACK message for congestion control feedback */ void send_cc_ack(struct group_list_t *group) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct cc_ack_h *cc_ack; struct tfmcc_ack_info_he *tfmcc; int payloadlen, enclen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; cc_ack = (struct cc_ack_h *)(buf + sizeof(struct uftp_h)); tfmcc = (struct tfmcc_ack_info_he *)((unsigned char *)cc_ack + sizeof(struct cc_ack_h)); set_uftp_header(header, CC_ACK, group); cc_ack->func = CC_ACK; cc_ack->hlen = (sizeof(struct cc_ack_h) + sizeof(struct tfmcc_ack_info_he)) / 4; set_tfmcc_ack_info(group, tfmcc); payloadlen = cc_ack->hlen * 4; if ((group->phase != PHASE_REGISTERED) && (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 CC_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 CC_ACK"); } else { glog2(group, "CC_ACK sent"); } set_timeout(group, 0); group->cc_time.tv_sec = 0; group->cc_time.tv_usec = 0; free(buf); }
/** * Sends an ABORT message to one or more clients */ void send_abort(const struct finfo_t *finfo, const char *message, const struct sockaddr_in *destaddr, const struct in_addr *dest, int encrypt) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct abort_h *abort; int payloadlen; buf = calloc(mtu, 1); if (buf == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)buf; abort = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, finfo, destaddr); abort->func = ABORT; if (dest) { abort->host = dest->s_addr; } strncpy(abort->message, message, sizeof(abort->message) - 1); payloadlen = sizeof(struct abort_h); if (encrypt) { encrypted = NULL; if (!encrypt_and_sign(buf, &encrypted, payloadlen, mtu, keytype, groupkey, groupsalt, ivlen, hashtype, grouphmackey, hmaclen, sigtype, privkey, rsalen)) { log0(0, 0, "Error encrypting ABORT"); free(buf); return; } outpacket = encrypted; payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize); } else { encrypted = NULL; outpacket = buf; header->blsize = htons(payloadlen); } if (nb_sendto(sock, outpacket, payloadlen + sizeof(struct uftp_h), 0, (struct sockaddr *)destaddr, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { sockerror(0, 0, "Error sending ABORT"); } free(buf); free(encrypted); }
/** * Sends an ABORT message to a server */ void send_abort(struct group_list_t *group, const char *message) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct abort_h *abort_hdr; int payloadlen, enclen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, group); abort_hdr->func = ABORT; abort_hdr->hlen = sizeof(struct abort_h) / 4; abort_hdr->host = 0; strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1); payloadlen = sizeof(struct abort_h); if ((group->phase != PHASE_REGISTERED) && (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 ABORT"); 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 ABORT"); } flush_disk_cache(group); file_cleanup(group, 1); free(buf); free(encrypted); }
/** * Sends an ABORT message upstream to a server */ void send_upstream_abort(int listidx, uint32_t addr, const char *message) { unsigned char *buf; struct uftp_h *header; struct abort_h *abort; int payloadlen; buf = calloc(group_list[listidx].mtu, 1); if (buf == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)buf; abort = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, listidx); if (!group_list[listidx].foundaddr) { header->srcaddr = m_interface[0].addr.s_addr; } header->destaddr = group_list[listidx].srcaddr.s_addr; abort->func = ABORT; abort->host = addr; strncpy(abort->message, message, sizeof(abort->message) - 1); header->blsize = htons(sizeof(struct abort_h)); payloadlen = sizeof(struct uftp_h) + sizeof(struct abort_h); // Proxies should never need to send an encrypted ABORT if (nb_sendto(listener, buf, payloadlen, 0, (struct sockaddr *)&group_list[listidx].up_addr, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { sockerror(group_list[listidx].group_id, 0, "Error sending ABORT"); } if (addr == 0) { group_cleanup(listidx); } free(buf); }
/** * Sends an ABORT message downstream to clients */ void send_downstream_abort(int listidx, uint32_t addr, const char *message) { unsigned char *buf; struct uftp_h *header; struct abort_h *abort; struct sockaddr_in sin; int payloadlen; buf = calloc(group_list[listidx].mtu, 1); if (buf == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)buf; abort = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, listidx); header->destaddr = group_list[listidx].multi.s_addr; abort->func = ABORT; abort->host = addr; strncpy(abort->message, message, sizeof(abort->message) - 1); header->blsize = htons(sizeof(struct abort_h)); payloadlen = sizeof(struct uftp_h) + sizeof(struct abort_h); // Proxies should never need to send an encrypted ABORT memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr = group_list[listidx].multi; sin.sin_port = htons(out_port); if (nb_sendto(listener, buf, payloadlen, 0, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { sockerror(group_list[listidx].group_id, 0, "Error sending ABORT"); } free(buf); }
/** * Send out REG_CONF messages specifying all registered clients. * Sent when encryption is disabled, or if the client is behind a proxy. * Returns 1 on success, 0 on fail */ int send_regconf(const struct finfo_t *finfo, int attempt, int do_regconf) { int rval; unsigned char *buf; struct uftp_h *header; struct regconf_h *regconf; uint32_t *idlist; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; regconf = (struct regconf_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, REG_CONF, finfo->group_id, finfo->group_inst, grtt, destcount); regconf->func = REG_CONF; regconf->hlen = sizeof(struct regconf_h) / 4; idlist = (uint32_t *)((uint8_t *)regconf + (regconf->hlen * 4)); rval = send_multiple(finfo, buf, REG_CONF, attempt, idlist, DEST_ACTIVE, 0, &receive_dest, do_regconf); free(buf); return rval; }
/** * Sends an ABORT message upstream to a server */ void send_upstream_abort(struct pr_group_list_t *group, uint32_t addr, const char *message) { unsigned char *buf; struct uftp_h *header; struct abort_h *abort_hdr; int payloadlen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; abort_hdr = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, group); header->seq = group->send_seq_up++; header->src_id = uid; abort_hdr->func = ABORT; abort_hdr->hlen = sizeof(struct abort_h) / 4; abort_hdr->flags = 0; abort_hdr->host = addr; strncpy(abort_hdr->message, message, sizeof(abort_hdr->message) - 1); payloadlen = sizeof(struct uftp_h) + sizeof(struct abort_h); // Proxies should never need to send an encrypted ABORT if (nb_sendto(listener, buf, payloadlen, 0, (struct sockaddr *)&group->up_addr, family_len(group->up_addr)) == SOCKET_ERROR) { gsockerror(group, "Error sending ABORT"); } if (addr == 0) { group_cleanup(group); } free(buf); }
/** * Sends a KEYINFO to each client that the server sent a REG_CONF for. */ void send_keyinfo(struct pr_group_list_t *group, const uint32_t *addrlist, int addrlen) { unsigned char *buf, *iv; struct uftp_h *header; struct keyinfo_h *keyinfo_hdr; struct destkey *keylist; unsigned int payloadlen, len; int maxdest, packetcnt, dests, iv_init, foundaddr, i, j; int unauth_keytype, unauth_keylen, unauth_ivlen; struct pr_destinfo_t *dest; // Don't use a cipher in an authentication mode to encrypt the group master unauth_keytype = unauth_key(group->keytype); get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen); buf = safe_calloc(MAXMTU, 1); iv = safe_calloc(unauth_ivlen, 1); header = (struct uftp_h *)buf; keyinfo_hdr = (struct keyinfo_h *)(buf + sizeof(struct uftp_h)); keylist= (struct destkey *)((char *)keyinfo_hdr + sizeof(struct keyinfo_h)); set_uftp_header(header, KEYINFO, group); keyinfo_hdr->func = KEYINFO; keyinfo_hdr->hlen = sizeof(struct keyinfo_h) / 4; iv_init = 0; maxdest = max_msg_dest(group, KEYINFO, keyinfo_hdr->hlen * 4); packetcnt = 1; for (i = 0, dests = 0; i < group->destcount; i++) { dest = &group->destinfo[i]; if (dest->state == PR_CLIENT_CONF) { if (addrlist) { // We just got a REG_CONF, so only send to listed hosts for (j = 0, foundaddr = 0; (j < addrlen) && (!foundaddr); j++) { if (dest->id == addrlist[j]) { foundaddr = 1; } } } else { foundaddr = 1; } if (foundaddr) { if (!iv_init) { group->ivctr++; keyinfo_hdr->iv_ctr_hi = htonl((group->ivctr & 0xFFFFFFFF00000000LL) >> 32); keyinfo_hdr->iv_ctr_lo = htonl(group->ivctr & 0x00000000FFFFFFFFLL); iv_init = 1; } keylist[dests].dest_id = dest->id; build_iv(iv, dest->salt, unauth_ivlen, uftp_htonll(group->ivctr), group->src_id); if (!encrypt_block(unauth_keytype, iv, dest->key, NULL, 0, &group->groupmaster[1], sizeof(group->groupmaster) - 1, keylist[dests].groupmaster, &len)) { glog0(group, "Error encrypting KEYINFO for %s", dest->name); free(buf); free(iv); return; } dests++; } } if ((dests >= maxdest) || ((i == group->destcount - 1) && (dests > 0))) { payloadlen = sizeof(struct keyinfo_h) + (dests * sizeof(struct destkey)); glog2(group,"Sending KEYINFO %d.%d", group->keyinfo_cnt, packetcnt); if (nb_sendto(listener, buf, payloadlen + sizeof(struct uftp_h), 0, (struct sockaddr *)&group->privatemcast, family_len(group->privatemcast)) == SOCKET_ERROR) { gsockerror(group, "Error sending KEYINFO"); free(buf); free(iv); return; } // TODO: This value is good for around 100Mbps. This is under the // assumption that the client proxy is local to the clients // it serves. This should probably be a parameter. usleep(120); memset(keylist, 0, maxdest * sizeof(struct destkey)); iv_init = 0; dests = 0; packetcnt++; } }
/** * Sends back a COMPLETE message in response to a DONE or FILEINFO */ void send_complete(struct group_list_t *group, int set_freespace) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct complete_h *complete; struct freespace_info_he *freespace; int payloadlen, enclen; struct timeval tv; gettimeofday(&tv, NULL); if ((group->phase == PHASE_COMPLETE) && (cmptimestamp(tv, group->expire_time) >= 0)) { glog1(group, "Completion unconfirmed by server"); move_files(group); file_cleanup(group, 0); return; } buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; complete = (struct complete_h *)(buf + sizeof(struct uftp_h)); freespace = (struct freespace_info_he *)((unsigned char *)complete + sizeof(struct complete_h)); set_uftp_header(header, COMPLETE, group); complete->func = COMPLETE; if (set_freespace) { complete->hlen = (sizeof(struct complete_h) + sizeof(struct freespace_info_he)) / 4; } else { complete->hlen = sizeof(struct complete_h) / 4; } complete->status = group->fileinfo.comp_status; complete->file_id = htons(group->file_id); if (set_freespace) { set_freespace_info(group, freespace); } payloadlen = complete->hlen * 4; if ((group->phase != PHASE_REGISTERED) && (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 COMPLETE"); 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 COMPLETE"); } else { glog2(group, "COMPLETE sent"); } set_timeout(group, 0); free(buf); free(encrypted); }
/** * Sends back a STATUS message with the given NAK list */ void send_status(struct group_list_t *group, unsigned int section, const unsigned char *naks, unsigned int nak_count) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct status_h *status; struct tfmcc_ack_info_he *tfmcc; unsigned char *sent_naks; int payloadlen, enclen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; status = (struct status_h *)(buf + sizeof(struct uftp_h)); tfmcc = (struct tfmcc_ack_info_he *)((unsigned char *)status + sizeof(struct status_h)); set_uftp_header(header, STATUS, group); status->func = STATUS; if (group->cc_type == CC_TFMCC) { status->hlen = (sizeof(struct status_h) + sizeof(struct tfmcc_ack_info_he)) / 4; } else { status->hlen = sizeof(struct status_h) / 4; } status->file_id = htons(group->file_id); status->section = htons(section); if (section >= group->fileinfo.big_sections) { payloadlen = (group->fileinfo.secsize_small / 8) + 1; } else { payloadlen = (group->fileinfo.secsize_big / 8) + 1; } if (group->cc_type == CC_TFMCC) { set_tfmcc_ack_info(group, tfmcc); } sent_naks = (unsigned char *)status + (status->hlen * 4); memcpy(sent_naks, naks, payloadlen); payloadlen += status->hlen * 4; if ((group->phase != PHASE_REGISTERED) && (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 STATUS"); 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 STATUS"); } else { glog2(group, "Sent %d NAKs for section %d", nak_count, section); } free(buf); free(encrypted); }
/** * Send a KEYINFO message. Sent during the Announce phase for a group * with encryption enabled. * Returns 1 on success, 0 on fail. */ int send_keyinfo(const struct finfo_t *finfo, int attempt) { unsigned char *buf, *iv; struct uftp_h *header; struct keyinfo_h *keyinfo; struct destkey *keylist; unsigned int hsize, payloadlen, len; int maxdest, packetcnt, dests, iv_init, i; int unauth_keytype, unauth_keylen, unauth_ivlen; // Don't use a cipher in an authentication mode to encrypt the group master unauth_keytype = unauth_key(keytype); get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen); buf = safe_calloc(MAXMTU, 1); iv = safe_calloc(unauth_ivlen, 1); header = (struct uftp_h *)buf; keyinfo = (struct keyinfo_h *)(buf + sizeof(struct uftp_h)); keylist = (struct destkey *)((char *)keyinfo + sizeof(struct keyinfo_h)); set_uftp_header(header, KEYINFO, finfo->group_id, finfo->group_inst, grtt, destcount); keyinfo->func = KEYINFO; keyinfo->hlen = sizeof(struct keyinfo_h) / 4; keylist = (struct destkey *)((uint8_t *)keyinfo + (keyinfo->hlen * 4)); iv_init = 0; hsize = sizeof(struct keyinfo_h); maxdest = blocksize / sizeof(struct destkey); packetcnt = 1; for (i = 0, dests = 0; i < destcount; i++) { if (destlist[i].status == DEST_REGISTERED) { if (!iv_init) { ivctr++; keyinfo->iv_ctr_hi =htonl((ivctr & 0xFFFFFFFF00000000LL) >> 32); keyinfo->iv_ctr_lo = htonl(ivctr & 0x00000000FFFFFFFFLL); iv_init = 1; } keylist[dests].dest_id = destlist[i].id; build_iv(iv, destlist[i].encinfo->salt, unauth_ivlen, uftp_htonll(ivctr), header->src_id); if (!encrypt_block(unauth_keytype, iv,destlist[i].encinfo->key, NULL,0, &groupmaster[1], sizeof(groupmaster) - 1, keylist[dests].groupmaster, &len)) { glog0(finfo, "Error encrypting KEYINFO for %s", destlist[i].name); free(buf); free(iv); return 0; } dests++; } if ((dests >= maxdest) || ((i == destcount - 1) && (dests > 0))) { header->seq = htons(send_seq++); payloadlen = hsize + (dests * sizeof(struct destkey)); glog2(finfo, "Sending KEYINFO %d.%d", attempt, packetcnt); if (nb_sendto(sock, buf, payloadlen + sizeof(struct uftp_h), 0, (struct sockaddr *)&receive_dest, family_len(receive_dest)) == SOCKET_ERROR) { gsockerror(finfo, "Error sending KEYINFO"); sleep(1); free(buf); free(iv); return 0; } if (packet_wait) usleep(packet_wait); memset(keylist, 0, maxdest * sizeof(struct destkey)); iv_init = 0; dests = 0; packetcnt++; } }
/** * Send the ANNOUNCE message * For open group membership, just send one. For closed group membership, * list as many destinations as will fit and send multiple packets so that * each receiver is listed. * Returns 1 on success, 0 on fail. */ int send_announce(const struct finfo_t *finfo, int attempt, int open) { int packetlen, rval, iplen, extlen; unsigned char *buf; struct uftp_h *header; struct announce_h *announce; unsigned char *publicaddr, *privateaddr; struct enc_info_he *encinfo; struct timeval tv; uint32_t *idlist; buf = safe_calloc(MAXMTU, 1); if (listen_dest.ss.ss_family == AF_INET6) { iplen = sizeof(struct in6_addr); } else { iplen = sizeof(struct in_addr); } header = (struct uftp_h *)buf; announce = (struct announce_h *)(buf + sizeof(struct uftp_h)); publicaddr = (unsigned char *)announce + sizeof(struct announce_h); privateaddr = publicaddr + iplen; encinfo = (struct enc_info_he *)(privateaddr + iplen); set_uftp_header(header, ANNOUNCE, finfo->group_id, finfo->group_inst, grtt, destcount); announce->func = ANNOUNCE; if (sync_mode) { announce->flags |= FLAG_SYNC_MODE; if (sync_preview) { announce->flags |= FLAG_SYNC_PREVIEW; } } announce->robust = robust; announce->cc_type = cc_type; announce->blocksize = htons(blocksize); gettimeofday(&tv, NULL); announce->tstamp_sec = htonl(tv.tv_sec); announce->tstamp_usec = htonl(tv.tv_usec); if (!is_multicast(&listen_dest, 0)) { memset(publicaddr, 0, iplen); memset(privateaddr, 0, iplen); } else if (listen_dest.ss.ss_family == AF_INET6) { memcpy(publicaddr, &listen_dest.sin6.sin6_addr.s6_addr, iplen); memcpy(privateaddr, &receive_dest.sin6.sin6_addr.s6_addr, iplen); } else { memcpy(publicaddr, &listen_dest.sin.sin_addr.s_addr, iplen); memcpy(privateaddr, &receive_dest.sin.sin_addr.s_addr, iplen); } if (listen_dest.ss.ss_family == AF_INET6) { announce->flags |= FLAG_IPV6; } if (keytype != KEY_NONE) { extlen = set_enc_info(finfo, encinfo); if (extlen == 0) { glog0(finfo, "Error setting up EXT_ENC_INFO"); free(buf); return 0; } announce->hlen = (sizeof(struct announce_h) + iplen + iplen + extlen) / 4; } else { announce->hlen = (sizeof(struct announce_h) + iplen + iplen) / 4; } idlist = (uint32_t *)((uint8_t *)announce + (announce->hlen * 4)); if (open) { header->seq = htons(send_seq++); packetlen = sizeof(struct uftp_h) + (announce->hlen * 4); if (!sign_announce(finfo, buf, packetlen)) { glog0(finfo, "Error signing ANNOUNCE"); free(buf); return 0; } glog2(finfo, "Sending ANNOUNCE %d", attempt); if (nb_sendto(sock, buf, packetlen, 0, (struct sockaddr *)&listen_dest, family_len(listen_dest)) == SOCKET_ERROR) { gsockerror(finfo, "Error sending ANNOUNCE"); // So we don't spin our wheels... sleep(1); free(buf); return 0; } free(buf); return 1; } else { rval = send_multiple(finfo, buf, ANNOUNCE, attempt, idlist, DEST_MUTE, 0, &listen_dest, 0); free(buf); return rval; } }
/** * Sends an ABORT message to a server */ void send_abort(int listidx, const char *message) { unsigned char *buf, *encrypted, *outpacket; struct uftp_h *header; struct abort_h *abort; struct uftp2_h *v2_header; char *v2_message; int payloadlen; buf = calloc(group_list[listidx].mtu, 1); if (buf == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } if (group_list[listidx].version == UFTP_V2_VER) { v2_header = (struct uftp2_h *)buf; v2_message = (char *)v2_header + sizeof(struct uftp2_h); v2_header->uftp_id = htonl(V2_UFTP_ID); v2_header->func = htonl(V2_ABORT); v2_header->tx_id = htonl(group_list[listidx].group_id); v2_header->blsize = htonl(strlen(message)); payloadlen = sizeof(struct uftp2_h) + strlen(message); strncpy(v2_message, message, V2_BLOCKSIZE - 1); encrypted = NULL; outpacket = buf; } else { header = (struct uftp_h *)buf; abort = (struct abort_h *)(buf + sizeof(struct uftp_h)); set_uftp_header(header, ABORT, listidx); abort->func = ABORT; abort->host = 0; strncpy(abort->message, message, sizeof(abort->message) - 1); payloadlen = sizeof(struct abort_h); if ((group_list[listidx].phase != PHASE_REGISTERED) && (group_list[listidx].keytype != KEY_NONE)) { encrypted = NULL; if (!encrypt_and_sign(buf, &encrypted, payloadlen, 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].clientkey, group_list[listidx].client_keylen)) { log0(0, 0, "Error encrypting ABORT"); free(buf); return; } outpacket = encrypted; payloadlen = ntohs(((struct uftp_h *)outpacket)->blsize); } else { encrypted = NULL; outpacket = buf; header->blsize = htons(payloadlen); } payloadlen += sizeof(struct uftp_h); } if (nb_sendto(listener, outpacket, payloadlen, 0, (struct sockaddr *)&group_list[listidx].replyaddr, sizeof(struct sockaddr_in)) == SOCKET_ERROR) { sockerror(group_list[listidx].group_id, group_list[listidx].file_id, "Error sending ABORT"); } file_cleanup(listidx, 1); free(buf); free(encrypted); }
/** * Performs the Transfer phase for a particular file * Returns 1 if at least one client finished, 0 if all are dropped or aborted */ int transfer_phase(struct finfo_t *finfo) { unsigned char *packet, *encpacket, *data; char path[MAXPATHNAME]; struct uftp_h *header; struct fileseg_h *fileseg; int max_time, alldone, numbytes, sent_blocks, current_naks; unsigned int pass, section, numnaks, block; struct timeval start_time, last_sent, current_sent; int64_t avgwait, waitcnt, overage, tdiff; f_offset_t offset, curr_offset; int file, i; if (finfo->file_id != 0) { // First check to see if all clients are already done for this file. // This can happen on a restart when the file finished on the // last attempt and responded to the FILEINFO with a COMPLETE for (i = 0, alldone = 1; (i < destcount) && alldone; i++) { alldone = alldone && ((destlist[i].status == DEST_DONE) || (client_error(i)) || (destlist[i].clientcnt != -1)); } if (alldone) { gettimeofday(&start_time, NULL); print_status(finfo, start_time); return 1; } } // If rate is -1, use 100Mbps for purpose of calculating max time max_time = (int)floor(((double)weight / 100) * ((double)finfo->size / (((rate == -1) ? 100000 : rate) / 8) / 1024)); if (max_time < min_time) { max_time = min_time; } if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) { log(0, 0, "Maximum file transfer time: %d seconds", max_time); snprintf(path, sizeof(path), "%s%c%s", finfo->basedir, PATH_SEP, finfo->filename); if ((file = open(path, OPENREAD, 0)) == -1) { syserror(0, 0, "Error opening file"); return 1; } } else { // At end of group, all non-errored client are DEST_DONE from the // last file, so reset them to DEST_ACTIVE to get the final COMPLETE. for (i = 0; i < destcount; i++) { if (!client_error(i)) { destlist[i].status = DEST_ACTIVE; } } } packet = calloc(mtu, 1); encpacket = calloc(mtu, 1); if ((packet == NULL) || (encpacket == NULL)) { syserror(0, 0, "calloc failed!"); exit(1); } header = (struct uftp_h *)packet; fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h)); data = (unsigned char *)fileseg + sizeof(struct fileseg_h); set_uftp_header(header, FILESEG, finfo, &receive_dest); pass = 1; alldone = 0; gettimeofday(&start_time, NULL); do { avgwait = 0; waitcnt = 0; numnaks = 0; overage = 0; section = 1; curr_offset = 0; sent_blocks = 0; gettimeofday(&last_sent, NULL); if (finfo->file_id != 0) { log(0, 0, "Sending file...pass %d", pass); lseek_func(file, 0, SEEK_SET); } else { log(0, 0, "Finishing group"); } fileseg->func = FILESEG; fileseg->file_id = htons(finfo->file_id); fileseg->pass = pass; fileseg->section = htons(section); for (block = 0; block < finfo->blocks; block++) { // If all clients received this file partially on a prior attempt // and it's the first pass, request NAKs for all sections // right away and don't send any data packets. if (((pass == 1) || finfo->naklist[block]) && !((pass == 1) && finfo->partial)) { if (diff_sec(last_sent, start_time) > max_time) { log0(0, 0, "Max file transfer time exceeded"); send_abort(finfo, "Max file transfer time exceeded", &receive_dest, NULL, (keytype != KEY_NONE), !quit_on_error); alldone = 1; for (i = 0; i < destcount; i++) { if (quit_on_error || ((destlist[i].status == DEST_ACTIVE) && destlist[i].clientcnt == -1 )) { destlist[i].status = DEST_ABORT; } } break; } // On the first pass, go straight through the file. // On later passes, seek to the next packet. if (pass != 1) { log4(0, 0, "Resending %d", block); if (!seek_block(file, block, &offset, curr_offset)) { continue; } } if ((numbytes = read(file, data, blocksize)) == -1) { syserror(0, 0, "read failed"); continue; } if (pass != 1) { curr_offset = offset + numbytes; } // Keep track of how long we really slept compared to how // long we expected to sleep. If we went over, subtract the // time over from the next sleep time. This way we maintain // the proper average sleep time. This can result in multiple // packets going out at once, potentially losing packets. if (packet_wait > overage) { usleep(packet_wait - (int32_t)overage); } gettimeofday(¤t_sent, NULL); tdiff = diff_usec(current_sent, last_sent); avgwait += tdiff; waitcnt++; if (packet_wait) overage += tdiff - packet_wait; last_sent = current_sent; fileseg->seq_num = htonl(block); send_data(finfo, packet, numbytes, encpacket); sent_blocks++; } if ((block % (blocksize * 8) == (blocksize * 8) - 1) || (block == finfo->blocks - 1)) { if ((pass == 1) || sent_blocks) { current_naks = get_naks(finfo, pass, section, &alldone); numnaks += current_naks; if ((rate != -1) && (cc_count > 0)) { if (!read_cc_config(cc_config)) { log1(0, 0, "Error rereading congestion control " "config, using prior values"); } adjust_rate(current_naks, sent_blocks); } overage = 0; if (alldone) break; } sent_blocks = 0; gettimeofday(&last_sent, NULL); fileseg->section = htons(++section); } } if ((finfo->size == 0) && !alldone) { // If it's the end of the group, or an empty file, a DONE was // never sent, so send it now numnaks += get_naks(finfo, pass, section, &alldone); } if (finfo->file_id != 0) { log(0, 0, "Average wait time = %.2f us", (waitcnt == 0) ? 0 : (float)avgwait / waitcnt); log(0, 0, "Received %d distinct NAKs for pass %d", numnaks, pass); } pass++; } while (!alldone); if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) { close(file); } print_status(finfo, start_time); free(packet); free(encpacket); for (i = 0; i < destcount; i++) { if (quit_on_error) { // Check to see that all finished if ((destlist[i].status != DEST_DONE) && (destlist[i].clientcnt == -1)) { return 0; } } else { // Check to see if at least one finished if (destlist[i].status == DEST_DONE) { return 1; } } } if (quit_on_error) { return 1; } else { return 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); }
/** * 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); }
/** * Sends a CLIENT_KEY message if the server requested it. * Always sent right after a REGISTER. */ void send_client_key(struct group_list_t *group) { struct uftp_h *header; struct client_key_h *client_key; unsigned char *buf, *keyblob, *verify; uint8_t *verifydata; unsigned int siglen, meslen; int verifylen; uint16_t bloblen; buf = safe_calloc(MAXMTU, 1); header = (struct uftp_h *)buf; client_key = (struct client_key_h *)(buf + sizeof(struct uftp_h)); keyblob = (unsigned char *)client_key + sizeof(struct client_key_h); verifydata = build_verify_data(group, &verifylen); if (!verifydata) { glog0(group, "Error getting verify data"); send_abort(group, "Error getting verify data"); goto end; } set_uftp_header(header, CLIENT_KEY, group); client_key->func = CLIENT_KEY; if ((group->keyextype == KEYEX_RSA) || (group->keyextype == KEYEX_ECDH_RSA)) { if (!export_RSA_key(group->client_privkey.rsa, keyblob, &bloblen)) { glog0(group, "Error exporting public key"); send_abort(group, "Error exporting public key"); goto end; } verify = keyblob + bloblen; if (!create_RSA_sig(group->client_privkey.rsa, group->hashtype, verifydata, verifylen, verify, &siglen)) { glog0(group, "Error signing verify data"); send_abort(group, "Error signing verify data"); goto end; } } else { if (!export_EC_key(group->client_privkey.ec, keyblob, &bloblen)) { glog0(group, "Error exporting public key"); send_abort(group, "Error exporting public key"); goto end; } verify = keyblob + bloblen; if (!create_ECDSA_sig(group->client_privkey.ec, group->hashtype, verifydata, verifylen, verify, &siglen)) { glog0(group, "Error signing verify data"); send_abort(group, "Error signing verify data"); goto end; } } client_key->bloblen = htons(bloblen); client_key->siglen = htons(siglen); client_key->hlen = (sizeof(struct client_key_h) + bloblen + siglen) / 4; meslen = sizeof(struct uftp_h) + (client_key->hlen * 4); if (nb_sendto(listener, buf, meslen, 0, (struct sockaddr *)&(group->replyaddr), family_len(group->replyaddr)) == SOCKET_ERROR) { gsockerror(group, "Error sending CLIENT_KEY"); } else { glog2(group, "CLIENT_KEY sent"); } end: free(verifydata); free(buf); }