blob: 60840b72489370b0933654bdc4c5cd7c466923b1 [file] [log] [blame]
Wentao Shangc0311e52012-12-03 10:38:23 -08001/**
Jeff Thompson5b265a72012-11-12 01:13:08 -08002 * @author: Meki Cherkaoui, Jeff Thompson, Wentao Shang
Jeff Thompson745026e2012-10-13 12:49:20 -07003 * See COPYING for copyright and distribution information.
4 * This class represents the top-level object for communicating with an NDN host.
5 */
Meki Cherkaoui8f173612012-06-06 01:05:40 -07006
Jeff Thompson3c263812012-12-01 17:20:28 -08007var LOG = 0;
Jeff Thompson5b265a72012-11-12 01:13:08 -08008
Jeff Thompsone06b31e2012-09-30 17:19:19 -07009/**
Jeff Thompson5b265a72012-11-12 01:13:08 -080010 * settings is an associative array with the following defaults:
11 * {
Jeff Thompson5b265a72012-11-12 01:13:08 -080012 * getTransport: function() { return new WebSocketTransport(); }
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080013 * getHostAndPort: transport.defaultGetHostAndPort,
Jeff Thompsond771b122013-01-26 19:04:41 -080014 * host: null, // If null, use getHostAndPort when connecting.
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080015 * port: 9696,
Wentao Shangc0311e52012-12-03 10:38:23 -080016 * onopen: function() { if (LOG > 3) console.log("NDN connection established."); }
17 * onclose: function() { if (LOG > 3) console.log("NDN connection closed."); }
Jeff Thompson5b265a72012-11-12 01:13:08 -080018 * }
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080019 *
20 * getHostAndPort is a function, on each call it returns a new { host: host, port: port } or
21 * null if there are no more hosts.
Jeff Thompsone06b31e2012-09-30 17:19:19 -070022 */
Jeff Thompson5b265a72012-11-12 01:13:08 -080023var NDN = function NDN(settings) {
24 settings = (settings || {});
Jeff Thompson5b265a72012-11-12 01:13:08 -080025 var getTransport = (settings.getTransport || function() { return new WebSocketTransport(); });
Wentao Shang0e291c82012-12-02 23:36:29 -080026 this.transport = getTransport();
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080027 this.getHostAndPort = (settings.getHostAndPort || this.transport.defaultGetHostAndPort);
Jeff Thompsond771b122013-01-26 19:04:41 -080028 this.host = (settings.host !== undefined ? settings.host : null);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080029 this.port = (settings.port || 9696);
Wentao Shang0e291c82012-12-02 23:36:29 -080030 this.readyStatus = NDN.UNOPEN;
Wentao Shangd4607392013-01-24 23:08:49 -080031 this.verify = (settings.verify !== undefined ? settings.verify : true);
Wentao Shang0e291c82012-12-02 23:36:29 -080032 // Event handler
Wentao Shangc0311e52012-12-03 10:38:23 -080033 this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("NDN connection established."); });
34 this.onclose = (settings.onclose || function() { if (LOG > 3) console.log("NDN connection closed."); });
Jeff Thompson75771cb2013-01-20 23:27:38 -080035 this.ccndid = null;
Meki Cherkaoui8f173612012-06-06 01:05:40 -070036};
37
Wentao Shang0e291c82012-12-02 23:36:29 -080038NDN.UNOPEN = 0; // created but not opened yet
39NDN.OPENED = 1; // connection to ccnd opened
40NDN.CLOSED = 2; // connection to ccnd closed
Jeff Thompson5b265a72012-11-12 01:13:08 -080041
Wentao Shangb42483a2013-01-03 15:32:32 -080042NDN.ccndIdFetcher = new Name('/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY');
Jeff Thompson5b265a72012-11-12 01:13:08 -080043
Wentao Shangb42483a2013-01-03 15:32:32 -080044NDN.prototype.createRoute = function(host, port) {
Meki Cherkaoui8f173612012-06-06 01:05:40 -070045 this.host=host;
46 this.port=port;
Wentao Shangb42483a2013-01-03 15:32:32 -080047};
Meki Cherkaoui8f173612012-06-06 01:05:40 -070048
Wentao Shang82854bd2012-12-27 14:14:41 -080049
50NDN.KeyStore = new Array();
51
Wentao Shangb5d0c3e2012-12-30 11:12:03 -080052var KeyStoreEntry = function KeyStoreEntry(name, rsa, time) {
Wentao Shang82854bd2012-12-27 14:14:41 -080053 this.keyName = name; // KeyName
Wentao Shang82854bd2012-12-27 14:14:41 -080054 this.rsaKey = rsa; // RSA key
Wentao Shangb5d0c3e2012-12-30 11:12:03 -080055 this.timeStamp = time; // Time Stamp
56};
57
58NDN.addKeyEntry = function(/* KeyStoreEntry */ keyEntry) {
59 var result = NDN.getKeyByName(keyEntry.keyName);
60 if (result == null)
61 NDN.KeyStore.push(keyEntry);
62 else
63 result = keyEntry;
Wentao Shang82854bd2012-12-27 14:14:41 -080064};
65
66NDN.getKeyByName = function(/* KeyName */ name) {
67 var result = null;
68
69 for (var i = 0; i < NDN.KeyStore.length; i++) {
Wentao Shangb42483a2013-01-03 15:32:32 -080070 if (NDN.KeyStore[i].keyName.contentName.match(name.contentName)) {
Wentao Shang82854bd2012-12-27 14:14:41 -080071 if (result == null ||
72 NDN.KeyStore[i].keyName.contentName.components.length > result.keyName.contentName.components.length)
73 result = NDN.KeyStore[i];
74 }
75 }
76
77 return result;
78};
79
Jeff Thompsonbe85be62012-12-13 22:32:01 -080080// For fetching data
81NDN.PITTable = new Array();
82
83var PITEntry = function PITEntry(interest, closure) {
84 this.interest = interest; // Interest
85 this.closure = closure; // Closure
Wentao Shangfcb16262013-01-20 14:42:46 -080086 this.timerID = -1; // Timer ID
Jeff Thompsonbe85be62012-12-13 22:32:01 -080087};
88
Jeff Thompson202728a2013-02-10 22:20:08 -080089/*
90 * Return the entry from NDN.PITTable where the name conforms to the interest selectors, and
91 * the interest name is the longest that matches name.
92 */
Jeff Thompsonbe85be62012-12-13 22:32:01 -080093NDN.getEntryForExpressedInterest = function(/*Name*/ name) {
Jeff Thompsonbe85be62012-12-13 22:32:01 -080094 var result = null;
95
96 for (var i = 0; i < NDN.PITTable.length; i++) {
97 if (NDN.PITTable[i].interest.matches_name(name)) {
98 if (result == null ||
99 NDN.PITTable[i].interest.name.components.length > result.interest.name.components.length)
100 result = NDN.PITTable[i];
101 }
102 }
103
104 return result;
105};
106
Jeff Thompson75771cb2013-01-20 23:27:38 -0800107// For publishing data
108NDN.CSTable = new Array();
109
110var CSEntry = function CSEntry(name, closure) {
111 this.name = name; // String
112 this.closure = closure; // Closure
113};
114
115function getEntryForRegisteredPrefix(name) {
116 for (var i = 0; i < NDN.CSTable.length; i++) {
117 if (NDN.CSTable[i].name.match(name) != null)
118 return NDN.CSTable[i];
119 }
120 return null;
121}
122
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800123/*
124 * Return a function that selects a host at random from hostList and returns { host: host, port: port }.
125 * If no more hosts remain, return null.
126 */
127NDN.makeShuffledGetHostAndPort = function(hostList, port) {
128 // Make a copy.
129 hostList = hostList.slice(0, hostList.length);
130 DataUtils.shuffle(hostList);
131
132 return function() {
133 if (hostList.length == 0)
134 return null;
135
136 return { host: hostList.splice(0, 1)[0], port: port };
137 };
138};
139
Jeff Thompson34419762012-10-15 22:24:12 -0700140/** Encode name as an Interest. If template is not null, use its attributes.
141 * Send the interest to host:port, read the entire response and call
142 * closure.upcall(Closure.UPCALL_CONTENT (or Closure.UPCALL_CONTENT_UNVERIFIED),
Jeff Thompson97f27432012-10-16 00:28:03 -0700143 * new UpcallInfo(this, interest, 0, contentObject)).
Jeff Thompson34419762012-10-15 22:24:12 -0700144 */
145NDN.prototype.expressInterest = function(
146 // Name
147 name,
148 // Closure
149 closure,
150 // Interest
151 template) {
Jeff Thompson5b265a72012-11-12 01:13:08 -0800152 var interest = new Interest(name);
Jeff Thompson34419762012-10-15 22:24:12 -0700153 if (template != null) {
Jeff Thompson4404ab52012-10-21 10:29:48 -0700154 interest.minSuffixComponents = template.minSuffixComponents;
155 interest.maxSuffixComponents = template.maxSuffixComponents;
156 interest.publisherPublicKeyDigest = template.publisherPublicKeyDigest;
157 interest.exclude = template.exclude;
158 interest.childSelector = template.childSelector;
159 interest.answerOriginKind = template.answerOriginKind;
160 interest.scope = template.scope;
161 interest.interestLifetime = template.interestLifetime;
Jeff Thompson34419762012-10-15 22:24:12 -0700162 }
163 else
Jeff Thompson42806a12012-12-29 18:19:39 -0800164 interest.interestLifetime = 4000; // default interest timeout value in milliseconds.
Jeff Thompson34419762012-10-15 22:24:12 -0700165
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800166 if (this.host == null || this.port == null) {
167 if (this.getHostAndPort == null)
168 console.log('ERROR: host OR port NOT SET');
Jeff Thompsona5668d52013-01-26 16:23:27 -0800169 else {
170 var thisNDN = this;
171 this.connectAndExecute
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800172 (function() { thisNDN.reconnectAndExpressInterest(interest, closure); });
Jeff Thompsona5668d52013-01-26 16:23:27 -0800173 }
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800174 }
175 else
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800176 this.reconnectAndExpressInterest(interest, closure);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800177};
Jeff Thompson5b265a72012-11-12 01:13:08 -0800178
Jeff Thompson537ce142013-01-26 20:02:48 -0800179/*
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800180 * If the host and port are different than the ones in this.transport, then call
181 * this.transport.connect to change the connection (or connect for the first time).
182 * Then call expressInterestHelper.
183 */
184NDN.prototype.reconnectAndExpressInterest = function(interest, closure) {
185 if (this.transport.connectedHost != this.host || this.transport.connectedPort != this.port) {
186 var thisNDN = this;
187 this.transport.connect(thisNDN, function() { thisNDN.expressInterestHelper(interest, closure); });
188 }
189 else
190 this.expressInterestHelper(interest, closure);
191};
192
193/*
194 * Do the work of reconnectAndExpressInterest once we know we are connected. Set the PITTable and call
Jeff Thompson537ce142013-01-26 20:02:48 -0800195 * this.transport.send to send the interest.
196 */
197NDN.prototype.expressInterestHelper = function(interest, closure) {
Jeff Thompsone8de5822013-02-17 21:18:49 -0800198 var binaryInterest = encodeToBinaryInterest(interest);
199 var thisNDN = this;
Jeff Thompson537ce142013-01-26 20:02:48 -0800200 //TODO: check local content store first
201 if (closure != null) {
202 var pitEntry = new PITEntry(interest, closure);
203 // TODO: This needs to be a single thread-safe transaction on a global object.
204 NDN.PITTable.push(pitEntry);
205 closure.pitEntry = pitEntry;
Jeff Thompson537ce142013-01-26 20:02:48 -0800206
Jeff Thompsone8de5822013-02-17 21:18:49 -0800207 // Set interest timer.
Jeff Thompsona53e65a2013-02-10 10:52:52 -0800208 var timeoutMilliseconds = (interest.interestLifetime || 4000);
Jeff Thompsone8de5822013-02-17 21:18:49 -0800209 var timeoutCallback = function() {
210 if (LOG > 3) console.log("Interest time out: " + interest.name.to_uri());
Jeff Thompson537ce142013-01-26 20:02:48 -0800211
Jeff Thompsone8de5822013-02-17 21:18:49 -0800212 // Remove PIT entry from NDN.PITTable, even if we add it again later to re-express
213 // the interest because we don't want to match it in the mean time.
Jeff Thompson537ce142013-01-26 20:02:48 -0800214 // TODO: Make this a thread-safe operation on the global PITTable.
215 var index = NDN.PITTable.indexOf(pitEntry);
Jeff Thompson537ce142013-01-26 20:02:48 -0800216 if (index >= 0)
217 NDN.PITTable.splice(index, 1);
Jeff Thompson537ce142013-01-26 20:02:48 -0800218
219 // Raise closure callback
Jeff Thompsone8de5822013-02-17 21:18:49 -0800220 if (closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,
221 new UpcallInfo(thisNDN, interest, 0, null)) == Closure.RESULT_REEXPRESS) {
222 if (LOG > 3) console.log("Re-express interest: " + interest.name.to_uri());
223 pitEntry.timerID = setTimeout(timeoutCallback, timeoutMilliseconds);
224 NDN.PITTable.push(pitEntry);
225 thisNDN.transport.send(binaryInterest);
226 }
227 };
228 pitEntry.timerID = setTimeout(timeoutCallback, timeoutMilliseconds);
Jeff Thompson537ce142013-01-26 20:02:48 -0800229 }
230
Jeff Thompsone8de5822013-02-17 21:18:49 -0800231 this.transport.send(binaryInterest);
Jeff Thompson537ce142013-01-26 20:02:48 -0800232};
233
Jeff Thompson5b265a72012-11-12 01:13:08 -0800234NDN.prototype.registerPrefix = function(name, closure, flag) {
Jeff Thompsond771b122013-01-26 19:04:41 -0800235 var thisNDN = this;
236 var onConnected = function() {
237 if (thisNDN.ccndid == null) {
238 // Fetch ccndid first, then register.
239 var interest = new Interest(NDN.ccndIdFetcher);
240 interest.interestLifetime = 4000; // milliseconds
241 if (LOG>3) console.log('Expressing interest for ccndid from ccnd.');
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800242 thisNDN.reconnectAndExpressInterest
243 (interest, new NDN.FetchCcndidClosure(thisNDN, name, closure, flag));
Jeff Thompsond771b122013-01-26 19:04:41 -0800244 }
245 else
246 thisNDN.registerPrefixHelper(name, closure, flag);
247 };
Jeff Thompson75771cb2013-01-20 23:27:38 -0800248
Jeff Thompsond771b122013-01-26 19:04:41 -0800249 if (this.host == null || this.port == null) {
250 if (this.getHostAndPort == null)
251 console.log('ERROR: host OR port NOT SET');
252 else
253 this.connectAndExecute(onConnected);
254 }
255 else
256 onConnected();
257};
258
259/*
260 * This is a closure to receive the ContentObject for NDN.ccndIdFetcher and call
261 * registerPrefixHelper(name, callerClosure, flag).
262 */
263NDN.FetchCcndidClosure = function FetchCcndidClosure(ndn, name, callerClosure, flag) {
264 // Inherit from Closure.
265 Closure.call(this);
266
267 this.ndn = ndn;
268 this.name = name;
269 this.callerClosure = callerClosure;
270 this.flag = flag;
271};
272
273NDN.FetchCcndidClosure.prototype.upcall = function(kind, upcallInfo) {
274 if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
275 console.log("Timeout while requesting the ccndid. Cannot registerPrefix for " +
276 this.name.to_uri() + " .");
277 return Closure.RESULT_OK;
278 }
279 if (!(kind == Closure.UPCALL_CONTENT ||
280 kind == Closure.UPCALL_CONTENT_UNVERIFIED))
281 // The upcall is not for us.
282 return Closure.RESULT_ERR;
283
284 var co = upcallInfo.contentObject;
285 if (!co.signedInfo || !co.signedInfo.publisher
286 || !co.signedInfo.publisher.publisherPublicKeyDigest)
287 console.log
288 ("ContentObject doesn't have a publisherPublicKeyDigest. Cannot set ccndid and registerPrefix for "
289 + this.name.to_uri() + " .");
290 else {
291 if (LOG>3) console.log('Got ccndid from ccnd.');
292 this.ndn.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
293 if (LOG>3) console.log(this.ndn.ccndid);
294
295 this.ndn.registerPrefixHelper(this.name, this.callerClosure, this.flag);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800296 }
Jeff Thompsond771b122013-01-26 19:04:41 -0800297
298 return Closure.RESULT_OK;
299};
300
Jeff Thompson537ce142013-01-26 20:02:48 -0800301/*
302 * Do the work of registerPrefix once we know we are connected with a ccndid.
303 */
Jeff Thompsond771b122013-01-26 19:04:41 -0800304NDN.prototype.registerPrefixHelper = function(name, closure, flag) {
Jeff Thompson75771cb2013-01-20 23:27:38 -0800305 var fe = new ForwardingEntry('selfreg', name, null, null, 3, 2147483647);
306 var bytes = encodeForwardingEntry(fe);
307
308 var si = new SignedInfo();
309 si.setFields();
310
311 var co = new ContentObject(new Name(), si, bytes, new Signature());
312 co.sign();
313 var coBinary = encodeToBinaryContentObject(co);
314
315 //var nodename = unescape('%00%88%E2%F4%9C%91%16%16%D6%21%8E%A0c%95%A5%A6r%11%E0%A0%82%89%A6%A9%85%AB%D6%E2%065%DB%AF');
316 var nodename = this.ccndid;
317 var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
318
319 var interest = new Interest(interestName);
320 interest.scope = 1;
321 if (LOG > 3) console.log('Send Interest registration packet.');
322
323 var csEntry = new CSEntry(name.getName(), closure);
324 NDN.CSTable.push(csEntry);
325
326 this.transport.send(encodeToBinaryInterest(interest));
Jeff Thompson75771cb2013-01-20 23:27:38 -0800327};
328
329/*
330 * This is called when an entire binary XML element is received, such as a ContentObject or Interest.
331 * Look up in the PITTable and call the closure callback.
332 */
333NDN.prototype.onReceivedElement = function(element) {
Jeff Thompsona46083c2013-01-20 23:55:21 -0800334 if (LOG>3) console.log('Complete element received. Length ' + element.length + '. Start decoding.');
Jeff Thompson75771cb2013-01-20 23:27:38 -0800335 var decoder = new BinaryXMLDecoder(element);
336 // Dispatch according to packet type
337 if (decoder.peekStartElement(CCNProtocolDTags.Interest)) { // Interest packet
338 if (LOG > 3) console.log('Interest packet received.');
339
340 var interest = new Interest();
341 interest.from_ccnb(decoder);
342 if (LOG > 3) console.log(interest);
343 var nameStr = escape(interest.name.getName());
344 if (LOG > 3) console.log(nameStr);
345
346 var entry = getEntryForRegisteredPrefix(nameStr);
347 if (entry != null) {
348 //console.log(entry);
349 var info = new UpcallInfo(this, interest, 0, null);
350 var ret = entry.closure.upcall(Closure.UPCALL_INTEREST, info);
351 if (ret == Closure.RESULT_INTEREST_CONSUMED && info.contentObject != null)
352 this.transport.send(encodeToBinaryContentObject(info.contentObject));
353 }
354 } else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) { // Content packet
355 if (LOG > 3) console.log('ContentObject packet received.');
356
357 var co = new ContentObject();
358 co.from_ccnb(decoder);
359
Jeff Thompsond771b122013-01-26 19:04:41 -0800360 var pitEntry = NDN.getEntryForExpressedInterest(co.name);
361 if (pitEntry != null) {
Jeff Thompson0ee721e2013-02-18 17:46:55 -0800362 // Cancel interest timer
363 clearTimeout(pitEntry.timerID);
364
Jeff Thompsond771b122013-01-26 19:04:41 -0800365 // Remove PIT entry from NDN.PITTable
366 var index = NDN.PITTable.indexOf(pitEntry);
367 if (index >= 0)
368 NDN.PITTable.splice(index, 1);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800369
Jeff Thompsond771b122013-01-26 19:04:41 -0800370 var currentClosure = pitEntry.closure;
Jeff Thompson0ee721e2013-02-18 17:46:55 -0800371
Jeff Thompsond771b122013-01-26 19:04:41 -0800372 if (this.verify == false) {
373 // Pass content up without verifying the signature
374 currentClosure.upcall(Closure.UPCALL_CONTENT_UNVERIFIED, new UpcallInfo(this, null, 0, co));
375 return;
Jeff Thompson75771cb2013-01-20 23:27:38 -0800376 }
Wentao Shangd4607392013-01-24 23:08:49 -0800377
Jeff Thompsond771b122013-01-26 19:04:41 -0800378 // Key verification
379
380 // Recursive key fetching & verification closure
381 var KeyFetchClosure = function KeyFetchClosure(content, closure, key, sig, wit) {
382 this.contentObject = content; // unverified content object
383 this.closure = closure; // closure corresponding to the contentObject
384 this.keyName = key; // name of current key to be fetched
385 this.sigHex = sig; // hex signature string to be verified
386 this.witness = wit;
387
388 Closure.call(this);
389 };
390
391 var thisNDN = this;
392 KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
393 if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
394 console.log("In KeyFetchClosure.upcall: interest time out.");
395 console.log(this.keyName.contentName.getName());
396 } else if (kind == Closure.UPCALL_CONTENT) {
397 //console.log("In KeyFetchClosure.upcall: signature verification passed");
398
399 var rsakey = decodeSubjectPublicKeyInfo(upcallInfo.contentObject.content);
400 var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.witness, this.sigHex);
401
402 var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
403 //console.log("raise encapsulated closure");
404 this.closure.upcall(flag, new UpcallInfo(thisNDN, null, 0, this.contentObject));
405
406 // Store key in cache
407 var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
408 NDN.addKeyEntry(keyEntry);
409 //console.log(NDN.KeyStore);
410 } else if (kind == Closure.UPCALL_CONTENT_BAD) {
411 console.log("In KeyFetchClosure.upcall: signature verification failed");
Wentao Shangd4607392013-01-24 23:08:49 -0800412 }
Jeff Thompsond771b122013-01-26 19:04:41 -0800413 };
Jeff Thompson75771cb2013-01-20 23:27:38 -0800414
Jeff Thompsond771b122013-01-26 19:04:41 -0800415 if (co.signedInfo && co.signedInfo.locator && co.signature) {
416 if (LOG > 3) console.log("Key verification...");
417 var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
Jeff Thompson75771cb2013-01-20 23:27:38 -0800418
Jeff Thompsond771b122013-01-26 19:04:41 -0800419 var wit = null;
420 if (co.signature.Witness != null) {
421 wit = new Witness();
422 wit.decode(co.signature.Witness);
423 }
Jeff Thompson75771cb2013-01-20 23:27:38 -0800424
Jeff Thompsond771b122013-01-26 19:04:41 -0800425 var keylocator = co.signedInfo.locator;
426 if (keylocator.type == KeyLocatorType.KEYNAME) {
427 if (LOG > 3) console.log("KeyLocator contains KEYNAME");
428 //var keyname = keylocator.keyName.contentName.getName();
429 //console.log(nameStr);
430 //console.log(keyname);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800431
Jeff Thompsond771b122013-01-26 19:04:41 -0800432 if (keylocator.keyName.contentName.match(co.name)) {
433 if (LOG > 3) console.log("Content is key itself");
Jeff Thompson75771cb2013-01-20 23:27:38 -0800434
Jeff Thompsond771b122013-01-26 19:04:41 -0800435 var rsakey = decodeSubjectPublicKeyInfo(co.content);
436 var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
437 var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
438
439 currentClosure.upcall(flag, new UpcallInfo(this, null, 0, co));
440
441 // SWT: We don't need to store key here since the same key will be
442 // stored again in the closure.
443 //var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
444 //NDN.addKeyEntry(keyEntry);
445 //console.log(NDN.KeyStore);
446 } else {
447 // Check local key store
448 var keyEntry = NDN.getKeyByName(keylocator.keyName);
449 if (keyEntry) {
450 // Key found, verify now
451 if (LOG > 3) console.log("Local key cache hit");
452 var rsakey = keyEntry.rsaKey;
Jeff Thompson75771cb2013-01-20 23:27:38 -0800453 var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
454 var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
Jeff Thompson75771cb2013-01-20 23:27:38 -0800455
Jeff Thompsond771b122013-01-26 19:04:41 -0800456 // Raise callback
457 currentClosure.upcall(flag, new UpcallInfo(this, null, 0, co));
458 } else {
459 // Not found, fetch now
460 if (LOG > 3) console.log("Fetch key according to keylocator");
461 var nextClosure = new KeyFetchClosure(co, currentClosure, keylocator.keyName, sigHex, wit);
462 this.expressInterest(keylocator.keyName.contentName.getPrefix(4), nextClosure);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800463 }
Jeff Thompson75771cb2013-01-20 23:27:38 -0800464 }
Jeff Thompsond771b122013-01-26 19:04:41 -0800465 } else if (keylocator.type == KeyLocatorType.KEY) {
466 if (LOG > 3) console.log("Keylocator contains KEY");
467
468 var rsakey = decodeSubjectPublicKeyInfo(co.signedInfo.locator.publicKey);
469 var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
470
471 var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
472 // Raise callback
473 currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(this, null, 0, co));
474
475 // Since KeyLocator does not contain key name for this key,
476 // we have no way to store it as a key entry in KeyStore.
477 } else {
478 var cert = keylocator.certificate;
479 console.log("KeyLocator contains CERT");
480 console.log(cert);
481
482 // TODO: verify certificate
Jeff Thompson75771cb2013-01-20 23:27:38 -0800483 }
484 }
485 }
486 } else
487 console.log('Incoming packet is not Interest or ContentObject. Discard now.');
Wentao Shangb42483a2013-01-03 15:32:32 -0800488};
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800489
490/*
491 * Assume this.getHostAndPort is not null. This is called when this.host is null or its host
Jeff Thompsona5668d52013-01-26 16:23:27 -0800492 * is not alive. Get a host and port, connect, then execute onConnected().
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800493 */
Jeff Thompsona5668d52013-01-26 16:23:27 -0800494NDN.prototype.connectAndExecute = function(onConnected) {
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800495 var hostAndPort = this.getHostAndPort();
496 if (hostAndPort == null) {
497 console.log('ERROR: No more hosts from getHostAndPort');
498 this.host = null;
499 return;
500 }
501
502 if (hostAndPort.host == this.host && hostAndPort.port == this.port) {
503 console.log('ERROR: The host returned by getHostAndPort is not alive: ' +
504 this.host + ":" + this.port);
505 return;
506 }
507
508 this.host = hostAndPort.host;
509 this.port = hostAndPort.port;
Jeff Thompsona5668d52013-01-26 16:23:27 -0800510 if (LOG>3) console.log("Connect: trying host from getHostAndPort: " + this.host);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800511
Jeff Thompson75771cb2013-01-20 23:27:38 -0800512 // Fetch any content.
513 var interest = new Interest(new Name("/"));
Jeff Thompson42806a12012-12-29 18:19:39 -0800514 interest.interestLifetime = 4000; // milliseconds
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800515
516 var thisNDN = this;
517 var timerID = setTimeout(function() {
Jeff Thompsona5668d52013-01-26 16:23:27 -0800518 if (LOG>3) console.log("Connect: timeout waiting for host " + thisNDN.host);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800519 // Try again.
Jeff Thompsona5668d52013-01-26 16:23:27 -0800520 thisNDN.connectAndExecute(onConnected);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800521 }, 3000);
522
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800523 this.reconnectAndExpressInterest
524 (interest, new NDN.ConnectClosure(this, onConnected, timerID));
Wentao Shangb42483a2013-01-03 15:32:32 -0800525};
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800526
Jeff Thompsona5668d52013-01-26 16:23:27 -0800527NDN.ConnectClosure = function ConnectClosure(ndn, onConnected, timerID) {
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800528 // Inherit from Closure.
529 Closure.call(this);
530
531 this.ndn = ndn;
Jeff Thompsona5668d52013-01-26 16:23:27 -0800532 this.onConnected = onConnected;
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800533 this.timerID = timerID;
534};
535
536NDN.ConnectClosure.prototype.upcall = function(kind, upcallInfo) {
537 if (!(kind == Closure.UPCALL_CONTENT ||
Jeff Thompson75771cb2013-01-20 23:27:38 -0800538 kind == Closure.UPCALL_CONTENT_UNVERIFIED))
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800539 // The upcall is not for us.
540 return Closure.RESULT_ERR;
541
Jeff Thompsona5668d52013-01-26 16:23:27 -0800542 // The host is alive, so cancel the timeout and continue with onConnected().
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800543 clearTimeout(this.timerID);
Jeff Thompsond771b122013-01-26 19:04:41 -0800544
545 // Call NDN.onopen after success
546 this.ndn.readyStatus = NDN.OPENED;
547 this.ndn.onopen();
548
Jeff Thompsona5668d52013-01-26 16:23:27 -0800549 this.onConnected();
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800550
551 return Closure.RESULT_OK;
552};
553
Jeff Thompson75771cb2013-01-20 23:27:38 -0800554/*
555 * A BinaryXmlElementReader lets you call onReceivedData multiple times which uses a
556 * BinaryXMLStructureDecoder to detect the end of a binary XML element and calls
557 * elementListener.onReceivedElement(element) with the element.
558 * This handles the case where a single call to onReceivedData may contain multiple elements.
559 */
560var BinaryXmlElementReader = function BinaryXmlElementReader(elementListener) {
561 this.elementListener = elementListener;
562 this.dataParts = [];
563 this.structureDecoder = new BinaryXMLStructureDecoder();
564};
565
566BinaryXmlElementReader.prototype.onReceivedData = function(/* Uint8Array */ rawData) {
Jeff Thompson75771cb2013-01-20 23:27:38 -0800567 // Process multiple objects in the data.
568 while(true) {
569 // Scan the input to check if a whole ccnb object has been read.
570 this.structureDecoder.seek(0);
571 if (this.structureDecoder.findElementEnd(rawData)) {
572 // Got the remainder of an object. Report to the caller.
573 this.dataParts.push(rawData.subarray(0, this.structureDecoder.offset));
Jeff Thompson0790af82013-01-26 19:54:27 -0800574 var element = DataUtils.concatArrays(this.dataParts);
575 this.dataParts = [];
576 try {
577 this.elementListener.onReceivedElement(element);
578 } catch (ex) {
579 console.log("BinaryXmlElementReader: ignoring exception from onReceivedElement: " + ex);
580 }
Jeff Thompson75771cb2013-01-20 23:27:38 -0800581
582 // Need to read a new object.
583 rawData = rawData.subarray(this.structureDecoder.offset, rawData.length);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800584 this.structureDecoder = new BinaryXMLStructureDecoder();
585 if (rawData.length == 0)
586 // No more data in the packet.
587 return;
588
589 // else loop back to decode.
590 }
591 else {
592 // Save for a later call to concatArrays so that we only copy data once.
593 this.dataParts.push(rawData);
Jeff Thompsona46083c2013-01-20 23:55:21 -0800594 if (LOG>3) console.log('Incomplete packet received. Length ' + rawData.length + '. Wait for more input.');
Jeff Thompson75771cb2013-01-20 23:27:38 -0800595 return;
596 }
597 }
598}