Beispiel #1
0
/**
 * Process an KEY_REQ message
 */
void handle_key_req(const union sockaddr_u *src,
                    const unsigned char *packet, unsigned packetlen)
{
    const struct key_req_h *keyreq;
    struct timeval current_timestamp;
    char destname[INET6_ADDRSTRLEN];
    int rval;

    keyreq = (const struct key_req_h *)(packet + sizeof(struct uftp_h));

    if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src),
            destname, sizeof(destname), NULL, 0, NI_NUMERICHOST)) != 0) {
        log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval));
    }
    if ((packetlen < sizeof(struct uftp_h) + (keyreq->hlen * 4U)) ||
            ((keyreq->hlen * 4U) < sizeof(struct key_req_h))) {
        log1(0,0,0,"Rejecting KEY_REQ from %s: invalid message size", destname);
        return;
    }
    log2(0, 0, 0, "Received KEY_REQ from %s", destname);

    gettimeofday(&current_timestamp, NULL);
    if (diff_sec(current_timestamp, last_key_req) > KEY_REQ_LIMIT) {
        send_proxy_key();
    }
}
Beispiel #2
0
/**
 * 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);
}
Beispiel #3
0
/**
 * 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);
}
Beispiel #4
0
/**
 * 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);
}
Beispiel #5
0
/**
 * Sends a KEY_REQ message to the proxy specified as the reply proxy
 */
void send_key_req()
{
    unsigned char *packet;
    struct uftp_h *header;
    struct key_req_h *keyreq;
    union sockaddr_u proxyaddr;
    char addrname[INET6_ADDRSTRLEN];
    int meslen, rval;

    packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct key_req_h), 1);

    header = (struct uftp_h *)packet;
    keyreq = (struct key_req_h *)(packet + sizeof(struct uftp_h));
    header->version = UFTP_VER_NUM;
    header->func = KEY_REQ;
    header->src_id = uid;
    keyreq->func = KEY_REQ;
    keyreq->hlen = sizeof(struct key_req_h) / 4;

    meslen = sizeof(struct uftp_h) + sizeof(struct key_req_h);
    proxyaddr = proxy_info.addr;
    if (nb_sendto(listener, packet, meslen, 0, 
                  (struct sockaddr *)&proxyaddr,
                  family_len(proxyaddr)) == SOCKET_ERROR) {
        sockerror(0, 0, 0, "Error sending KEY_REQ");
    } else {
        if ((rval = getnameinfo((struct sockaddr *)&proxyaddr,
                family_len(proxyaddr), addrname, sizeof(addrname),
                NULL, 0, NI_NUMERICHOST)) != 0) {
            log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval));
        }

        log2(0, 0, 0, "Sent KEY_REQ to %s:%s", addrname, portname);
    }

    free(packet);
    gettimeofday(&next_keyreq_time, NULL);
    next_keyreq_time.tv_sec += KEY_REQ_INT;
}
Beispiel #6
0
/**
 * 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);
}
Beispiel #7
0
/**
 * Sends an HB_RESP in response to an HB_REQ
 */
void send_hb_response(const union sockaddr_u *src, int response)
{
    unsigned char *packet;
    struct uftp_h *header;
    struct hb_resp_h *hbresp;
    char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN];
    int meslen, rval;

    packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct hb_resp_h), 1);

    header = (struct uftp_h *)packet;
    hbresp = (struct hb_resp_h *)(packet + sizeof(struct uftp_h));
    header->version = UFTP_VER_NUM;
    header->func = HB_RESP;
    header->src_id = uid;
    hbresp->func = HB_RESP;
    hbresp->hlen = sizeof(struct hb_resp_h) / 4;
    hbresp->authenticated = response;
    if (response == HB_AUTH_CHALLENGE) {
        hbresp->nonce = htonl(down_nonce);
    }

    meslen = sizeof(struct uftp_h) + sizeof(struct hb_resp_h);
    if (nb_sendto(listener, packet, meslen, 0, (const struct sockaddr *)src,
                  family_len(*src)) == SOCKET_ERROR) {
        sockerror(0, 0, 0, "Error sending HB_RESP");
    } else {
        if ((rval = getnameinfo((const struct sockaddr *)src,
                family_len(*src), destname, sizeof(destname), destport,
                sizeof(destport), NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
            log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval));
        }
        log2(0, 0, 0, "Sent HB_RESP to %s:%s", destname, destport);
    }
    free(packet);
}
Beispiel #8
0
/**
 * 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);
}
Beispiel #9
0
/**
 * 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++;
        }
    }
Beispiel #10
0
/**
 * 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;
    }
}
Beispiel #11
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);
}
Beispiel #12
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);
}
Beispiel #13
0
/**
 * Forward a message unmodified to the next hop, resigning if necessary.
 */
