/** * * @param ccnl * @param content * @param content_len * @param sig * @param sig_len * @param callback function which should be called when crypto system returns * for a new callback function you have to extend ccnl_crypto()!!!! * @return */ int ccnl_crypto_verify(struct ccnl_relay_s *ccnl, char *content, int content_len, char *sig, int sig_len, char* callback, int sequnum) { char *msg = 0; int len = 0, ret = 0; struct ccnl_buf_s *retbuf; //int plen; //unsigned char *buf; plen = 0; memset(buf,0,sizeof(buf)); if(!ccnl->crypto_face) return ret; msg = (char *)ccnl_malloc(sizeof(char)*(content_len+sig_len)+3000); len = ccnl_crypto_create_ccnl_sign_verify_msg("verify", sequnum, content, content_len, sig, sig_len, msg, callback); if(len > CCNL_MAX_PACKET_SIZE){ DEBUGMSG(DEBUG,"Ignored, packet size too large"); return 0; } //send ccn_msg to crytoserver retbuf = ccnl_buf_new((char *)msg, len); ccnl_face_enqueue(ccnl, ccnl->crypto_face, retbuf); if(msg) ccnl_free(msg); return ret; }
void ccnl_interest_propagate(struct ccnl_relay_s *ccnl, struct ccnl_interest_s *i) { struct ccnl_forward_s *fwd; DEBUGMSG(99, "ccnl_interest_propagate\n"); // CONFORM: "A node MUST implement some strategy rule, even if it is only to // transmit an Interest Message on all listed dest faces in sequence." // CCNL strategy: we forward on all FWD entries with a prefix match int forward_cnt = 0; for (fwd = ccnl->fib; fwd; fwd = fwd->next) { int rc = ccnl_prefix_cmp(fwd->prefix, NULL, i->prefix, CMP_LONGEST); DEBUGMSG(40, " ccnl_interest_propagate, rc=%d/%d\n", rc, fwd->prefix->compcnt); if (rc < fwd->prefix->compcnt) { continue; } DEBUGMSG(40, " ccnl_interest_propagate, fwd==%p\n", (void *) fwd); // suppress forwarding to origin of interest, except wireless if (!i->from || fwd->face != i->from || (i->from->flags & CCNL_FACE_FLAGS_REFLECT)) { i->forwarded_over = fwd; fwd->face->stat.send_interest[i->retries]++; ccnl_get_timeval(&i->last_used); ccnl_face_enqueue(ccnl, fwd->face, buf_dup(i->pkt)); ccnl_get_timeval(&fwd->last_used); forward_cnt++; } } if (forward_cnt == 0) { DEBUGMSG(40, " ccnl_interest_propagate: using broadcast face!\n"); ccnl->ifs[RIOT_TRANS_IDX].broadcast_face->stat.send_interest[i->retries]++; ccnl_get_timeval(&i->last_used); ccnl_face_enqueue(ccnl, ccnl->ifs[RIOT_TRANS_IDX].broadcast_face, buf_dup(i->pkt)); } return; }
// deliver new content c to all clients with (loosely) matching interest, // but only one copy per face // returns: number of forwards int ccnl_content_serve_pending(struct ccnl_relay_s *ccnl, struct ccnl_content_s *c, struct ccnl_face_s *from) { struct ccnl_interest_s *i; struct ccnl_face_s *f; int cnt = 0; DEBUGMSG(99, "ccnl_content_serve_pending\n"); for (f = ccnl->faces; f; f = f->next) { f->flags &= ~CCNL_FACE_FLAGS_SERVED; // reply on a face only once } for (i = ccnl->pit; i;) { struct ccnl_pendint_s *pi; if (!ccnl_i_prefixof_c(i->prefix, i->ppkd, i->minsuffix, i->maxsuffix, c)) { i = i->next; continue; } // CONFORM: "Data MUST only be transmitted in response to // an Interest that matches the Data." for (pi = i->pending; pi; pi = pi->next) { if (pi->face->flags & CCNL_FACE_FLAGS_SERVED) { continue; } if (pi->face == from) { // the existing pending interest is from the same face // as the newly arrived content is...no need to send content back DEBUGMSG(1, " detected looping content, before loop could happen\n"); continue; } pi->face->flags |= CCNL_FACE_FLAGS_SERVED; DEBUGMSG(6, " forwarding content <%s>\n", ccnl_prefix_to_path(c->name)); pi->face->stat.send_content[c->served_cnt % CCNL_MAX_CONTENT_SERVED_STAT]++; ccnl_face_enqueue(ccnl, pi->face, buf_dup(c->pkt)); c->served_cnt++; ccnl_get_timeval(&c->last_used); cnt++; } i = ccnl_interest_remove(ccnl, i); } return cnt; }
int ccnl_mgmt_crypto(struct ccnl_relay_s *ccnl, char *type, unsigned char *buf, int buflen) { struct ccnl_face_s *from; DEBUGMSG(DEBUG,"ccnl_crypto type: %s\n", type); if(!strcmp(type, "verify")){ int seqnum = 0; int verified = ccnl_crypto_extract_verify_reply(&buf, &buflen, &seqnum); unsigned char *msg, *msg2; char cmd[500]; int len = ccnl_crypto_extract_msg(&buf, &buflen, &msg), len2 = 0; struct ccnl_face_s *from; //DEBUGMSG(DEBUG,"VERIFIED: %d, MSG_LEN: %d\n", verified, len); int scope=3, aok=3, minsfx=0, maxsfx=CCNL_MAX_NAME_COMP, contlen; struct ccnl_buf_s *buf1 = 0, *nonce=0, *ppkd=0; struct ccnl_prefix_s *p = 0; struct ccnl_buf_s *msg2_buf; unsigned char *content = 0; msg2 = (char *) ccnl_malloc(sizeof(char) * len + 200); len2 = ccnl_ccnb_mkHeader(msg2,CCN_DTAG_NAME, CCN_TT_DTAG); memcpy(msg2+len2, msg, len); len2 +=len; msg2[len2++] = 0; from = ccnl->faces; while(from){ if(from->faceid == seqnum) break; from = from->next; } buf1 = ccnl_ccnb_extract(&msg2, &len2, &scope, &aok, &minsfx, &maxsfx, &p, &nonce, &ppkd, &content, &contlen); if (p->complen[2] < sizeof(cmd)) { memcpy(cmd, p->comp[2], p->complen[2]); cmd[p->complen[2]] = '\0'; } else strcpy(cmd, "cmd-is-too-long-to-display"); msg2_buf = ccnl_buf_new((char *)msg2, len2); ccnl_mgmt_handle(ccnl, msg2_buf, p, from, cmd, verified); ccnl_free(msg2_buf); }else if(!strcmp(type, "sign")){ char *sig = (char *) ccnl_malloc(sizeof(char)* CCNL_MAX_PACKET_SIZE); unsigned char *out; unsigned char *msg; int siglen = 0, seqnum = 0, len, len1; struct ccnl_buf_s *retbuf; ccnl_crypto_extract_sign_reply(&buf, &buflen, sig, &siglen, &seqnum); len = ccnl_crypto_extract_msg(&buf, &buflen, &msg); out = (char *) ccnl_malloc(sizeof(unsigned char)*len + sizeof(unsigned char)*siglen + 4096); len1 = ccnl_ccnb_mkHeader(out, CCN_DTAG_CONTENTOBJ, CCN_TT_DTAG); // content if(siglen > 0) len1 += ccnl_crypto_add_signature(out+len1, sig, siglen); memcpy(out+len1, msg, len); len1 +=len; out[len1++] = 0; // end-of-interest from = ccnl->faces; while(from){ if(from->faceid == seqnum) break; from = from->next; } retbuf = ccnl_buf_new((char *)out, len1); if(seqnum >= 0){ ccnl_face_enqueue(ccnl, from, retbuf); }else{ struct ccnl_prefix_s *prefix_a = 0; struct ccnl_content_s *c = 0; struct ccnl_buf_s *nonce=0, *ppkd=0, *pkt = 0; unsigned char *content = 0; char *ht = (char *) ccnl_malloc(sizeof(char)*20); int contlen; pkt = ccnl_ccnb_extract(&out, &len1, 0, 0, 0, 0, &prefix_a, &nonce, &ppkd, &content, &contlen); if (!pkt) { DEBUGMSG(WARNING, " parsing error\n"); goto Done; } if (prefix_a) { //DEBUGMSG(DEBUG, "%s", prefix_a->comp); //ccnl_free(prefix_a); } //prefix_a = (struct ccnl_prefix_s *)ccnl_malloc(sizeof(struct ccnl_prefix_s)); prefix_a->compcnt = 2; prefix_a->comp = (unsigned char **) ccnl_malloc(sizeof(unsigned char*)*2); prefix_a->comp[0] = "mgmt"; sprintf(ht, "seqnum-%d", -seqnum); prefix_a->comp[1] = ht; prefix_a->complen = (int *) ccnl_malloc(sizeof(int)*2); prefix_a->complen[0] = strlen("mgmt"); prefix_a->complen[1] = strlen(ht); c = ccnl_content_new(ccnl, CCNL_SUITE_CCNB, &pkt, &prefix_a, &ppkd, content, contlen); if (!c) goto Done; ccnl_content_serve_pending(ccnl, c); ccnl_content_add2cache(ccnl, c); } Done: ccnl_free(out); } return 0; }
// deliver new content c to all clients with (loosely) matching interest, // but only one copy per face // returns: number of forwards int ccnl_content_serve_pending(struct ccnl_relay_s *ccnl, struct ccnl_content_s *c) { struct ccnl_interest_s *i; struct ccnl_face_s *f; int cnt = 0; DEBUGMSG_CORE(TRACE, "ccnl_content_serve_pending\n"); for (f = ccnl->faces; f; f = f->next){ f->flags &= ~CCNL_FACE_FLAGS_SERVED; // reply on a face only once } for (i = ccnl->pit; i;) { struct ccnl_pendint_s *pi; if (!i->pkt->pfx) continue; switch (i->pkt->pfx->suite) { #ifdef USE_SUITE_CCNB case CCNL_SUITE_CCNB: if (!ccnl_i_prefixof_c(i->pkt->pfx, i->pkt->s.ccnb.minsuffix, i->pkt->s.ccnb.maxsuffix, c)) { // XX must also check i->ppkd i = i->next; continue; } break; #endif #ifdef USE_SUITE_CCNTLV case CCNL_SUITE_CCNTLV: if (ccnl_prefix_cmp(c->pkt->pfx, NULL, i->pkt->pfx, CMP_EXACT)) { // XX must also check keyid i = i->next; continue; } break; #endif #ifdef USE_SUITE_CISTLV case CCNL_SUITE_CISTLV: if (ccnl_prefix_cmp(c->pkt->pfx, NULL, i->pkt->pfx, CMP_EXACT)) { // XX must also check keyid i = i->next; continue; } break; #endif #ifdef USE_SUITE_IOTTLV case CCNL_SUITE_IOTTLV: if (ccnl_prefix_cmp(c->pkt->pfx, NULL, i->pkt->pfx, CMP_EXACT)) { // XX must also check keyid i = i->next; continue; } break; #endif #ifdef USE_SUITE_NDNTLV case CCNL_SUITE_NDNTLV: if (!ccnl_i_prefixof_c(i->pkt->pfx, i->pkt->s.ndntlv.minsuffix, i->pkt->s.ndntlv.maxsuffix, c)) { // XX must also check i->ppkl, i = i->next; continue; } break; #endif default: i = i->next; continue; } //Hook for add content to cache by callback: if(i && ! i->pending){ DEBUGMSG_CORE(WARNING, "releasing interest 0x%p OK?\n", (void*)i); c->flags |= CCNL_CONTENT_FLAGS_STATIC; i = ccnl_interest_remove(ccnl, i); return 1; } // CONFORM: "Data MUST only be transmitted in response to // an Interest that matches the Data." for (pi = i->pending; pi; pi = pi->next) { if (pi->face->flags & CCNL_FACE_FLAGS_SERVED) continue; pi->face->flags |= CCNL_FACE_FLAGS_SERVED; if (pi->face->ifndx >= 0) { DEBUGMSG_CFWD(INFO, " outgoing data=<%s>%s to=%s\n", ccnl_prefix_to_path(i->pkt->pfx), ccnl_suite2str(i->pkt->pfx->suite), ccnl_addr2ascii(&pi->face->peer)); DEBUGMSG_CORE(VERBOSE, " Serve to face: %d (pkt=%p)\n", pi->face->faceid, (void*) c->pkt); ccnl_nfn_monitor(ccnl, pi->face, c->pkt->pfx, c->pkt->content, c->pkt->contlen); ccnl_face_enqueue(ccnl, pi->face, buf_dup(c->pkt->buf)); } else {// upcall to deliver content to local client ccnl_app_RX(ccnl, c); } c->served_cnt++; cnt++; } i = ccnl_interest_remove(ccnl, i); } return cnt; }
void ccnl_interest_propagate(struct ccnl_relay_s *ccnl, struct ccnl_interest_s *i) { struct ccnl_forward_s *fwd; int rc = 0; #ifdef USE_NACK int matching_face = 0; #endif if (!i) return; DEBUGMSG_CORE(DEBUG, "ccnl_interest_propagate\n"); // CONFORM: "A node MUST implement some strategy rule, even if it is only to // transmit an Interest Message on all listed dest faces in sequence." // CCNL strategy: we forward on all FWD entries with a prefix match for (fwd = ccnl->fib; fwd; fwd = fwd->next) { if (!fwd->prefix) continue; //Only for matching suite if (!i->pkt->pfx || fwd->suite != i->pkt->pfx->suite) { DEBUGMSG_CORE(VERBOSE, " not same suite (%d/%d)\n", fwd->suite, i->pkt->pfx ? i->pkt->pfx->suite : -1); continue; } rc = ccnl_prefix_cmp(fwd->prefix, NULL, i->pkt->pfx, CMP_LONGEST); DEBUGMSG_CORE(DEBUG, " ccnl_interest_propagate, rc=%d/%d\n", rc, fwd->prefix->compcnt); if (rc < fwd->prefix->compcnt) continue; DEBUGMSG_CORE(DEBUG, " ccnl_interest_propagate, fwd==%p\n", (void*)fwd); // suppress forwarding to origin of interest, except wireless if (!i->from || fwd->face != i->from || (i->from->flags & CCNL_FACE_FLAGS_REFLECT)) { DEBUGMSG_CFWD(INFO, " outgoing interest=<%s> to=%s\n", ccnl_prefix_to_path(i->pkt->pfx), fwd->face ? ccnl_addr2ascii(&fwd->face->peer) : "<tap>"); ccnl_nfn_monitor(ccnl, fwd->face, i->pkt->pfx, NULL, 0); // DEBUGMSG(DEBUG, "%p %p %p\n", (void*)i, (void*)i->pkt, (void*)i->pkt->buf); if (fwd->tap) (fwd->tap)(ccnl, i->from, i->pkt->pfx, i->pkt->buf); if (fwd->face) ccnl_face_enqueue(ccnl, fwd->face, buf_dup(i->pkt->buf)); #ifdef USE_NACK matching_face = 1; #endif } else { DEBUGMSG_CORE(DEBUG, " not forwarded\n"); } } #ifdef USE_NACK if(!matching_face){ ccnl_nack_reply(ccnl, i->pkt->pfx, i->from, i->pkt->pfx->suite); ccnl_interest_remove(ccnl, i); } #endif return; }
int ccnl_ccnb_forwarder(struct ccnl_relay_s *relay, struct ccnl_face_s *from, unsigned char **data, int *datalen) { int rc= -1, scope=3, aok=3, minsfx=0, maxsfx=CCNL_MAX_NAME_COMP, contlen; struct ccnl_buf_s *buf = 0, *nonce=0, *ppkd=0; struct ccnl_interest_s *i = 0; struct ccnl_content_s *c = 0; struct ccnl_prefix_s *p = 0; unsigned char *content = 0; DEBUGMSG(99, "ccnl/ccnb forwarder (%d bytes left)\n", *datalen); buf = ccnl_ccnb_extract(data, datalen, &scope, &aok, &minsfx, &maxsfx, &p, &nonce, &ppkd, &content, &contlen); if (!buf) { DEBUGMSG(6, " parsing error or no prefix\n"); goto Done; } if (nonce && ccnl_nonce_find_or_append(relay, nonce)) { DEBUGMSG(6, " dropped because of duplicate nonce\n"); goto Skip; } if (buf->data[0] == 0x01 && buf->data[1] == 0xd2) { // interest DEBUGMSG(6, " interest=<%s>\n", ccnl_prefix_to_path(p)); ccnl_print_stats(relay, STAT_RCV_I); //log count recv_interest if (p->compcnt > 0 && p->comp[0][0] == (unsigned char) 0xc1) goto Skip; if (p->compcnt == 4 && !memcmp(p->comp[0], "ccnx", 4)) { rc = ccnl_mgmt(relay, buf, p, from); goto Done; } // CONFORM: Step 1: if ( aok & 0x01 ) { // honor "answer-from-existing-content-store" flag for (c = relay->contents; c; c = c->next) { if (c->suite != CCNL_SUITE_CCNB) continue; if (!ccnl_i_prefixof_c(p, minsfx, maxsfx, c)) continue; if (ppkd && !buf_equal(ppkd, c->details.ccnb.ppkd)) continue; // FIXME: should check stale bit in aok here DEBUGMSG(7, " matching content for interest, content %p\n", (void *) c); ccnl_print_stats(relay, STAT_SND_C); //log sent_c if (from->ifndx >= 0) ccnl_face_enqueue(relay, from, buf_dup(c->pkt)); else ccnl_app_RX(relay, c); goto Skip; } } // CONFORM: Step 2: check whether interest is already known for (i = relay->pit; i; i = i->next) { if (i->suite == CCNL_SUITE_CCNB && !ccnl_prefix_cmp(i->prefix, NULL, p, CMP_EXACT) && i->details.ccnb.minsuffix == minsfx && i->details.ccnb.maxsuffix == maxsfx && ((!ppkd && !i->details.ccnb.ppkd) || buf_equal(ppkd, i->details.ccnb.ppkd)) ) break; } if (!i) { // this is a new/unknown I request: create and propagate i = ccnl_interest_new(relay, from, CCNL_SUITE_CCNB, &buf, &p, minsfx, maxsfx); if (ppkd) i->details.ccnb.ppkd = ppkd, ppkd = NULL; if (i) { // CONFORM: Step 3 (and 4) DEBUGMSG(7, " created new interest entry %p\n", (void *) i); if (scope > 2) ccnl_interest_propagate(relay, i); } } else if (scope > 2 && (from->flags & CCNL_FACE_FLAGS_FWDALLI)) { DEBUGMSG(7, " old interest, nevertheless propagated %p\n", (void *) i); ccnl_interest_propagate(relay, i); } if (i) { // store the I request, for the incoming face (Step 3) DEBUGMSG(7, " appending interest entry %p\n", (void *) i); ccnl_interest_append_pending(i, from); } } else { // content DEBUGMSG(6, " content=<%s>\n", ccnl_prefix_to_path(p)); ccnl_print_stats(relay, STAT_RCV_C); //log count recv_content #ifdef USE_SIGNATURES if (p->compcnt == 2 && !memcmp(p->comp[0], "ccnx", 4) && !memcmp(p->comp[1], "crypto", 6) && from == relay->crypto_face) { rc = ccnl_crypto(relay, buf, p, from); goto Done; } #endif /*USE_SIGNATURES*/ // CONFORM: Step 1: for (c = relay->contents; c; c = c->next) if (buf_equal(c->pkt, buf)) goto Skip; // content is dup c = ccnl_content_new(relay, CCNL_SUITE_CCNB, &buf, &p, &ppkd, content, contlen); if (c) { // CONFORM: Step 2 (and 3) if (!ccnl_content_serve_pending(relay, c)) { // unsolicited content // CONFORM: "A node MUST NOT forward unsolicited data [...]" DEBUGMSG(7, " removed because no matching interest\n"); free_content(c); goto Skip; } if (relay->max_cache_entries != 0) { // it's set to -1 or a limit DEBUGMSG(7, " adding content to cache\n"); ccnl_content_add2cache(relay, c); } else { DEBUGMSG(7, " content not added to cache\n"); free_content(c); } } } Skip: rc = 0; Done: free_prefix(p); free_3ptr_list(buf, nonce, ppkd); return rc; }
int ccnl_core_RX_i_or_c(struct ccnl_relay_s *relay, struct ccnl_face_s *from, unsigned char **data, int *datalen) { int rc = -1, scope = 3, aok = 3, minsfx = 0, maxsfx = CCNL_MAX_NAME_COMP, contlen; struct ccnl_buf_s *buf = 0, *nonce = 0, *ppkd = 0; struct ccnl_interest_s *i = 0; struct ccnl_content_s *c = 0; struct ccnl_prefix_s *p = 0; unsigned char *content = 0; DEBUGMSG(1, "ccnl_core_RX_i_or_c: (%d bytes left)\n", *datalen); buf = ccnl_extract_prefix_nonce_ppkd(data, datalen, &scope, &aok, &minsfx, &maxsfx, &p, &nonce, &ppkd, &content, &contlen); if (!buf) { DEBUGMSG(6, " parsing error or no prefix\n"); goto Done; } if (nonce && ccnl_nonce_find_or_append(relay, nonce)) { DEBUGMSG(6, " dropped because of duplicate nonce\n"); goto Skip; } if (buf->data[0] == 0x01 && buf->data[1] == 0xd2) { // interest DEBUGMSG(1, "ccnl_core_RX_i_or_c: interest=<%s>\n", ccnl_prefix_to_path(p)); from->stat.received_interest++; if (p->compcnt > 0 && p->comp[0][0] == (unsigned char) 0xc1) { goto Skip; } if (p->compcnt == 4 && !memcmp(p->comp[0], "ccnx", 4)) { DEBUGMSG(1, "it's a mgnt msg!\n"); rc = ccnl_mgmt(relay, buf, p, from); DEBUGMSG(1, "mgnt processing done!\n"); goto Done; } // CONFORM: Step 1: if (aok & 0x01) { // honor "answer-from-existing-content-store" flag for (c = relay->contents; c; c = c->next) { if (!ccnl_i_prefixof_c(p, ppkd, minsfx, maxsfx, c)) { continue; } // FIXME: should check stale bit in aok here DEBUGMSG(7, " matching content for interest, content %p\n", (void *) c); from->stat.send_content[c->served_cnt % CCNL_MAX_CONTENT_SERVED_STAT]++; c->served_cnt++; if (from->ifndx >= 0) { ccnl_face_enqueue(relay, from, buf_dup(c->pkt)); } goto Skip; } } // CONFORM: Step 2: check whether interest is already known for (i = relay->pit; i; i = i->next) { if (!ccnl_prefix_cmp(i->prefix, NULL, p, CMP_EXACT) && i->minsuffix == minsfx && i->maxsuffix == maxsfx && ((!ppkd && !i->ppkd) || buf_equal(ppkd, i->ppkd))) { break; } } if (!i) { // this is a new/unknown I request: create and propagate i = ccnl_interest_new(relay, from, &buf, &p, minsfx, maxsfx, &ppkd); if (i) { // CONFORM: Step 3 (and 4) DEBUGMSG(7, " created new interest entry %p\n", (void *) i); if (scope > 2) { ccnl_interest_propagate(relay, i); } } } else if (scope > 2 && (from->flags & CCNL_FACE_FLAGS_FWDALLI)) { DEBUGMSG(7, " old interest, nevertheless propagated %p\n", (void *) i); ccnl_interest_propagate(relay, i); } if (i) { // store the I request, for the incoming face (Step 3) DEBUGMSG(7, " appending interest entry %p\n", (void *) i); ccnl_interest_append_pending(i, from); } } else { // content DEBUGMSG(6, " content=<%s>\n", ccnl_prefix_to_path(p)); from->stat.received_content++; // CONFORM: Step 1: for (c = relay->contents; c; c = c->next) { if (buf_equal(c->pkt, buf)) { DEBUGMSG(1, "content is dup: skip\n"); goto Skip; } } c = ccnl_content_new(relay, &buf, &p, &ppkd, content, contlen); if (c) { // CONFORM: Step 2 (and 3) if (!ccnl_content_serve_pending(relay, c, from)) { // unsolicited content // CONFORM: "A node MUST NOT forward unsolicited data [...]" DEBUGMSG(7, " removed because no matching interest\n"); free_content(c); goto Skip; } #if CCNL_DYNAMIC_FIB else { /* content has matched an interest, we consider this name as available on this face */ ccnl_content_learn_name_route(relay, c->name, from, relay->fib_threshold_prefix, 0); } #endif if (relay->max_cache_entries != 0) { // it's set to -1 or a limit DEBUGMSG(7, " adding content to cache\n"); ccnl_content_add2cache(relay, c); } else { DEBUGMSG(7, " content not added to cache\n"); free_content(c); } } } Skip: rc = 0; Done: free_prefix(p); free_3ptr_list(buf, nonce, ppkd); DEBUGMSG(1, "leaving\n"); return rc; }