ccnc.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Palo Alto Research Center, Inc.
00003  *
00004  * This work is free software; you can redistribute it and/or modify it under
00005  * the terms of the GNU General Public License version 2 as published by the
00006  * Free Software Foundation.
00007  * This work is distributed in the hope that it will be useful, but WITHOUT ANY
00008  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
00009  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
00010  * for more details. You should have received a copy of the GNU General Public
00011  * License along with this program; if not, write to the
00012  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00013  * Boston, MA 02110-1301, USA.
00014  */
00015 
00016 #include <sys/select.h>
00017 #include <sys/time.h>
00018 #include <fcntl.h>
00019 #include <pwd.h>
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <sys/socket.h>
00024 #include <unistd.h>
00025 #include <ccn/ccn.h>
00026 #include <ccn/ccn_private.h> /* for ccn_process_scheduled_operations() */
00027 #include <ccn/charbuf.h>
00028 #include <ccn/lned.h>
00029 #include <ccn/uri.h>
00030 
00031 #define USAGE                                                                 \
00032     "[-hdi:nqr:vx:] ccnx:/chat/room - community text chat"               "\n" \
00033     " -h - help"                                                         "\n" \
00034     " -d - debug mode - no input editing"                                "\n" \
00035     " -i n - print n bytes of signer's public key digest in hex"         "\n" \
00036     " -n - no echo of own output"                                        "\n" \
00037     " -q - no automatic greeting or farwell"                             "\n" \
00038     " -r command - hook up to input and output of responder command"     "\n" \
00039     " -v - verbose trace of what is happening"                           "\n" \
00040     " -x sec - set freshness"
00041 
00042 /** Entry in the application's pending interest table */
00043 struct pit_entry {
00044     struct ccn_charbuf *pib;    /* Buffer for received Interest */
00045     int consumed;               /* Set when this interest is consumed */
00046     unsigned short expiry;      /* Wrapped time that this object expires */
00047 };
00048 /** Number of pending interests we will keep */
00049 #define PIT_LIMIT 10
00050 
00051 /** Entry in the mini content store that holds our generated data */
00052 struct cs_entry {
00053     struct ccn_charbuf *cob;    /* Buffer for ContentObject*/
00054     int sent;                   /* Number of times sent */
00055     int matched;                /* Non-zero if send needed */
00056 };
00057 /** Number of generated data items we will hold */
00058 #define CS_LIMIT 3
00059 /** Max number of received versions to track */
00060 #define VER_LIMIT 5
00061 
00062 /**
00063  * Application state
00064  *
00065  * A pointer to one of these is stored in the data field of the closure.
00066  */
00067 struct ccnxchat_state {
00068     struct ccn *h;              /* Backlink to ccn handle */
00069     int n_pit;                  /* Number of live PIT entries */
00070     struct pit_entry pit[PIT_LIMIT];
00071     int n_cob;                  /* Number of live CS entries */
00072     struct cs_entry cs[CS_LIMIT];
00073     int n_ver;                  /* Number of recently received versions */
00074     struct ccn_charbuf *ver[VER_LIMIT];
00075     struct ccn_closure *cc;     /* Closure for incoming content */
00076     struct ccn_charbuf *payload; /* Buffer for payload */
00077     struct ccn_charbuf *lineout; /* For building output line */
00078     struct ccn_charbuf *luser;   /* user's name */
00079     /* The following buffers contain ccnb-encoded data */
00080     struct ccn_charbuf *basename; /* The namespace we are serving */
00081     struct ccn_charbuf *name;   /* Buffer for constructed name */
00082     struct ccn_charbuf *cob;    /* Buffer for ContentObject */
00083     struct ccn_charbuf *incob;  /* Most recent incoming ContentObject */    
00084     int eof;                    /* true if we have encountered eof */
00085     int ready;                  /* true if payload is ready to go */
00086     int prefer_newest;          /* for saner startup */
00087     int echo;                   /* to see own output */
00088     int freshness;              /* to set FreshnessSeconds */
00089     int quiet;                  /* no automatic greeting or farwell */
00090     int robotname;              /* print n bytes of robot name */
00091     int verbose;                /* to turn on debugging output */
00092 };
00093 
00094 /* Prototypes */
00095 static enum ccn_upcall_res incoming_interest(struct ccn_closure *,
00096                                              enum ccn_upcall_kind,
00097                                              struct ccn_upcall_info *);
00098 static enum ccn_upcall_res  incoming_content(struct ccn_closure *,
00099                                              enum ccn_upcall_kind,
00100                                              struct ccn_upcall_info *);
00101 static void fatal(int lineno, int val);
00102 static void initialize(struct ccnxchat_state *st, struct ccn_charbuf *);
00103 struct ccn_charbuf *adjust_regprefix(struct ccn_charbuf *);
00104 static int namecompare(const void *, const void *);
00105 static void stampnow(struct ccn_charbuf *);
00106 static void seed_random(void);
00107 static void usage(void);
00108 unsigned short wrappednow(void);
00109 
00110 static int wait_for_input_or_timeout(struct ccn *, int );
00111 static void read_input(struct ccnxchat_state *);
00112 static void generate_new_data(struct ccnxchat_state *);
00113 static int  matchbox(struct ccnxchat_state *);
00114 static int  send_matching_data(struct ccnxchat_state *);
00115 static void toss_in_cs(struct ccnxchat_state *, const unsigned char *, size_t);
00116 static void toss_in_pit(struct ccnxchat_state *,
00117                         const unsigned char *, struct ccn_parsed_interest *);
00118 static void age_cs(struct ccnxchat_state *);
00119 static void age_pit(struct ccnxchat_state *);
00120 static void debug_logger(struct ccnxchat_state *, int, struct ccn_charbuf *);
00121 static int append_interest_details(struct ccn_charbuf *c,
00122                                    const unsigned char *ccnb, size_t size);
00123 static void generate_cob(struct ccnxchat_state *);
00124 static void add_info_exclusion(struct ccnxchat_state *,
00125                                struct ccn_upcall_info *);
00126 static void add_uri_exclusion(struct ccnxchat_state *, const char *);
00127 static void add_ver_exclusion(struct ccnxchat_state *, struct ccn_charbuf **);
00128 static void display_the_content(struct ccnxchat_state *,
00129                                 struct ccn_upcall_info *);
00130 static void express_interest(struct ccnxchat_state *);
00131 static void init_ver_exclusion(struct ccnxchat_state *);
00132 static void prune_oldest_exclusion(struct ccnxchat_state *);
00133 static int append_full_user_name(struct ccn_charbuf *);
00134 
00135 /* Very simple error handling */
00136 #define FATAL(res) fatal(__LINE__, res)
00137 
00138 /* Debug messages */
00139 #define DB(st, ccnb) debug_logger(st, __LINE__, ccnb)
00140 
00141 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00142 
00143 /** Main entry point for chat */
00144 static int
00145 chat_main(int argc, char **argv)
00146 {
00147     struct ccn *h = NULL;
00148     struct ccn_charbuf *name = NULL;
00149     struct ccn_charbuf *cob = NULL;
00150     struct ccn_closure in_interest = {0};
00151     struct ccn_closure in_content = {0};
00152     struct ccnxchat_state state = {0};
00153     struct ccnxchat_state *st;
00154     int res;
00155     int timeout_ms;
00156     
00157     st = &state;
00158     name = ccn_charbuf_create();
00159     initialize(st, name);
00160     cob = ccn_charbuf_create();
00161     /* Connect to ccnd */
00162     h = ccn_create();
00163     if (ccn_connect(h, NULL) == -1)
00164         FATAL(-1);
00165     /* Set up state for interest handler */
00166     in_interest.p = &incoming_interest;
00167     in_interest.data = st;
00168     /* Set up state for content handler */
00169     in_content.p = &incoming_content;
00170     in_content.data = st;
00171     st->h = h;
00172     st->cc = &in_content;
00173     st->basename = name;
00174     st->name = ccn_charbuf_create();
00175     st->payload = ccn_charbuf_create();
00176     st->cob = ccn_charbuf_create();
00177     st->incob = ccn_charbuf_create();
00178     st->lineout = ccn_charbuf_create();
00179     st->luser = ccn_charbuf_create();
00180     append_full_user_name(st->luser);
00181     init_ver_exclusion(st);
00182     /* Set up a handler for interests */
00183     res = ccn_set_interest_filter(h, st->basename, &in_interest);
00184     if (res < 0)
00185         FATAL(res);
00186     debug_logger(st, __LINE__, st->basename);
00187     st->prefer_newest = 1;
00188     express_interest(st);
00189     /* Run the event loop */
00190     for (;;) {
00191         res = -1;
00192         timeout_ms = 10000;
00193         if (st->ready == 0 && st->eof == 0 && st->n_pit != 0) {
00194             res = wait_for_input_or_timeout(h, 0);
00195             timeout_ms = 10;
00196         }
00197         if (res != 0)
00198             read_input(st);
00199         if (st->eof)
00200             timeout_ms = 100;
00201         else if (st->ready)
00202             timeout_ms = 10;
00203         res = ccn_run(h, timeout_ms);
00204         if (res != 0)
00205             FATAL(res);
00206         generate_new_data(st);
00207         matchbox(st);
00208         res = send_matching_data(st);
00209         if (st->eof && st->eof++ > 3)
00210             exit(0);
00211         if (res > 0 || st->cc->refcount == 0)
00212             express_interest(st);
00213         age_cs(st);
00214         age_pit(st);
00215     }
00216     /* Loop has no normal exit */
00217 }
00218 
00219 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00220 
00221 /** Interest handler */
00222 static enum ccn_upcall_res
00223 incoming_interest(struct ccn_closure *selfp,
00224                   enum ccn_upcall_kind kind,
00225                   struct ccn_upcall_info *info)
00226 {
00227     struct ccnxchat_state *st = selfp->data;
00228     
00229     switch (kind) {
00230         case CCN_UPCALL_FINAL:
00231             break;
00232         case CCN_UPCALL_INTEREST:
00233             ccn_set_run_timeout(info->h, 0);
00234             toss_in_pit(st, info->interest_ccnb, info->pi);
00235             if (st->ready)
00236                 generate_new_data(st);
00237             if (matchbox(st) != 0)
00238                 return(CCN_UPCALL_RESULT_INTEREST_CONSUMED);
00239             break;
00240         default:
00241             break;
00242     }
00243     return(CCN_UPCALL_RESULT_OK);
00244 }
00245 
00246 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00247 
00248 static enum ccn_upcall_res
00249 incoming_content(struct ccn_closure *selfp,
00250                   enum ccn_upcall_kind kind,
00251                   struct ccn_upcall_info *info)
00252 {
00253     struct ccnxchat_state *st = selfp->data;
00254 
00255     switch (kind) {
00256         case CCN_UPCALL_FINAL:
00257             return(CCN_UPCALL_RESULT_OK);
00258         case CCN_UPCALL_CONTENT_UNVERIFIED:
00259             DB(st, NULL);
00260             add_info_exclusion(st, info);
00261             return(CCN_UPCALL_RESULT_VERIFY);
00262         case CCN_UPCALL_CONTENT:
00263             display_the_content(st, info);
00264             add_info_exclusion(st, info);
00265             ccn_set_run_timeout(info->h, 0);
00266             return(CCN_UPCALL_RESULT_OK);
00267         case CCN_UPCALL_INTEREST_TIMED_OUT:
00268             prune_oldest_exclusion(st);
00269             if (st->eof == 0)
00270                 ccn_set_run_timeout(info->h, 0);
00271             return(CCN_UPCALL_RESULT_OK);
00272         default:
00273             /* something unexpected - make noise */
00274             DB(st, NULL);
00275             return(CCN_UPCALL_RESULT_ERR);
00276     }
00277 }
00278 
00279 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00280 
00281 static void
00282 display_the_content(struct ccnxchat_state *st, struct ccn_upcall_info *info)
00283 {
00284     struct ccn_charbuf *cob = st->incob;
00285     struct ccn_charbuf *line = st->lineout;
00286     const unsigned char *keyhash = NULL;
00287     const unsigned char *data = NULL;
00288     size_t size;
00289     size_t ksize;
00290     ssize_t sres;
00291     int i;
00292     int res;
00293     
00294     /* We see our own data twice because of having 2 outstanding interests */
00295     size = info->pco->offset[CCN_PCO_E];
00296     if (size == cob->length && memcmp(cob->buf, info->content_ccnb, size) == 0)
00297         return;
00298     ccn_charbuf_reset(cob);
00299     ccn_charbuf_append(cob, info->content_ccnb, size);
00300     DB(st, cob);
00301     res = ccn_content_get_value(cob->buf, cob->length, info->pco,
00302                                 &data, &size);
00303     if (res < 0) abort();
00304     res = ccn_ref_tagged_BLOB(CCN_DTAG_PublisherPublicKeyDigest,
00305                cob->buf,
00306                info->pco->offset[CCN_PCO_B_PublisherPublicKeyDigest],
00307                info->pco->offset[CCN_PCO_E_PublisherPublicKeyDigest],
00308                &keyhash, &ksize);
00309      if (res < 0 || ksize < 32) abort();
00310      ccn_charbuf_reset(line);
00311      for (i = 0; i < st->robotname; i++)
00312          ccn_charbuf_putf(line, "%02x", keyhash[i]);
00313      if (i > 0)
00314          ccn_charbuf_putf(line, " ");
00315      ccn_charbuf_append(line, data, size);
00316      ccn_charbuf_putf(line, "\n");
00317      sres = write(1, line->buf, line->length);
00318      if (sres != line->length)
00319           exit(1);
00320 }
00321 
00322 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00323 static void
00324 add_ver_exclusion(struct ccnxchat_state *st, struct ccn_charbuf **c)
00325 {
00326     int i;
00327     int j;
00328     int t;
00329     
00330     for (i = 0; i < st->n_ver; i++) {
00331         t = namecompare(c, &(st->ver[i]));
00332         if (t == 0)
00333             return;
00334         if (t < 0)
00335             break;
00336     }
00337     if (st->n_ver == VER_LIMIT || st->prefer_newest) {
00338         if (i == 0)
00339             return;
00340         ccn_charbuf_destroy(&st->ver[0]);
00341         for (j = 0; j + 1 < i; j++)
00342             st->ver[j] = st->ver[j + 1];
00343         st->ver[j] = *c;
00344         *c = NULL;
00345         st->prefer_newest = 0;
00346         return;
00347     }
00348     for (j = st->n_ver; j > i; j--)
00349         st->ver[j] = st->ver[j - 1];
00350     st->n_ver++;
00351     st->ver[j] = *c;
00352     *c = NULL;
00353 }
00354 
00355 static void
00356 prune_oldest_exclusion(struct ccnxchat_state *st)
00357 {
00358     int j;
00359     
00360     if (st->n_ver <= 2)
00361         return;
00362     ccn_charbuf_destroy(&st->ver[0]);
00363     for (j = 0; j + 1 < st->n_ver; j++)
00364         st->ver[j] = st->ver[j + 1];
00365     st->n_ver--;
00366 }
00367 
00368 static void
00369 add_info_exclusion(struct ccnxchat_state *st, struct ccn_upcall_info *info)
00370 {
00371     struct ccn_charbuf *c = ccn_charbuf_create();
00372     const unsigned char *ver = NULL;
00373     size_t ver_size = 0;
00374     int res;
00375     
00376     if (info->content_comps->n > info->matched_comps + 1) {
00377         ccn_name_init(c);
00378         res = ccn_ref_tagged_BLOB(CCN_DTAG_Component, info->content_ccnb,
00379                   info->content_comps->buf[info->matched_comps],
00380                   info->content_comps->buf[info->matched_comps + 1],
00381                   &ver,
00382                   &ver_size);
00383         if (res < 0) abort();
00384         ccn_name_append(c, ver, ver_size);
00385         add_ver_exclusion(st, &c);
00386     }
00387     ccn_charbuf_destroy(&c);
00388 }
00389 
00390 static void
00391 add_uri_exclusion(struct ccnxchat_state *st, const char *uri)
00392 {
00393     struct ccn_charbuf *c = ccn_charbuf_create();
00394     
00395     ccn_name_from_uri(c, uri);
00396     add_ver_exclusion(st, &c);
00397     ccn_charbuf_destroy(&c);
00398 }
00399 
00400 static void
00401 add_cob_exclusion(struct ccnxchat_state *st, struct ccn_charbuf *cob)
00402 {
00403     struct ccn_parsed_ContentObject co;
00404     struct ccn_parsed_ContentObject *pco = &co;
00405     struct ccn_indexbuf *comps;
00406     struct ccn_charbuf *c;
00407     const unsigned char *ver = NULL;
00408     size_t ver_size = 0;
00409     int i;
00410     int res;
00411     
00412     i = ccn_name_split(st->basename, NULL);
00413     c = ccn_charbuf_create();
00414     comps = ccn_indexbuf_create();
00415     res = ccn_parse_ContentObject(cob->buf, cob->length, pco, comps);
00416     if (res >= 0 && i + 1 < comps->n) {
00417         res = ccn_ref_tagged_BLOB(CCN_DTAG_Component, cob->buf,
00418                                   comps->buf[i], comps->buf[i + 1],
00419                                   &ver, &ver_size);
00420         if (res >= 0) {
00421             ccn_name_init(c);
00422             ccn_name_append(c, ver, ver_size);
00423             add_ver_exclusion(st, &c);
00424         }
00425     }
00426     ccn_indexbuf_destroy(&comps);
00427     ccn_charbuf_destroy(&c);
00428 }
00429 
00430 static void
00431 init_ver_exclusion(struct ccnxchat_state *st)
00432 {    
00433     add_uri_exclusion(st, "/%FE%00%00%00%00%00%00");
00434     add_uri_exclusion(st, "/%FD%00%FF%FF%FF%FF%FF");
00435 }
00436 
00437 static void
00438 express_interest(struct ccnxchat_state *st)
00439 {
00440     struct ccn_charbuf *templ = ccn_charbuf_create();
00441     struct ccn_charbuf *comp = NULL;
00442     int i;
00443     
00444     ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG);
00445     ccn_charbuf_append(templ, st->basename->buf, st->basename->length);
00446     ccnb_tagged_putf(templ, CCN_DTAG_MinSuffixComponents, "%d", 3);
00447     ccnb_tagged_putf(templ, CCN_DTAG_MaxSuffixComponents, "%d", 3);
00448     ccn_charbuf_append_tt(templ, CCN_DTAG_Exclude, CCN_DTAG);
00449     if (st->n_ver > 1)
00450         ccnb_tagged_putf(templ, CCN_DTAG_Any, "");
00451     for (i = 0; i < st->n_ver; i++) {
00452         comp = st->ver[i];
00453         if (comp->length < 4) abort();
00454         ccn_charbuf_append(templ, comp->buf + 1, comp->length - 2);
00455     }
00456     ccnb_tagged_putf(templ, CCN_DTAG_Any, "");
00457     ccn_charbuf_append_closer(templ); /* </Exclude> */
00458     if (st->prefer_newest)
00459         ccnb_tagged_putf(templ, CCN_DTAG_ChildSelector, "%d", 1);
00460     ccn_charbuf_append_closer(templ); /* </Interest> */
00461     ccn_express_interest(st->h, st->basename, st->cc, templ);
00462     ccn_charbuf_destroy(&templ);
00463 }
00464 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00465 
00466 /**
00467  * Generate a content object containing the current payload
00468  *
00469  * The standard versioning and segmentation profiles are used.
00470  * It is assumed that the payload fits into one content object.
00471  */
00472 static void
00473 generate_cob(struct ccnxchat_state *st)
00474 {
00475     struct ccn_signing_params sp = CCN_SIGNING_PARAMS_INIT;
00476     int res;
00477     
00478     /* Make sure name buffer is up to date */
00479     ccn_charbuf_reset(st->name);
00480     ccn_charbuf_append(st->name, st->basename->buf, st->basename->length);
00481     ccn_create_version(st->h, st->name, CCN_V_NOW, 0, 0);
00482     ccn_name_append_numeric(st->name, CCN_MARKER_SEQNUM, 0);
00483     sp.sp_flags |= CCN_SP_FINAL_BLOCK; /* always produce just one segment */
00484     if (st->freshness > 0)
00485         sp.freshness = st->freshness;
00486     /* Make a ContentObject using the constructed name and our payload */
00487     ccn_charbuf_reset(st->cob);
00488     res = ccn_sign_content(st->h, st->cob, st->name, &sp,
00489                            st->payload->buf, st->payload->length);
00490     if (res < 0)
00491         FATAL(res);
00492     DB(st, st->cob);
00493 }
00494 
00495 /**
00496  * Wait until input on fd is ready or ccn_run needs to be called
00497  *
00498  * @returns 1 if STDIN is ready to read, 0 if not, or -1 for error.
00499  */
00500 static int
00501 wait_for_input_or_timeout(struct ccn *h, int fd)
00502 {
00503     fd_set readfds;
00504     struct timeval tv;
00505     int ccnfd;
00506     int maxfd = fd;
00507     int res = -1;
00508     
00509     ccnfd = ccn_get_connection_fd(h);
00510     if (ccnfd < 0)
00511         return(-1);
00512     if (maxfd < ccnfd)
00513         maxfd = ccnfd;
00514     FD_ZERO(&readfds);
00515     FD_SET(fd, &readfds);
00516     FD_SET(ccnfd, &readfds);
00517     res = ccn_process_scheduled_operations(h);
00518     if (res >= 0) {
00519         tv.tv_sec  = res / 1000000U;
00520         tv.tv_usec = res % 1000000U;
00521         res = select(maxfd + 1, &readfds, 0, 0, &tv);                
00522     }
00523     if (res >= 0)
00524         res = FD_ISSET(fd, &readfds);
00525     return(res);
00526 }
00527 
00528 /** Read a line of standard input into payload */
00529 static void
00530 read_input(struct ccnxchat_state *st)
00531 {
00532     unsigned char *cp = NULL;
00533     ssize_t res;
00534     int fl;
00535     int fd = 0;       /* standard input */
00536     
00537     if (st->ready)
00538         return;
00539     if (st->eof) {
00540         if (st->payload->length > 0)
00541             st->ready = 1;
00542         return;
00543     }
00544     fl = fcntl(fd, F_GETFL);
00545     fcntl(fd, F_SETFL, O_NONBLOCK | fl);
00546     while (st->ready == 0) {
00547         cp = ccn_charbuf_reserve(st->payload, 1);
00548         res = read(fd, cp, 1);
00549         if (res == 1) {
00550             if (cp[0] == '\n')
00551                 st->ready = 1;
00552             else
00553                 st->payload->length++;
00554         }
00555         else if (res == 0) {
00556             if (st->eof == 0 && st->quiet == 0) {
00557                 ccn_charbuf_putf(st->payload, "=== ");
00558                 ccn_charbuf_append_charbuf(st->payload, st->luser);
00559                 ccn_charbuf_putf(st->payload, " leaving chat");
00560                 st->freshness = 1;
00561             }
00562             st->eof = 1;
00563             if (st->payload->length > 0)
00564                 st->ready = 1;
00565             break;
00566         }
00567         else
00568             break; /* partial line */
00569     }
00570     fcntl(fd, F_SETFL, fl);
00571 }
00572 
00573 /** Collect some new data and when ready, place it in store */
00574 static void
00575 generate_new_data(struct ccnxchat_state *st)
00576 {
00577     if (st->ready && st->n_pit > 0 && st->n_cob < CS_LIMIT) {    
00578         generate_cob(st);
00579         toss_in_cs(st, st->cob->buf, st->cob->length);
00580         if (st->echo == 0)
00581             add_cob_exclusion(st, st->cob);
00582         ccn_charbuf_reset(st->payload);
00583         st->ready = 0;
00584     }
00585 }
00586 
00587 /**
00588  * Insert a ccnb-encoded ContentObject into our content store
00589  */
00590 static void
00591 toss_in_cs(struct ccnxchat_state *st, const unsigned char *p, size_t size)
00592 {
00593     struct cs_entry *cse;        /* New slot in our store */
00594 
00595     if (st->n_cob >= CS_LIMIT)
00596         FATAL(st->n_cob);
00597     cse = &(st->cs[st->n_cob++]); /* Allocate cs slot */
00598     cse->cob = ccn_charbuf_create();
00599     ccn_charbuf_append(cse->cob, p, size);
00600     cse->sent = 0;
00601     cse->matched = 0;
00602 }
00603 
00604 /** Insert a ccnb-encoded Interest message into our pending interest table */
00605 static void
00606 toss_in_pit(struct ccnxchat_state *st, const unsigned char *p,
00607             struct ccn_parsed_interest *pi)
00608 {
00609     intmax_t lifetime;
00610     unsigned short lifetime_ms = ((unsigned short)(~0)) >> 1;
00611     struct pit_entry *pie;
00612     size_t size;
00613     
00614     size = pi->offset[CCN_PI_E];
00615     lifetime = ccn_interest_lifetime(p, pi); /* units: s/4096 */
00616     lifetime = (lifetime * (1000 / 8) + (4096 / 8 - 1)) / (4096 / 8);
00617     if (lifetime_ms > lifetime)
00618         lifetime_ms = lifetime;
00619     if (st->n_pit == PIT_LIMIT)
00620         age_pit(st);
00621     if (st->n_pit == PIT_LIMIT) {
00622         /* Forcibly free up a slot if we must */
00623         st->pit[0].consumed = 1;
00624         age_pit(st);
00625     }
00626     if (st->n_pit >= PIT_LIMIT)
00627         FATAL(st->n_pit);
00628     pie = &(st->pit[st->n_pit++]); /* Allocate pit slot */
00629     pie->pib = ccn_charbuf_create();
00630     ccn_charbuf_append(pie->pib, p, size);
00631     pie->consumed = 0;
00632     pie->expiry = wrappednow() + lifetime_ms;
00633     DB(st, pie->pib);
00634 }
00635 
00636 /**
00637  * Match PIT entries against the store
00638  *
00639  * This implementation relies on both tables being relatively
00640  * small, since it can look at all n x m combinations.
00641  *
00642  * @returns number of new matches found
00643  */
00644 static int
00645 matchbox(struct ccnxchat_state *st)
00646 {
00647     int new_matches;
00648     struct cs_entry *cse;
00649     struct pit_entry *pie;
00650     int i, j;
00651     
00652     new_matches = 0;
00653     for (i = 0; i < st->n_pit; i++) {
00654         pie = &(st->pit[i]);
00655         if (pie->consumed)
00656             continue;
00657         for (j = 0; j < st->n_cob; j++) {
00658             cse = &(st->cs[j]);
00659             if (ccn_content_matches_interest(
00660                   cse->cob->buf, cse->cob->length, 1, NULL,
00661                   pie->pib->buf, pie->pib->length, NULL)) {
00662                 if (cse->sent == 0)
00663                     new_matches++;
00664                 cse->matched = 1;
00665                 pie->consumed = 1;
00666                 DB(st, pie->pib);
00667             }
00668         }
00669     }
00670     return(new_matches);
00671 }
00672 
00673 /** Send data that has been matched */
00674 static int
00675 send_matching_data(struct ccnxchat_state *st)
00676 {
00677     struct cs_entry *cse = NULL;
00678     int i;
00679     int res;
00680     int sent;
00681     
00682     sent = 0;
00683     for (i = 0; i < st->n_cob; i++) {
00684         cse = &(st->cs[i]);
00685         if (cse->matched) {
00686             res = ccn_put(st->h, cse->cob->buf, cse->cob->length);
00687             if (res < 0)
00688                 FATAL(res);
00689             cse->sent++;
00690             cse->matched = 0;
00691             sent++;
00692         }
00693     }
00694     return(sent);
00695 }
00696 
00697 /** Remove already-sent entries from the content store */
00698 static void
00699 age_cs(struct ccnxchat_state *st)
00700 {
00701     const struct cs_entry empty = {0};
00702     int i;
00703     int j;
00704     
00705     for (i = 0, j = 0; i < st->n_cob; i++) {
00706         if (st->cs[i].sent != 0) {
00707             /* could log here */
00708             DB(st, st->cs[i].cob);
00709             ccn_charbuf_destroy(&(st->cs[i].cob));
00710         }
00711         else
00712             st->cs[j++] = st->cs[i];
00713     }
00714     st->n_cob = j;
00715     /* Clear garbage in now-unused entries */
00716     while (i > j)
00717         st->cs[--i] = empty;
00718 }
00719 
00720 /** Get rid of PIT entries that have timed out or been consumed */
00721 static void
00722 age_pit(struct ccnxchat_state *st)
00723 {
00724     const struct pit_entry empty = {0};
00725     struct pit_entry *pie;
00726     unsigned short now;
00727     unsigned short delta;
00728     unsigned short deltawrap;
00729     int i;
00730     int j;
00731     
00732     deltawrap = ~0;
00733     deltawrap >>= 1; /* 32767 on most platforms */
00734     now = wrappednow();
00735     for (i = 0, j = 0; i < st->n_pit; i++) {
00736         pie = &(st->pit[i]);
00737         delta = now - pie->expiry;
00738         if (delta <= deltawrap) {
00739             DB(st, pie->pib);
00740             pie->consumed = 1;
00741         }
00742         if (pie->consumed)
00743             ccn_charbuf_destroy(&(pie->pib));
00744         else
00745             st->pit[j++] = st->pit[i];
00746     }
00747     st->n_pit = j;
00748     /* Clear out now-unused entries */
00749     while (i > j)
00750         st->pit[--i] = empty;
00751 }
00752 
00753 /**
00754  * Comparison operator for sorting the excl list with qsort.
00755  * For convenience, the items in the excl array are
00756  * charbufs containing ccnb-encoded Names of one component each.
00757  * (This is not the most efficient representation.)
00758  */
00759 static int /* for qsort */
00760 namecompare(const void *a, const void *b)
00761 {
00762     const struct ccn_charbuf *aa = *(const struct ccn_charbuf **)a;
00763     const struct ccn_charbuf *bb = *(const struct ccn_charbuf **)b;
00764     int ans = ccn_compare_names(aa->buf, aa->length, bb->buf, bb->length);
00765     return (ans);
00766 }
00767 
00768 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00769 
00770 /** Global progam name for messages */
00771 static const char *progname;
00772 /** Global option info */
00773 static struct {
00774     int debug;
00775     int echo;
00776     int freshness;
00777     int robotname;
00778     int quiet;
00779     int verbose;
00780     const char *basename;
00781     const char *responder;
00782 } option;
00783 
00784 static void
00785 parseopts(int argc, char **argv)
00786 {
00787     int opt;
00788     
00789     progname = argv[0];
00790     optind = 1;
00791     option.echo = 1;
00792     option.robotname = 3;
00793     option.verbose = 0;
00794     option.quiet = 0;
00795     option.freshness = 30 * 60;
00796     while ((opt = getopt(argc, argv, "hdi:nqr:vx:")) != -1) {
00797         switch (opt) {
00798             case 'd':
00799                 option.debug = 1;
00800                 break;
00801             case 'i':
00802                 option.robotname = atoi(optarg);
00803                 if (option.robotname < 0 || option.robotname > 32)
00804                     usage();
00805                 break;
00806             case 'n':
00807                 option.echo = 0;
00808                 break;
00809             case 'q':
00810                 option.quiet = 1;
00811                 break;
00812             case 'r':
00813                 option.responder = optarg;
00814                 break;
00815             case 'v':
00816                 option.verbose++;
00817                 break;
00818             case 'x':
00819                 option.freshness = atoi(optarg);
00820                 break;
00821             case 'h':
00822             default:
00823                 usage();
00824         }
00825     }
00826     option.basename = argv[optind];
00827     if (option.basename == NULL || argv[optind + 1] != NULL)
00828         usage();
00829 }
00830 
00831 /**
00832  * Initialization at startup
00833  *
00834  * If there is a command line argument, it is interpreted as a
00835  * URI relative to basename, and basename is updated accordingly.
00836  *
00837  * basename is a Name in ccnb encoding.
00838  */
00839 static void
00840 initialize(struct ccnxchat_state *st, struct ccn_charbuf *basename)
00841 {
00842     int res;
00843     
00844     res = ccn_name_from_uri(basename, option.basename);
00845     if (res < 0)
00846         usage();
00847     st->echo = option.echo;
00848     st->freshness = option.freshness;
00849     st->verbose = option.verbose;
00850     st->robotname = option.robotname;
00851     st->quiet = option.quiet;
00852     seed_random();
00853 }
00854 
00855 /** Return a newly-allocated Name buffer with one Component chopped off */
00856 struct ccn_charbuf *
00857 adjust_regprefix(struct ccn_charbuf *name)
00858 {
00859     struct ccn_charbuf *c;
00860     
00861     c = ccn_charbuf_create();
00862     ccn_charbuf_append(c, name->buf, name->length);
00863     ccn_name_chop(c, NULL, -1);
00864     DB(NULL, c);
00865     return(c);
00866 }
00867 
00868 /** Print cryptic message and exit */
00869 static void
00870 fatal(int lineno, int val)
00871 {
00872     fprintf(stderr, "Error near %s:%d (%d)\n", progname, lineno, val);
00873     exit(1);
00874 }
00875 
00876 /** Usage */
00877 static void
00878 usage(void)
00879 {
00880     fprintf(stderr, "%s " USAGE "\n", progname);
00881     exit(1);
00882 }
00883 
00884 /** Append a numeric timestamp to a charbuf */
00885 static void
00886 stampnow(struct ccn_charbuf *c)
00887 {
00888     struct timeval tv;
00889     unsigned long sec;
00890     unsigned usec;
00891     
00892     gettimeofday(&tv, NULL);
00893     sec = tv.tv_sec;
00894     usec = tv.tv_usec;
00895     ccn_charbuf_putf(c, "%lu.%06u ", sec, usec);
00896 }
00897 
00898 /** Wrapped time - (normally) 16-bit unsigned; millisecond units */
00899 unsigned short
00900 wrappednow(void)
00901 {
00902     unsigned short now;
00903     struct timeval tv;
00904     
00905     gettimeofday(&tv, NULL);
00906     now = tv.tv_sec * 1000 + tv.tv_usec / 1000;
00907     return(now);
00908 }
00909 
00910 /** Seed the pseudo-random numbers */
00911 static void
00912 seed_random(void)
00913 {
00914     struct timeval tv;
00915     
00916     gettimeofday(&tv, NULL);
00917     srandom(getpid() * 31415 + tv.tv_sec + tv.tv_usec);
00918 }
00919 
00920 /**
00921  * Debugging aid
00922  *
00923  * Prints some internal state to stderr.
00924  * If non-NULL, ccnb should be a ccnb-encoded Name, Interest, or ContentObject.
00925  */
00926 static void
00927 debug_logger(struct ccnxchat_state *st, int lineno, struct ccn_charbuf *ccnb)
00928 {
00929     struct ccn_charbuf *c;
00930     
00931     if (st->verbose == 0)
00932         return;
00933     c = ccn_charbuf_create();
00934     stampnow(c);
00935     ccn_charbuf_putf(c, "debug.%d %5d", lineno, wrappednow());
00936     if (st != NULL)
00937         ccn_charbuf_putf(c, " pit=%d pot=%d cob=%d buf=%d",
00938                          st->n_pit, st->cc->refcount,
00939                          st->n_cob, (int)st->payload->length);
00940     if (ccnb != NULL) {
00941         ccn_charbuf_putf(c, " ");
00942         ccn_uri_append(c, ccnb->buf, ccnb->length, 1);
00943         append_interest_details(c, ccnb->buf, ccnb->length);
00944     }
00945     fprintf(stderr, "%s\n", ccn_charbuf_as_string(c));
00946     ccn_charbuf_destroy(&c);
00947 }
00948 
00949 static int
00950 append_interest_details(struct ccn_charbuf *c,
00951                         const unsigned char *ccnb, size_t size)
00952 {
00953     struct ccn_parsed_interest interest;
00954     struct ccn_parsed_interest *pi = &interest;
00955     struct ccn_buf_decoder decoder;
00956     struct ccn_buf_decoder *d;
00957     const unsigned char *bloom;
00958     size_t bloom_size = 0;
00959     const unsigned char *comp;    
00960     size_t comp_size = 0;
00961     int i;
00962     int l;
00963     int res;
00964 
00965     res = ccn_parse_interest(ccnb, size, pi, NULL);
00966     if (res < 0)
00967         return(res);
00968     i = pi->offset[CCN_PI_B_Exclude];
00969     l = pi->offset[CCN_PI_E_Exclude] - i;
00970     if (l > 0) {
00971         d = ccn_buf_decoder_start(&decoder, ccnb + i, l);
00972         ccn_charbuf_append_string(c, " excl: ");
00973         ccn_buf_advance(d);
00974         
00975         if (ccn_buf_match_dtag(d, CCN_DTAG_Any)) {
00976             ccn_buf_advance(d);
00977             ccn_charbuf_append_string(c, "* ");
00978             ccn_buf_check_close(d);
00979         }
00980         else if (ccn_buf_match_dtag(d, CCN_DTAG_Bloom)) {
00981             ccn_buf_advance(d);
00982             if (ccn_buf_match_blob(d, &bloom, &bloom_size))
00983                 ccn_buf_advance(d);
00984             ccn_charbuf_append_string(c, "? ");
00985             ccn_buf_check_close(d);
00986         }
00987         while (ccn_buf_match_dtag(d, CCN_DTAG_Component)) {
00988             ccn_buf_advance(d);
00989             comp_size = 0;
00990             if (ccn_buf_match_blob(d, &comp, &comp_size))
00991                 ccn_buf_advance(d);
00992             ccn_uri_append_percentescaped(c, comp, comp_size);
00993             ccn_charbuf_append_string(c, " ");
00994             ccn_buf_check_close(d);
00995             if (ccn_buf_match_dtag(d, CCN_DTAG_Any)) {
00996                 ccn_buf_advance(d);
00997                 ccn_charbuf_append_string(c, "* ");
00998                 ccn_buf_check_close(d);
00999             }
01000             else if (ccn_buf_match_dtag(d, CCN_DTAG_Bloom)) {
01001                 ccn_buf_advance(d);
01002                 if (ccn_buf_match_blob(d, &bloom, &bloom_size))
01003                     ccn_buf_advance(d);
01004                 ccn_charbuf_append_string(c, "? ");
01005                 ccn_buf_check_close(d);
01006             }
01007         }
01008     }
01009     return(0);
01010 }
01011 
01012 static int
01013 append_full_user_name(struct ccn_charbuf *c)
01014 {
01015     int res = -1;
01016 #ifndef C_NO_GECOS
01017     struct passwd *pwd;
01018     pwd = getpwuid(getuid());
01019     if (pwd != NULL) {
01020         res = 0;
01021         ccn_charbuf_putf(c, "%s", pwd->pw_gecos);
01022     }
01023 #endif
01024     return(res);
01025 }
01026 
01027 /**
01028  * classic unix fork/exec to hook up an automatic responder
01029  */
01030 static int
01031 robo_chat(int argc, char **argv)
01032 {
01033     pid_t p;
01034     int res;
01035     int io[2] = {-1, -1};
01036     int oi[2] = {-1, -1};
01037     
01038     res = socketpair(AF_UNIX, SOCK_STREAM, 0, io);
01039     if (res < 0) exit(1);
01040     res = socketpair(AF_UNIX, SOCK_STREAM, 0, oi);
01041     if (res < 0) exit(1);
01042     p = fork();
01043     if (p < 0) {
01044         perror("fork");
01045         exit(1);
01046     }
01047     if (p == 0) {
01048         /* child */
01049         dup2(io[1], 0);
01050         dup2(oi[1], 1);
01051         close(io[0]);
01052         close(io[1]);
01053         close(oi[0]);
01054         close(oi[1]);
01055         execlp("sh", "sh", "-c", option.responder, NULL);
01056         perror(option.responder); /* no return unless there was an error */
01057         exit(1);
01058     }
01059     /* parent */
01060     dup2(io[0], 1);
01061     dup2(oi[0], 0);
01062     close(io[0]);
01063     close(io[1]);
01064     close(oi[0]);
01065     close(oi[1]);
01066     /* Turn off echo and extra output for the robo-chat case */
01067     option.echo = 0;
01068     option.quiet = 1;
01069     return(chat_main(argc, argv));
01070 }
01071 
01072 int
01073 main(int argc, char **argv)
01074 {
01075     parseopts(argc, argv);
01076     if (option.responder != NULL)
01077         return(robo_chat(argc, argv));
01078     if (option.debug)
01079         return(chat_main(argc, argv));
01080     return(lned_run(argc, argv, "Chat.. ", &chat_main));
01081 }
Generated on Tue Aug 21 14:54:16 2012 for Content-Centric Networking in C by  doxygen 1.6.3