void forward_message(struct pr_group_list_t *group,
                     const union sockaddr_u *src,
                     unsigned char *packet, int packetlen)
{
    struct uftp_h *header;
    struct encrypted_h *encrypted;
    struct announce_h *announce;
    struct enc_info_he *encinfo;
    union sockaddr_u dest;
    unsigned int meslen, siglen;
    int hostidx, rval, iplen, resign;
    char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN];
    uint8_t *sig, *sigcopy;
    union key_t key;

    header = (struct uftp_h *)packet;
    meslen = (unsigned int)packetlen;

    memset(&dest, 0, sizeof(dest));
    if (!memcmp(src, &group->up_addr, sizeof(*src))) {
        if (proxy_type == RESPONSE_PROXY) {
            // Response proxy, no downstream forwarding
            set_timeout(group, 0, 0);
            return;
        } else if (proxy_type == SERVER_PROXY) {
            dest = down_addr;
        } else {
            if (header->func == ANNOUNCE) {
                dest = group->publicmcast;
            } else {
                dest = group->privatemcast;
            }
            key = group->server_pubkey;
        }
    } else {
        dest = group->up_addr;
        if (proxy_type != SERVER_PROXY) {
            hostidx = find_client(group, header->src_id);
            if (hostidx == -1) {
                glog1(group, "Couldn't find receiver in list");
                return;
            }
            key = group->destinfo[hostidx].pubkey;
        }
    }

    // If we're using KEYEX signatures, or sending an ANNOUNCE with ECDH,
    // verify the signature and resign
    resign = 0;
    if ((proxy_type != SERVER_PROXY) && (header->func == ENCRYPTED) &&
            (group->sigtype == SIG_KEYEX)) {
        encrypted = (struct encrypted_h *)(packet + sizeof(struct uftp_h));
        sig = (uint8_t *)encrypted + sizeof(struct encrypted_h);
        siglen = ntohs(encrypted->sig_len);
        resign = 1;
    } else if ((proxy_type != SERVER_PROXY) && (header->func == ANNOUNCE) &&
            ((group->keyextype == KEYEX_ECDH_RSA) ||
             (group->keyextype == KEYEX_ECDH_ECDSA))) {
        announce = (struct announce_h *)(packet + sizeof(struct uftp_h));
        iplen = ((announce->flags & FLAG_IPV6) != 0) ? 16 : 4;
        encinfo = (struct enc_info_he *) ((uint8_t *)announce +
                sizeof(struct announce_h) + iplen + iplen);
        sig = (uint8_t *)encinfo + sizeof(struct enc_info_he) +
                ntohs(encinfo->keylen) + ntohs(encinfo->dhlen);
        siglen = ntohs(encinfo->siglen);
        resign = 1;
    }
    if (resign) {
        sigcopy = safe_calloc(siglen, 1);
        memcpy(sigcopy, sig, siglen);
        memset(sig, 0, siglen);
        if ((group->keyextype == KEYEX_RSA) ||
                (group->keyextype == KEYEX_ECDH_RSA)) {
            if (header->func == ENCRYPTED) {
                if (!verify_RSA_sig(key.rsa, group->hashtype, packet,
                                    meslen, sigcopy, siglen)) {
                    glog1(group, "Signature verification failed");
                    free(sigcopy);
                    return;
                }
            }
            if (!create_RSA_sig(group->proxy_privkey.rsa, group->hashtype,
                                packet, meslen, sigcopy, &siglen)) {
                glog0(group, "Signature creation failed");
                free(sigcopy);
                return;
            }
        } else {
            if (header->func == ENCRYPTED) {
                if (!verify_ECDSA_sig(key.ec, group->hashtype, packet,
                                      meslen, sigcopy, siglen)) {
                    glog1(group, "Signature verification failed");
                    free(sigcopy);
                    return;
                }
            }
            if (!create_ECDSA_sig(group->proxy_privkey.ec, group->hashtype,
                                  packet, meslen, sigcopy, &siglen)) {
                glog0(group, "Signature creation failed");
                free(sigcopy);
                return;
            }
        }
        memcpy(sig, sigcopy, siglen);
        free(sigcopy);
    }

    if (nb_sendto(listener, packet, meslen, 0, (struct sockaddr *)&dest,
               family_len(dest)) == SOCKET_ERROR) {
        gsockerror(group, "Error forwarding message");
        if ((rval = getnameinfo((struct sockaddr *)&dest, family_len(dest),
                destname, sizeof(destname), destport, sizeof(destport),
                NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
            glog1(group, "getnameinfo failed: %s", gai_strerror(rval));
        }
        glog2(group, "Dest: %s:%s", destname, destport);
    }
    set_timeout(group, 0, 0);
}
Beispiel #14
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);
}
Beispiel #15
0
/**
 * 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);
}
Beispiel #16
0
/**
 * 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++;
        }
    }
Beispiel #17
0
/**
 * Process an HB_REQ message
 */
