/** * 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); } } }
/** * 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(int listidx, int hostidx, const unsigned char *message) { struct infoack_h *infoack; struct status_h *status; struct complete_h *complete; const uint8_t *func; struct pr_pending_info_t *pending; int match, pendidx; func = message; infoack = (struct infoack_h *)message; status = (struct status_h *)message; complete = (struct complete_h *)message; for (pendidx = 0; pendidx < MAX_PEND; pendidx++) { pending = &group_list[listidx].pending[pendidx]; if (group_list[listidx].pending[pendidx].msg == 0) { match = 1; break; } match = (*func == pending->msg); switch (*func) { case REGISTER: // REGISTER always matches itself break; case INFO_ACK: // Only in response to FILEINFO. // Responses to KEYINFO are not forwarded. match = match && (ntohs(infoack->file_id) == pending->file_id); break; case STATUS: match = match && ((ntohs(status->file_id) == pending->file_id) && (status->pass == pending->pass) && (ntohs(status->section) == pending->section)); break; case COMPLETE: match = match && ((ntohs(complete->file_id) == pending->file_id) && (complete->status == pending->comp_status)); break; default: log(group_list[listidx].group_id, 0, "Tried to check pending " "on invalid type %s", func_name(*func)); return; } if (match) { break; } } if (!match) { send_all_pending(listidx); pendidx = 0; pending = &group_list[listidx].pending[pendidx]; } pending->msg = *func; if (group_list[listidx].destinfo[hostidx].pending != pendidx) { group_list[listidx].destinfo[hostidx].pending = pendidx; pending->count++; } switch (*func) { case INFO_ACK: if (pending->count == 1) { pending->partial = 1; } pending->file_id = ntohs(infoack->file_id); pending->partial = pending->partial && ((infoack->flags & FLAG_PARTIAL) != 0); break; case STATUS: pending->file_id = ntohs(status->file_id); pending->pass = status->pass; pending->section = ntohs(status->section); pending->seq = group_list[listidx].last_seq; if (!pending->naklist) { pending->naklist = calloc(group_list[listidx].blocksize, 1); if (pending->naklist == NULL) { syserror(0, 0, "calloc failed!"); exit(1); } } if (ntohl(status->nak_count) != 0) { add_naks_to_pending(listidx, pendidx, message); } break; case COMPLETE: pending->file_id = ntohs(complete->file_id); pending->comp_status = complete->status; break; } if (pending->count == max_msg_dest(listidx, *func)) { send_pending(listidx, pendidx); } else { int total_pending, i; for (total_pending = 0, i = 0; i < MAX_PEND; i++) { total_pending += group_list[listidx].pending[i].count; } if (total_pending == 1) { set_timeout(listidx, 1); } } }
/** * 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++; } }