void handle_hb_request(const union sockaddr_u *src,
                       unsigned char *packet, unsigned packetlen)
{
    struct hb_req_h *hbreq;
    unsigned char *keyblob, *sig;
    union key_t key;
    unsigned char fingerprint[HMAC_LEN];
    unsigned int fplen, bloblen, siglen;
    char destname[INET6_ADDRSTRLEN], destport[PORTNAME_LEN];
    int resp, rval;

    hbreq = (struct hb_req_h *)(packet + sizeof(struct uftp_h));

    if ((rval = getnameinfo((const struct sockaddr *)src, family_len(*src),
            destname, sizeof(destname), destport, sizeof(destport),
            NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
        log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval));
    }
    if ((packetlen < sizeof(struct uftp_h) + (hbreq->hlen * 4)) ||
            ((hbreq->hlen * 4) < sizeof(struct hb_req_h))) {
        log1(0,0,0, "Rejecting HB_REQ from %s: invalid message size", destname);
        return;
    }
    log2(0, 0, 0, "Received HB_REQ from %s", destname);
    if ((proxy_type == SERVER_PROXY) && have_down_fingerprint) {
        if (addr_equal(&down_addr, src)) {
            resp = HB_AUTH_OK;
        } else if (down_nonce != ntohl(hbreq->nonce)) {
            resp = HB_AUTH_CHALLENGE;
        } else {
            keyblob = (unsigned char *)hbreq + sizeof(struct hb_req_h);
            bloblen = ntohs(hbreq->bloblen);
            sig = keyblob + bloblen;
            siglen = ntohs(hbreq->siglen);

            // First check key fingerprint, then check signature
            if (keyblob[0] == KEYBLOB_RSA) {
                if (!import_RSA_key(&key.rsa, keyblob, bloblen)) {
                    log1(0, 0, 0, "Failed to import public key from HB_REQ");
                    resp = HB_AUTH_FAILED;
                    goto end;
                } 

                hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen);
                if (memcmp(down_fingerprint, fingerprint, fplen)) {
                    log1(0, 0, 0, "Failed to verify HB_REQ fingerprint");
                    resp = HB_AUTH_FAILED;
                    goto end;
                }

                if (!verify_RSA_sig(key.rsa, HASH_SHA1,
                        (unsigned char *)&hbreq->nonce,
                        sizeof(hbreq->nonce), sig, siglen)) {
                    log1(0, 0, 0, "Failed to verify HB_REQ signature");
                    resp = HB_AUTH_FAILED;
                    goto end;
                }
            } else {
                if (!import_EC_key(&key.ec, keyblob, bloblen, 0)) {
                    log1(0, 0, 0, "Failed to import public key from HB_REQ");
                    resp = HB_AUTH_FAILED;
                    goto end;
                } 

                hash(HASH_SHA1, keyblob, bloblen, fingerprint, &fplen);
                if (memcmp(down_fingerprint, fingerprint, fplen)) {
                    log1(0, 0, 0, "Failed to verify HB_REQ fingerprint");
                    resp = HB_AUTH_FAILED;
                    goto end;
                }

                if (!verify_ECDSA_sig(key.ec, HASH_SHA1,
                        (unsigned char *)&hbreq->nonce,
                        sizeof(hbreq->nonce), sig, siglen)) {
                    log1(0, 0, 0, "Failed to verify HB_REQ signature");
                    resp = HB_AUTH_FAILED;
                    goto end;
                }
            }

            down_addr = *src;
            log2(0, 0, 0, "Using %s:%s as downstream address:port",
                       destname, destport);
            down_nonce = rand32();
            resp = HB_AUTH_OK;
        }
    } else {
        resp = HB_AUTH_OK;
    }

end:
    send_hb_response(src, resp);
}
Beispiel #18
0
/**
 * 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);
}
Beispiel #19
0
/**
 * Sends a PROXY_KEY message to the first listed public multicast address.
 */
void send_proxy_key()
{
    unsigned char *packet, *keyblob, *dhblob, *sig;
    struct uftp_h *header;
    struct proxy_key_h *proxykey;
    uint32_t nonce;
    unsigned int meslen, siglen;
    uint16_t bloblen, dhlen;
    char pubname[INET6_ADDRSTRLEN];
    int rval;

    packet = safe_calloc(sizeof(struct uftp_h) + sizeof(struct hb_req_h) +
                    (PUBKEY_LEN * 3) , 1);

    header = (struct uftp_h *)packet;
    proxykey = (struct proxy_key_h *)(packet + sizeof(struct uftp_h));
    keyblob = (unsigned char *)proxykey + sizeof(struct proxy_key_h);
    header->version = UFTP_VER_NUM;
    header->func = PROXY_KEY;
    header->src_id = uid;
    proxykey->func = PROXY_KEY;
    nonce = htonl(rand32());
    proxykey->nonce = nonce;

    if (privkey_type[0] == KEYBLOB_RSA) {
        if (!export_RSA_key(privkey[0].rsa, keyblob, &bloblen)) {
            log0(0, 0, 0, "Error exporting public key");
            free(packet);
            return;
        }
    } else {
        if (!export_EC_key(privkey[0].ec, keyblob, &bloblen)) {
            log0(0, 0, 0, "Error exporting public key");
            free(packet);
            return;
        }
    }
    dhblob = keyblob + bloblen;
    if (dhkey.key) {
        if (!export_EC_key(dhkey.ec, dhblob, &dhlen)) {
            log0(0, 0, 0, "Error exporting public key");
            free(packet);
            return;
        }
    } else {
        dhlen = 0;
    }
    sig = dhblob + dhlen;
    if (privkey_type[0] == KEYBLOB_RSA) {
        if (!create_RSA_sig(privkey[0].rsa, HASH_SHA1, (unsigned char *)&nonce,
                            sizeof(nonce), sig, &siglen)) {
            log0(0, 0, 0, "Error signing nonce");
            free(packet);
            return;
        }
    } else {
        if (!create_ECDSA_sig(privkey[0].ec, HASH_SHA1, (unsigned char *)&nonce,
                              sizeof(nonce), sig, &siglen)) {
            log0(0, 0, 0, "Error signing nonce");
            free(packet);
            return;
        }
    }
    proxykey->bloblen = htons(bloblen);
    proxykey->dhlen = htons(dhlen);
    proxykey->siglen = htons(siglen);
    proxykey->hlen = (sizeof(struct proxy_key_h) + bloblen + dhlen + siglen)/4;

    meslen = sizeof(struct uftp_h) + (proxykey->hlen * 4);
    if (nb_sendto(listener, packet, meslen, 0, 
                  (struct sockaddr *)&pub_multi[0],
                  family_len(pub_multi[0])) == SOCKET_ERROR) {
        sockerror(0, 0, 0, "Error sending PROXY_KEY");
    } else {
        if ((rval = getnameinfo((struct sockaddr *)&pub_multi[0],
                family_len(pub_multi[0]), pubname, sizeof(pubname), NULL, 0,
                NI_NUMERICHOST)) != 0) {
            log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval));
        }
        log2(0, 0, 0, "Sent PROXY_KEY to %s", pubname);
    }
    free(packet);
}
Beispiel #20
0
/**
 * 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);
}
Beispiel #21
0
/**
 * Process a PROXY_KEY message
 */
void handle_proxy_key(const union sockaddr_u *src,
                      unsigned char *message, unsigned meslen)
{
    struct proxy_key_h *proxykey;
    unsigned char *keyblob, *dhblob, *sig;
    unsigned char fingerprint[HMAC_LEN];
    unsigned int fplen, keylen, dhlen, siglen;
    char addrname[INET6_ADDRSTRLEN];
    int rval;

    proxykey = (struct proxy_key_h *)message;

    if (meslen < (proxykey->hlen * 4U) ||
            ((proxykey->hlen * 4U) < sizeof(struct proxy_key_h) +
                ntohs(proxykey->bloblen) + ntohs(proxykey->dhlen) +
                ntohs(proxykey->siglen))) {
        log2(0, 0, 0, "Rejecting PROXY_KEY: invalid message size");
        return;
    }

    if ((rval = getnameinfo((const struct sockaddr *)src,
            family_len(*src), addrname, sizeof(addrname),
            NULL, 0, NI_NUMERICHOST)) != 0) {
        log1(0, 0, 0, "getnameinfo failed: %s", gai_strerror(rval));
    }
    log2(0, 0, 0, "Received PROXY_KEY from %s", addrname);

    if (!has_proxy) {
        log2(0, 0, 0, "No reply proxy specified");
        return;
    }
    if (!addr_equal(&proxy_info.addr, src)) {
        log2(0, 0, 0, "PROXY_KEY not from specified reply proxy");
        return;
    }

    keyblob = (unsigned char *)proxykey + sizeof(struct proxy_key_h);
    keylen = ntohs(proxykey->bloblen);
    dhblob = keyblob + keylen;
    dhlen = ntohs(proxykey->dhlen);
    sig = dhblob + dhlen;
    siglen = ntohs(proxykey->siglen);

    if (keyblob[0] == KEYBLOB_RSA) {
        if (!import_RSA_key(&proxy_pubkey.rsa, keyblob, keylen)) {
            log0(0, 0, 0, "Failed to import public key from PROXY_KEY");
            return;
        } 
        if (proxy_info.has_fingerprint) {
            hash(HASH_SHA1, keyblob, keylen, fingerprint, &fplen);
            if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) {
                log1(0, 0, 0, "Failed to verify PROXY_KEY fingerprint");
                free_RSA_key(proxy_pubkey.rsa);
                return;
            }
        }
        if (!verify_RSA_sig(proxy_pubkey.rsa, HASH_SHA1,
                            (unsigned char *)&proxykey->nonce,
                            sizeof(proxykey->nonce), sig, siglen)) {
            log1(0, 0, 0, "Failed to verify PROXY_KEY signature");
            free_RSA_key(proxy_pubkey.rsa);
            return;
        }
    } else {
        if (!import_EC_key(&proxy_pubkey.ec, keyblob, keylen, 0)) {
            log0(0, 0, 0, "Failed to import public key from PROXY_KEY");
            return;
        } 
        if (proxy_info.has_fingerprint) {
            hash(HASH_SHA1, keyblob, keylen, fingerprint, &fplen);
            if (memcmp(proxy_info.fingerprint, fingerprint, fplen)) {
                log1(0, 0, 0, "Failed to verify PROXY_KEY fingerprint");
                free_RSA_key(proxy_pubkey.rsa);
                return;
            }
        }
        if (!verify_ECDSA_sig(proxy_pubkey.ec, HASH_SHA1,
                              (unsigned char *)&proxykey->nonce,
                              sizeof(proxykey->nonce), sig, siglen)) {
            log1(0, 0, 0, "Failed to verify PROXY_KEY signature");
            free_RSA_key(proxy_pubkey.rsa);
            return;
        }
    }
    if (dhlen) {
        if (!import_EC_key(&proxy_dhkey.ec, dhblob, dhlen, 1)) {
            log0(0, 0, 0, "Failed to import ECDH public key from PROXY_KEY");
            return;
        } 
    }
}
Beispiel #22
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;
            }
        }
    }
}
Beispiel #23
0
/**
 * Set argument defaults, read and validate command line options
 */
void process_args(int argc, char *argv[])
{
    struct addrinfo ai_hints, *ai_rval;
    int c, i, listidx, rval;
    long tmpval;
    char *p, *p2, *hoststr, *portstr, pubname[INET6_ADDRSTRLEN];
    const char opts[] = "s:crdx:p:t:Q:N:O:U:q:mh:H:g:n:B:L:P:C:S:e:k:K:I:M:";

    set_defaults();
    srand((unsigned int)time(NULL) ^ getpid());

    // read lettered arguments
    while ((c = getopt(argc, argv, opts)) != EOF) {
        switch (c) {
        case 's':
            if (proxy_type != UNDEF_PROXY) {
                fprintf(stderr, "Only one of -s, -c, -r may be specified\n");
                exit(ERR_PARAM);
            }
            proxy_type = SERVER_PROXY;
            memset(&down_addr, 0, sizeof(down_addr));
            if (!strncmp(optarg, "fp=", 3)) {
                have_down_fingerprint =
                    parse_fingerprint(down_fingerprint, optarg + 3);
                if (!have_down_fingerprint) {
                    fprintf(stderr, "Failed to parse downstream fingerprint\n");
                    exit(ERR_PARAM);
                }
                down_nonce = rand32();
            } else {
                have_down_fingerprint = 0;
                memset(&ai_hints, 0, sizeof(ai_hints));
                ai_hints.ai_family = AF_UNSPEC;
                ai_hints.ai_socktype = SOCK_DGRAM;
                ai_hints.ai_protocol = 0;
                ai_hints.ai_flags = 0;
                if ((rval = getaddrinfo(optarg, NULL, &ai_hints,
                                        &ai_rval)) != 0) {
                    fprintf(stderr, "Invalid host name: %s: %s\n",
                            optarg, gai_strerror(rval));
                    exit(ERR_PARAM);
                }
                memcpy(&down_addr, ai_rval->ai_addr, ai_rval->ai_addrlen);
                freeaddrinfo(ai_rval);
            }
            break;
        case 'c':
            if (proxy_type != UNDEF_PROXY) {
                fprintf(stderr, "Only one of -s, -c, -r may be specified\n");
                exit(ERR_PARAM);
            }
            proxy_type = CLIENT_PROXY;
            break;
        case 'r':
            if (proxy_type != UNDEF_PROXY) {
                fprintf(stderr, "Only one of -s, -c, -r may be specified\n");
                exit(ERR_PARAM);
            }
            proxy_type = RESPONSE_PROXY;
            break;
        case 'd':
            debug = 1;
            break;
        case 'x':
            log_level = atoi(optarg);
            if (log_level < 0) {
                fprintf(stderr, "Invalid log level\n");
                exit(ERR_PARAM);
            }
            break;
        case 'p':
            strncpy(portname, optarg, sizeof(portname)-1);
            portname[sizeof(portname)-1] = '\x0';
            port = atoi(portname);
            if (port == 0) {
                fprintf(stderr, "Invalid port\n");
                exit(ERR_PARAM);
            }
            break;
        case 't':
            tmpval = atoi(optarg);
            if ((tmpval <= 0) || (tmpval > 255)) {
                fprintf(stderr, "Invalid ttl\n");
                exit(ERR_PARAM);
            }
            ttl = (char)tmpval;
            break;
        case 'Q':
            tmpval = strtol(optarg, NULL, 0);
            if ((tmpval < 0) || (tmpval > 63)) {
                fprintf(stderr, "Invalid dscp\n");
                exit(ERR_PARAM);
            }
            dscp = (tmpval & 0xFF) << 2;
            break;
        case 'N':
            priority = atoi(optarg);
            if (!valid_priority(priority)) {
                fprintf(stderr, "Invalid priority value\n");
                exit(ERR_PARAM);
            }
            break;
        case 'O':
            if ((listidx = getifbyname(optarg, ifl, ifl_len)) != -1) {
                out_if = ifl[listidx];
                break;
            }
            memset(&ai_hints, 0, sizeof(ai_hints));
            ai_hints.ai_family = AF_UNSPEC;
            ai_hints.ai_socktype = SOCK_DGRAM;
            ai_hints.ai_protocol = 0;
            ai_hints.ai_flags = 0;
            if ((rval = getaddrinfo(optarg, NULL, &ai_hints, &ai_rval)) != 0) {
                fprintf(stderr, "Invalid name/address %s: %s\n",
                        optarg, gai_strerror(rval));
                exit(ERR_PARAM);
            }
            // Just use the first addrinfo entry
            if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr,
                                       ifl, ifl_len)) == -1) {
                fprintf(stderr, "Interface %s not found", optarg);
                exit(ERR_PARAM);
            }
            out_if = ifl[listidx];
            freeaddrinfo(ai_rval);
            break;
        case 'U':
            errno = 0;
            uid = strtoul(optarg, NULL, 16);
            if (errno) {
                perror("Invalid UID\n");
                exit(ERR_PARAM);
            }
            uid = htonl(uid);
            break;
        case 'q':
            strncpy(out_portname, optarg, sizeof(out_portname)-1);
            out_portname[sizeof(out_portname)-1] = '\x0';
            out_port = atoi(out_portname);
            if (out_port == 0) {
                fprintf(stderr, "Invalid outgoing port\n");
                exit(ERR_PARAM);
            }
            break;
        case 'm':
            sys_keys = 1;
            break;
        case 'H':
            p = strtok(optarg, ",");
            while (p != NULL) {
                p2 = strchr(p, ':');
                if (p2) {
                    hoststr = strdup(p);
                    hoststr[p2 - p] = '\x0';
                    portstr = p2 + 1;
                } else {
                    hoststr = p;
                    portstr = NULL;
                }
                memset(&ai_hints, 0, sizeof(ai_hints));
                ai_hints.ai_family = AF_UNSPEC;
                ai_hints.ai_socktype = SOCK_DGRAM;
                ai_hints.ai_protocol = 0;
                ai_hints.ai_flags = 0;
                if ((rval = getaddrinfo(hoststr, portstr ? portstr : DEF_PORT,
                                        &ai_hints, &ai_rval)) != 0) {
                    fprintf(stderr, "Invalid name/address %s: %s\n",
                            hoststr, gai_strerror(rval));
                    exit(ERR_PARAM);
                }
                memcpy(&hb_hosts[hbhost_count++], ai_rval->ai_addr,
                       ai_rval->ai_addrlen);
                p = strtok(NULL, ",");
            }
            break;
        case 'h':
            hb_interval = atoi(optarg);
            if ((hb_interval <= 0) || (hb_interval > 3600)) {
                fprintf(stderr, "Invalid heartbeat interval\n");
                exit(ERR_PARAM);
            }
            break;
        case 'g':
            max_log_size = atoi(optarg);
            if ((max_log_size < 1) || (max_log_size > 1024)) {
                fprintf(stderr, "Invalid max log size\n");
                exit(ERR_PARAM);
            }
            max_log_size *= 1000000;
            break;
        case 'n':
            max_log_count = atoi(optarg);
            if ((max_log_count < 1) || (max_log_count > 1000)) {
                fprintf(stderr, "Invalid max log count\n");
                exit(ERR_PARAM);
            }
            break;
        case 'B':
            rcvbuf = atoi(optarg);
            if ((rcvbuf < 65536) || (rcvbuf > 104857600)) {
                fprintf(stderr, "Invalid buffer size\n");
                exit(ERR_PARAM);
            }
            break;
        case 'L':
            strncpy(logfile, optarg, sizeof(logfile)-1);
            logfile[sizeof(logfile)-1] = '\x0';
            break;
        case 'P':
            strncpy(pidfile, optarg, sizeof(pidfile)-1);
            pidfile[sizeof(pidfile)-1] = '\x0';
            break;
        case 'C':
            add_hosts_by_name(client_fp, &client_fp_count, optarg, 0);
            break;
        case 'S':
            add_hosts_by_name(server_fp, &server_fp_count, optarg, 1);
            break;
        case 'e':
            ecdh_curve = get_curve(optarg);
            if (ecdh_curve == 0) {
                fprintf(stderr, "Invalid curve\n");
                exit(ERR_PARAM);
            }
            break;
        case 'k':
            p = strtok(optarg, ",");
            while (p != NULL) {
                strncpy(keyfile[keyfile_count], p, sizeof(keyfile[0])-1);
                keyfile[keyfile_count][sizeof(keyfile[0])-1] = '\x0';
                keyfile_count++;
                p = strtok(NULL, ",");
            }
            break;
        case 'K':
            p = strtok(optarg, ",");
            while (p != NULL) {
                strncpy(keyinfo[keyinfo_count], p, sizeof(keyinfo[0])-1);
                keyinfo[keyinfo_count][sizeof(keyinfo[0])-1] = '\x0';
                keyinfo_count++;
                p = strtok(NULL, ",");
            }
            break;
        case 'I':
            p = strtok(optarg, ",");
            while (p != NULL) {
                if ((listidx = getifbyname(p, ifl, ifl_len)) != -1) {
                    m_interface[interface_count++] = ifl[listidx];
                    p = strtok(NULL, ",");
                    continue;
                }
                memset(&ai_hints, 0, sizeof(ai_hints));
                ai_hints.ai_family = AF_UNSPEC;
                ai_hints.ai_socktype = SOCK_DGRAM;
                ai_hints.ai_protocol = 0;
                ai_hints.ai_flags = 0;
                if ((rval = getaddrinfo(p, NULL,
                                        &ai_hints, &ai_rval)) != 0) {
                    fprintf(stderr, "Invalid name/address %s: %s\n",
                            p, gai_strerror(rval));
                    exit(ERR_PARAM);
                }
                if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr,
                                           ifl, ifl_len)) == -1) {
                    fprintf(stderr, "Interface %s not found\n", p);
                    exit(ERR_PARAM);
                }
                m_interface[interface_count++] = ifl[listidx];
                freeaddrinfo(ai_rval);
                p = strtok(NULL, ",");
            }
            break;
        case 'M':
            p = strtok(optarg, ",");
            while (p != NULL) {
                memset(&ai_hints, 0, sizeof(ai_hints));
                ai_hints.ai_family = AF_UNSPEC;
                ai_hints.ai_socktype = SOCK_DGRAM;
                ai_hints.ai_protocol = 0;
                ai_hints.ai_flags = 0;
                if ((rval = getaddrinfo(p, NULL,
                                        &ai_hints, &ai_rval)) != 0) {
                    fprintf(stderr, "Invalid multicast address %s: %s\n",
                            p, gai_strerror(rval));
                    exit(ERR_PARAM);
                }
                memcpy(&pub_multi[pub_multi_count], ai_rval->ai_addr,
                       ai_rval->ai_addrlen);
                pub_multi_count++;
                freeaddrinfo(ai_rval);
                p = strtok(NULL, ",");
            }
            break;
        case '?':
            fprintf(stderr, USAGE);
            exit(ERR_PARAM);
        }
    }

    if (proxy_type == UNDEF_PROXY) {
        fprintf(stderr, "Either -s, -c, or -r must be specified\n");
        fprintf(stderr, USAGE);
        exit(ERR_PARAM);
    }
    if (proxy_type == RESPONSE_PROXY) {
        out_port = port;
    }
    if (proxy_type == SERVER_PROXY) {
        if (down_addr.ss.ss_family == AF_INET6) {
            down_addr.sin6.sin6_port = htons(out_port);
        } else {
            down_addr.sin.sin_port = htons(out_port);
        }
    }
    if (proxy_type != CLIENT_PROXY) {
        if (server_fp_count) {
            for (i = 0; i < pub_multi_count; i++) {
                if (!is_multicast(&pub_multi[i], 1)) {
                    if ((rval = getnameinfo((struct sockaddr *)&pub_multi[i],
                                            family_len(pub_multi[i]), pubname, sizeof(pubname),
                                            NULL, 0, NI_NUMERICHOST)) != 0) {
                        fprintf(stderr,"getnameinfo failed: %s",
                                gai_strerror(rval));
                    }
                    fprintf(stderr, "Invalid source specific "
                            "multicast address: %s\n", pubname);
                    exit(ERR_PARAM);
                }
            }
            if (pub_multi_count == 0) {
                fprintf(stderr, "Default multicast address %s invalid "
                        "for source specific multicast\n", DEF_PUB_MULTI);
                exit(ERR_PARAM);
            }
        }
    }
    if ((keyfile_count != 0) && (keyinfo_count != 0) &&
            (keyfile_count != keyinfo_count)) {
        fprintf(stderr, "Must list same number of items for -k and -K\n");
        exit(ERR_PARAM);
    }
    for (i = 0; i < pub_multi_count; i++) {
        if (pub_multi[i].ss.ss_family == AF_INET6) {
            pub_multi[i].sin6.sin6_port = htons(out_port);
        } else {
            pub_multi[i].sin.sin_port = htons(out_port);
        }
    }
}