blob: 33151524bb3196062db60601541ca3c957394330 [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 Thompsonac0ed602013-02-18 21:52:42 -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,
Jeff Thompsonac0ed602013-02-18 21:52:42 -080016 * onopen: function() { if (LOG > 3) console.log("NDN connection established."); },
17 * onclose: function() { if (LOG > 3) console.log("NDN connection closed."); },
18 * verify: true // If false, don't verify and call upcall with Closure.UPCALL_CONTENT_UNVERIFIED.
Jeff Thompson5b265a72012-11-12 01:13:08 -080019 * }
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080020 *
21 * getHostAndPort is a function, on each call it returns a new { host: host, port: port } or
22 * null if there are no more hosts.
Jeff Thompsone06b31e2012-09-30 17:19:19 -070023 */
Jeff Thompson5b265a72012-11-12 01:13:08 -080024var NDN = function NDN(settings) {
25 settings = (settings || {});
Jeff Thompson5b265a72012-11-12 01:13:08 -080026 var getTransport = (settings.getTransport || function() { return new WebSocketTransport(); });
Wentao Shang0e291c82012-12-02 23:36:29 -080027 this.transport = getTransport();
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080028 this.getHostAndPort = (settings.getHostAndPort || this.transport.defaultGetHostAndPort);
Jeff Thompsond771b122013-01-26 19:04:41 -080029 this.host = (settings.host !== undefined ? settings.host : null);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -080030 this.port = (settings.port || 9696);
Wentao Shang0e291c82012-12-02 23:36:29 -080031 this.readyStatus = NDN.UNOPEN;
Wentao Shangd4607392013-01-24 23:08:49 -080032 this.verify = (settings.verify !== undefined ? settings.verify : true);
Wentao Shang0e291c82012-12-02 23:36:29 -080033 // Event handler
Wentao Shangc0311e52012-12-03 10:38:23 -080034 this.onopen = (settings.onopen || function() { if (LOG > 3) console.log("NDN connection established."); });
35 this.onclose = (settings.onclose || function() { if (LOG > 3) console.log("NDN connection closed."); });
Jeff Thompson75771cb2013-01-20 23:27:38 -080036 this.ccndid = null;
Meki Cherkaoui8f173612012-06-06 01:05:40 -070037};
38
Wentao Shang0e291c82012-12-02 23:36:29 -080039NDN.UNOPEN = 0; // created but not opened yet
40NDN.OPENED = 1; // connection to ccnd opened
41NDN.CLOSED = 2; // connection to ccnd closed
Jeff Thompson5b265a72012-11-12 01:13:08 -080042
Wentao Shangb42483a2013-01-03 15:32:32 -080043NDN.ccndIdFetcher = new Name('/%C1.M.S.localhost/%C1.M.SRV/ccnd/KEY');
Jeff Thompson5b265a72012-11-12 01:13:08 -080044
Wentao Shangb42483a2013-01-03 15:32:32 -080045NDN.prototype.createRoute = function(host, port) {
Meki Cherkaoui8f173612012-06-06 01:05:40 -070046 this.host=host;
47 this.port=port;
Wentao Shangb42483a2013-01-03 15:32:32 -080048};
Meki Cherkaoui8f173612012-06-06 01:05:40 -070049
Wentao Shang82854bd2012-12-27 14:14:41 -080050
51NDN.KeyStore = new Array();
52
Wentao Shangb5d0c3e2012-12-30 11:12:03 -080053var KeyStoreEntry = function KeyStoreEntry(name, rsa, time) {
Wentao Shang82854bd2012-12-27 14:14:41 -080054 this.keyName = name; // KeyName
Wentao Shang82854bd2012-12-27 14:14:41 -080055 this.rsaKey = rsa; // RSA key
Wentao Shangb5d0c3e2012-12-30 11:12:03 -080056 this.timeStamp = time; // Time Stamp
57};
58
59NDN.addKeyEntry = function(/* KeyStoreEntry */ keyEntry) {
60 var result = NDN.getKeyByName(keyEntry.keyName);
61 if (result == null)
62 NDN.KeyStore.push(keyEntry);
63 else
64 result = keyEntry;
Wentao Shang82854bd2012-12-27 14:14:41 -080065};
66
67NDN.getKeyByName = function(/* KeyName */ name) {
68 var result = null;
69
70 for (var i = 0; i < NDN.KeyStore.length; i++) {
Wentao Shangb42483a2013-01-03 15:32:32 -080071 if (NDN.KeyStore[i].keyName.contentName.match(name.contentName)) {
Wentao Shang82854bd2012-12-27 14:14:41 -080072 if (result == null ||
73 NDN.KeyStore[i].keyName.contentName.components.length > result.keyName.contentName.components.length)
74 result = NDN.KeyStore[i];
75 }
76 }
77
78 return result;
79};
80
Jeff Thompsonbe85be62012-12-13 22:32:01 -080081// For fetching data
82NDN.PITTable = new Array();
83
84var PITEntry = function PITEntry(interest, closure) {
85 this.interest = interest; // Interest
86 this.closure = closure; // Closure
Wentao Shangfcb16262013-01-20 14:42:46 -080087 this.timerID = -1; // Timer ID
Jeff Thompsonbe85be62012-12-13 22:32:01 -080088};
89
Jeff Thompson202728a2013-02-10 22:20:08 -080090/*
91 * Return the entry from NDN.PITTable where the name conforms to the interest selectors, and
92 * the interest name is the longest that matches name.
93 */
Jeff Thompsonbe85be62012-12-13 22:32:01 -080094NDN.getEntryForExpressedInterest = function(/*Name*/ name) {
Jeff Thompsonbe85be62012-12-13 22:32:01 -080095 var result = null;
96
97 for (var i = 0; i < NDN.PITTable.length; i++) {
98 if (NDN.PITTable[i].interest.matches_name(name)) {
99 if (result == null ||
100 NDN.PITTable[i].interest.name.components.length > result.interest.name.components.length)
101 result = NDN.PITTable[i];
102 }
103 }
104
105 return result;
106};
107
Jeff Thompson75771cb2013-01-20 23:27:38 -0800108// For publishing data
109NDN.CSTable = new Array();
110
111var CSEntry = function CSEntry(name, closure) {
112 this.name = name; // String
113 this.closure = closure; // Closure
114};
115
116function getEntryForRegisteredPrefix(name) {
117 for (var i = 0; i < NDN.CSTable.length; i++) {
118 if (NDN.CSTable[i].name.match(name) != null)
119 return NDN.CSTable[i];
120 }
121 return null;
122}
123
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800124/*
125 * Return a function that selects a host at random from hostList and returns { host: host, port: port }.
126 * If no more hosts remain, return null.
127 */
128NDN.makeShuffledGetHostAndPort = function(hostList, port) {
129 // Make a copy.
130 hostList = hostList.slice(0, hostList.length);
131 DataUtils.shuffle(hostList);
132
133 return function() {
134 if (hostList.length == 0)
135 return null;
136
137 return { host: hostList.splice(0, 1)[0], port: port };
138 };
139};
140
Jeff Thompson34419762012-10-15 22:24:12 -0700141/** Encode name as an Interest. If template is not null, use its attributes.
142 * Send the interest to host:port, read the entire response and call
143 * closure.upcall(Closure.UPCALL_CONTENT (or Closure.UPCALL_CONTENT_UNVERIFIED),
Jeff Thompson97f27432012-10-16 00:28:03 -0700144 * new UpcallInfo(this, interest, 0, contentObject)).
Jeff Thompson34419762012-10-15 22:24:12 -0700145 */
146NDN.prototype.expressInterest = function(
147 // Name
148 name,
149 // Closure
150 closure,
151 // Interest
152 template) {
Jeff Thompson5b265a72012-11-12 01:13:08 -0800153 var interest = new Interest(name);
Jeff Thompson34419762012-10-15 22:24:12 -0700154 if (template != null) {
Jeff Thompson4404ab52012-10-21 10:29:48 -0700155 interest.minSuffixComponents = template.minSuffixComponents;
156 interest.maxSuffixComponents = template.maxSuffixComponents;
157 interest.publisherPublicKeyDigest = template.publisherPublicKeyDigest;
158 interest.exclude = template.exclude;
159 interest.childSelector = template.childSelector;
160 interest.answerOriginKind = template.answerOriginKind;
161 interest.scope = template.scope;
162 interest.interestLifetime = template.interestLifetime;
Jeff Thompson34419762012-10-15 22:24:12 -0700163 }
164 else
Jeff Thompson42806a12012-12-29 18:19:39 -0800165 interest.interestLifetime = 4000; // default interest timeout value in milliseconds.
Jeff Thompson34419762012-10-15 22:24:12 -0700166
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800167 if (this.host == null || this.port == null) {
168 if (this.getHostAndPort == null)
169 console.log('ERROR: host OR port NOT SET');
Jeff Thompsona5668d52013-01-26 16:23:27 -0800170 else {
171 var thisNDN = this;
172 this.connectAndExecute
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800173 (function() { thisNDN.reconnectAndExpressInterest(interest, closure); });
Jeff Thompsona5668d52013-01-26 16:23:27 -0800174 }
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800175 }
176 else
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800177 this.reconnectAndExpressInterest(interest, closure);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800178};
Jeff Thompson5b265a72012-11-12 01:13:08 -0800179
Jeff Thompson537ce142013-01-26 20:02:48 -0800180/*
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800181 * If the host and port are different than the ones in this.transport, then call
182 * this.transport.connect to change the connection (or connect for the first time).
183 * Then call expressInterestHelper.
184 */
185NDN.prototype.reconnectAndExpressInterest = function(interest, closure) {
186 if (this.transport.connectedHost != this.host || this.transport.connectedPort != this.port) {
187 var thisNDN = this;
188 this.transport.connect(thisNDN, function() { thisNDN.expressInterestHelper(interest, closure); });
189 }
190 else
191 this.expressInterestHelper(interest, closure);
192};
193
194/*
195 * Do the work of reconnectAndExpressInterest once we know we are connected. Set the PITTable and call
Jeff Thompson537ce142013-01-26 20:02:48 -0800196 * this.transport.send to send the interest.
197 */
198NDN.prototype.expressInterestHelper = function(interest, closure) {
Jeff Thompsone8de5822013-02-17 21:18:49 -0800199 var binaryInterest = encodeToBinaryInterest(interest);
200 var thisNDN = this;
Jeff Thompson537ce142013-01-26 20:02:48 -0800201 //TODO: check local content store first
202 if (closure != null) {
203 var pitEntry = new PITEntry(interest, closure);
204 // TODO: This needs to be a single thread-safe transaction on a global object.
205 NDN.PITTable.push(pitEntry);
206 closure.pitEntry = pitEntry;
Jeff Thompson537ce142013-01-26 20:02:48 -0800207
Jeff Thompsone8de5822013-02-17 21:18:49 -0800208 // Set interest timer.
Jeff Thompsona53e65a2013-02-10 10:52:52 -0800209 var timeoutMilliseconds = (interest.interestLifetime || 4000);
Jeff Thompsone8de5822013-02-17 21:18:49 -0800210 var timeoutCallback = function() {
211 if (LOG > 3) console.log("Interest time out: " + interest.name.to_uri());
Jeff Thompson537ce142013-01-26 20:02:48 -0800212
Jeff Thompsone8de5822013-02-17 21:18:49 -0800213 // Remove PIT entry from NDN.PITTable, even if we add it again later to re-express
214 // the interest because we don't want to match it in the mean time.
Jeff Thompson537ce142013-01-26 20:02:48 -0800215 // TODO: Make this a thread-safe operation on the global PITTable.
216 var index = NDN.PITTable.indexOf(pitEntry);
Jeff Thompson537ce142013-01-26 20:02:48 -0800217 if (index >= 0)
218 NDN.PITTable.splice(index, 1);
Jeff Thompson537ce142013-01-26 20:02:48 -0800219
220 // Raise closure callback
Jeff Thompsone8de5822013-02-17 21:18:49 -0800221 if (closure.upcall(Closure.UPCALL_INTEREST_TIMED_OUT,
222 new UpcallInfo(thisNDN, interest, 0, null)) == Closure.RESULT_REEXPRESS) {
223 if (LOG > 3) console.log("Re-express interest: " + interest.name.to_uri());
224 pitEntry.timerID = setTimeout(timeoutCallback, timeoutMilliseconds);
225 NDN.PITTable.push(pitEntry);
226 thisNDN.transport.send(binaryInterest);
227 }
228 };
229 pitEntry.timerID = setTimeout(timeoutCallback, timeoutMilliseconds);
Jeff Thompson537ce142013-01-26 20:02:48 -0800230 }
231
Jeff Thompsone8de5822013-02-17 21:18:49 -0800232 this.transport.send(binaryInterest);
Jeff Thompson537ce142013-01-26 20:02:48 -0800233};
234
Jeff Thompson5b265a72012-11-12 01:13:08 -0800235NDN.prototype.registerPrefix = function(name, closure, flag) {
Jeff Thompsond771b122013-01-26 19:04:41 -0800236 var thisNDN = this;
237 var onConnected = function() {
238 if (thisNDN.ccndid == null) {
239 // Fetch ccndid first, then register.
240 var interest = new Interest(NDN.ccndIdFetcher);
241 interest.interestLifetime = 4000; // milliseconds
242 if (LOG>3) console.log('Expressing interest for ccndid from ccnd.');
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800243 thisNDN.reconnectAndExpressInterest
244 (interest, new NDN.FetchCcndidClosure(thisNDN, name, closure, flag));
Jeff Thompsond771b122013-01-26 19:04:41 -0800245 }
246 else
247 thisNDN.registerPrefixHelper(name, closure, flag);
248 };
Jeff Thompson75771cb2013-01-20 23:27:38 -0800249
Jeff Thompsond771b122013-01-26 19:04:41 -0800250 if (this.host == null || this.port == null) {
251 if (this.getHostAndPort == null)
252 console.log('ERROR: host OR port NOT SET');
253 else
254 this.connectAndExecute(onConnected);
255 }
256 else
257 onConnected();
258};
259
260/*
261 * This is a closure to receive the ContentObject for NDN.ccndIdFetcher and call
262 * registerPrefixHelper(name, callerClosure, flag).
263 */
264NDN.FetchCcndidClosure = function FetchCcndidClosure(ndn, name, callerClosure, flag) {
265 // Inherit from Closure.
266 Closure.call(this);
267
268 this.ndn = ndn;
269 this.name = name;
270 this.callerClosure = callerClosure;
271 this.flag = flag;
272};
273
274NDN.FetchCcndidClosure.prototype.upcall = function(kind, upcallInfo) {
275 if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
276 console.log("Timeout while requesting the ccndid. Cannot registerPrefix for " +
277 this.name.to_uri() + " .");
278 return Closure.RESULT_OK;
279 }
280 if (!(kind == Closure.UPCALL_CONTENT ||
281 kind == Closure.UPCALL_CONTENT_UNVERIFIED))
282 // The upcall is not for us.
283 return Closure.RESULT_ERR;
284
285 var co = upcallInfo.contentObject;
286 if (!co.signedInfo || !co.signedInfo.publisher
287 || !co.signedInfo.publisher.publisherPublicKeyDigest)
288 console.log
289 ("ContentObject doesn't have a publisherPublicKeyDigest. Cannot set ccndid and registerPrefix for "
290 + this.name.to_uri() + " .");
291 else {
292 if (LOG>3) console.log('Got ccndid from ccnd.');
293 this.ndn.ccndid = co.signedInfo.publisher.publisherPublicKeyDigest;
294 if (LOG>3) console.log(this.ndn.ccndid);
295
296 this.ndn.registerPrefixHelper(this.name, this.callerClosure, this.flag);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800297 }
Jeff Thompsond771b122013-01-26 19:04:41 -0800298
299 return Closure.RESULT_OK;
300};
301
Jeff Thompson537ce142013-01-26 20:02:48 -0800302/*
303 * Do the work of registerPrefix once we know we are connected with a ccndid.
304 */
Jeff Thompsond771b122013-01-26 19:04:41 -0800305NDN.prototype.registerPrefixHelper = function(name, closure, flag) {
Jeff Thompson75771cb2013-01-20 23:27:38 -0800306 var fe = new ForwardingEntry('selfreg', name, null, null, 3, 2147483647);
307 var bytes = encodeForwardingEntry(fe);
308
309 var si = new SignedInfo();
310 si.setFields();
311
312 var co = new ContentObject(new Name(), si, bytes, new Signature());
313 co.sign();
314 var coBinary = encodeToBinaryContentObject(co);
315
316 //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');
317 var nodename = this.ccndid;
318 var interestName = new Name(['ccnx', nodename, 'selfreg', coBinary]);
319
320 var interest = new Interest(interestName);
321 interest.scope = 1;
322 if (LOG > 3) console.log('Send Interest registration packet.');
323
324 var csEntry = new CSEntry(name.getName(), closure);
325 NDN.CSTable.push(csEntry);
326
327 this.transport.send(encodeToBinaryInterest(interest));
Jeff Thompson75771cb2013-01-20 23:27:38 -0800328};
329
330/*
331 * This is called when an entire binary XML element is received, such as a ContentObject or Interest.
332 * Look up in the PITTable and call the closure callback.
333 */
334NDN.prototype.onReceivedElement = function(element) {
Jeff Thompsona46083c2013-01-20 23:55:21 -0800335 if (LOG>3) console.log('Complete element received. Length ' + element.length + '. Start decoding.');
Jeff Thompson75771cb2013-01-20 23:27:38 -0800336 var decoder = new BinaryXMLDecoder(element);
337 // Dispatch according to packet type
338 if (decoder.peekStartElement(CCNProtocolDTags.Interest)) { // Interest packet
339 if (LOG > 3) console.log('Interest packet received.');
340
341 var interest = new Interest();
342 interest.from_ccnb(decoder);
343 if (LOG > 3) console.log(interest);
344 var nameStr = escape(interest.name.getName());
345 if (LOG > 3) console.log(nameStr);
346
347 var entry = getEntryForRegisteredPrefix(nameStr);
348 if (entry != null) {
349 //console.log(entry);
350 var info = new UpcallInfo(this, interest, 0, null);
351 var ret = entry.closure.upcall(Closure.UPCALL_INTEREST, info);
352 if (ret == Closure.RESULT_INTEREST_CONSUMED && info.contentObject != null)
353 this.transport.send(encodeToBinaryContentObject(info.contentObject));
354 }
355 } else if (decoder.peekStartElement(CCNProtocolDTags.ContentObject)) { // Content packet
356 if (LOG > 3) console.log('ContentObject packet received.');
357
358 var co = new ContentObject();
359 co.from_ccnb(decoder);
360
Jeff Thompsond771b122013-01-26 19:04:41 -0800361 var pitEntry = NDN.getEntryForExpressedInterest(co.name);
362 if (pitEntry != null) {
Jeff Thompson0ee721e2013-02-18 17:46:55 -0800363 // Cancel interest timer
364 clearTimeout(pitEntry.timerID);
365
Jeff Thompsond771b122013-01-26 19:04:41 -0800366 // Remove PIT entry from NDN.PITTable
367 var index = NDN.PITTable.indexOf(pitEntry);
368 if (index >= 0)
369 NDN.PITTable.splice(index, 1);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800370
Jeff Thompsond771b122013-01-26 19:04:41 -0800371 var currentClosure = pitEntry.closure;
Jeff Thompson0ee721e2013-02-18 17:46:55 -0800372
Jeff Thompsond771b122013-01-26 19:04:41 -0800373 if (this.verify == false) {
374 // Pass content up without verifying the signature
375 currentClosure.upcall(Closure.UPCALL_CONTENT_UNVERIFIED, new UpcallInfo(this, null, 0, co));
376 return;
Jeff Thompson75771cb2013-01-20 23:27:38 -0800377 }
Wentao Shangd4607392013-01-24 23:08:49 -0800378
Jeff Thompsond771b122013-01-26 19:04:41 -0800379 // Key verification
380
381 // Recursive key fetching & verification closure
382 var KeyFetchClosure = function KeyFetchClosure(content, closure, key, sig, wit) {
383 this.contentObject = content; // unverified content object
384 this.closure = closure; // closure corresponding to the contentObject
385 this.keyName = key; // name of current key to be fetched
386 this.sigHex = sig; // hex signature string to be verified
387 this.witness = wit;
388
389 Closure.call(this);
390 };
391
392 var thisNDN = this;
393 KeyFetchClosure.prototype.upcall = function(kind, upcallInfo) {
394 if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) {
395 console.log("In KeyFetchClosure.upcall: interest time out.");
396 console.log(this.keyName.contentName.getName());
397 } else if (kind == Closure.UPCALL_CONTENT) {
398 //console.log("In KeyFetchClosure.upcall: signature verification passed");
399
400 var rsakey = decodeSubjectPublicKeyInfo(upcallInfo.contentObject.content);
401 var verified = rsakey.verifyByteArray(this.contentObject.rawSignatureData, this.witness, this.sigHex);
402
403 var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
404 //console.log("raise encapsulated closure");
405 this.closure.upcall(flag, new UpcallInfo(thisNDN, null, 0, this.contentObject));
406
407 // Store key in cache
408 var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
409 NDN.addKeyEntry(keyEntry);
410 //console.log(NDN.KeyStore);
411 } else if (kind == Closure.UPCALL_CONTENT_BAD) {
412 console.log("In KeyFetchClosure.upcall: signature verification failed");
Wentao Shangd4607392013-01-24 23:08:49 -0800413 }
Jeff Thompsond771b122013-01-26 19:04:41 -0800414 };
Jeff Thompson75771cb2013-01-20 23:27:38 -0800415
Jeff Thompsond771b122013-01-26 19:04:41 -0800416 if (co.signedInfo && co.signedInfo.locator && co.signature) {
417 if (LOG > 3) console.log("Key verification...");
418 var sigHex = DataUtils.toHex(co.signature.signature).toLowerCase();
Jeff Thompson75771cb2013-01-20 23:27:38 -0800419
Jeff Thompsond771b122013-01-26 19:04:41 -0800420 var wit = null;
421 if (co.signature.Witness != null) {
422 wit = new Witness();
423 wit.decode(co.signature.Witness);
424 }
Jeff Thompson75771cb2013-01-20 23:27:38 -0800425
Jeff Thompsond771b122013-01-26 19:04:41 -0800426 var keylocator = co.signedInfo.locator;
427 if (keylocator.type == KeyLocatorType.KEYNAME) {
428 if (LOG > 3) console.log("KeyLocator contains KEYNAME");
429 //var keyname = keylocator.keyName.contentName.getName();
430 //console.log(nameStr);
431 //console.log(keyname);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800432
Jeff Thompsond771b122013-01-26 19:04:41 -0800433 if (keylocator.keyName.contentName.match(co.name)) {
434 if (LOG > 3) console.log("Content is key itself");
Jeff Thompson75771cb2013-01-20 23:27:38 -0800435
Jeff Thompsond771b122013-01-26 19:04:41 -0800436 var rsakey = decodeSubjectPublicKeyInfo(co.content);
437 var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
438 var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
439
440 currentClosure.upcall(flag, new UpcallInfo(this, null, 0, co));
441
442 // SWT: We don't need to store key here since the same key will be
443 // stored again in the closure.
444 //var keyEntry = new KeyStoreEntry(keylocator.keyName, rsakey, new Date().getTime());
445 //NDN.addKeyEntry(keyEntry);
446 //console.log(NDN.KeyStore);
447 } else {
448 // Check local key store
449 var keyEntry = NDN.getKeyByName(keylocator.keyName);
450 if (keyEntry) {
451 // Key found, verify now
452 if (LOG > 3) console.log("Local key cache hit");
453 var rsakey = keyEntry.rsaKey;
Jeff Thompson75771cb2013-01-20 23:27:38 -0800454 var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
455 var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
Jeff Thompson75771cb2013-01-20 23:27:38 -0800456
Jeff Thompsond771b122013-01-26 19:04:41 -0800457 // Raise callback
458 currentClosure.upcall(flag, new UpcallInfo(this, null, 0, co));
459 } else {
460 // Not found, fetch now
461 if (LOG > 3) console.log("Fetch key according to keylocator");
462 var nextClosure = new KeyFetchClosure(co, currentClosure, keylocator.keyName, sigHex, wit);
463 this.expressInterest(keylocator.keyName.contentName.getPrefix(4), nextClosure);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800464 }
Jeff Thompson75771cb2013-01-20 23:27:38 -0800465 }
Jeff Thompsond771b122013-01-26 19:04:41 -0800466 } else if (keylocator.type == KeyLocatorType.KEY) {
467 if (LOG > 3) console.log("Keylocator contains KEY");
468
469 var rsakey = decodeSubjectPublicKeyInfo(co.signedInfo.locator.publicKey);
470 var verified = rsakey.verifyByteArray(co.rawSignatureData, wit, sigHex);
471
472 var flag = (verified == true) ? Closure.UPCALL_CONTENT : Closure.UPCALL_CONTENT_BAD;
473 // Raise callback
474 currentClosure.upcall(Closure.UPCALL_CONTENT, new UpcallInfo(this, null, 0, co));
475
476 // Since KeyLocator does not contain key name for this key,
477 // we have no way to store it as a key entry in KeyStore.
478 } else {
479 var cert = keylocator.certificate;
480 console.log("KeyLocator contains CERT");
481 console.log(cert);
482
483 // TODO: verify certificate
Jeff Thompson75771cb2013-01-20 23:27:38 -0800484 }
485 }
486 }
487 } else
488 console.log('Incoming packet is not Interest or ContentObject. Discard now.');
Wentao Shangb42483a2013-01-03 15:32:32 -0800489};
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800490
491/*
492 * Assume this.getHostAndPort is not null. This is called when this.host is null or its host
Jeff Thompsona5668d52013-01-26 16:23:27 -0800493 * is not alive. Get a host and port, connect, then execute onConnected().
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800494 */
Jeff Thompsona5668d52013-01-26 16:23:27 -0800495NDN.prototype.connectAndExecute = function(onConnected) {
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800496 var hostAndPort = this.getHostAndPort();
497 if (hostAndPort == null) {
498 console.log('ERROR: No more hosts from getHostAndPort');
499 this.host = null;
500 return;
501 }
502
503 if (hostAndPort.host == this.host && hostAndPort.port == this.port) {
504 console.log('ERROR: The host returned by getHostAndPort is not alive: ' +
505 this.host + ":" + this.port);
506 return;
507 }
508
509 this.host = hostAndPort.host;
510 this.port = hostAndPort.port;
Jeff Thompsona5668d52013-01-26 16:23:27 -0800511 if (LOG>3) console.log("Connect: trying host from getHostAndPort: " + this.host);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800512
Jeff Thompson75771cb2013-01-20 23:27:38 -0800513 // Fetch any content.
514 var interest = new Interest(new Name("/"));
Jeff Thompson42806a12012-12-29 18:19:39 -0800515 interest.interestLifetime = 4000; // milliseconds
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800516
517 var thisNDN = this;
518 var timerID = setTimeout(function() {
Jeff Thompsona5668d52013-01-26 16:23:27 -0800519 if (LOG>3) console.log("Connect: timeout waiting for host " + thisNDN.host);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800520 // Try again.
Jeff Thompsona5668d52013-01-26 16:23:27 -0800521 thisNDN.connectAndExecute(onConnected);
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800522 }, 3000);
523
Jeff Thompsonf668acb2013-01-26 20:29:46 -0800524 this.reconnectAndExpressInterest
525 (interest, new NDN.ConnectClosure(this, onConnected, timerID));
Wentao Shangb42483a2013-01-03 15:32:32 -0800526};
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800527
Jeff Thompsona5668d52013-01-26 16:23:27 -0800528NDN.ConnectClosure = function ConnectClosure(ndn, onConnected, timerID) {
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800529 // Inherit from Closure.
530 Closure.call(this);
531
532 this.ndn = ndn;
Jeff Thompsona5668d52013-01-26 16:23:27 -0800533 this.onConnected = onConnected;
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800534 this.timerID = timerID;
535};
536
537NDN.ConnectClosure.prototype.upcall = function(kind, upcallInfo) {
538 if (!(kind == Closure.UPCALL_CONTENT ||
Jeff Thompson75771cb2013-01-20 23:27:38 -0800539 kind == Closure.UPCALL_CONTENT_UNVERIFIED))
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800540 // The upcall is not for us.
541 return Closure.RESULT_ERR;
542
Jeff Thompsona5668d52013-01-26 16:23:27 -0800543 // The host is alive, so cancel the timeout and continue with onConnected().
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800544 clearTimeout(this.timerID);
Jeff Thompsond771b122013-01-26 19:04:41 -0800545
546 // Call NDN.onopen after success
547 this.ndn.readyStatus = NDN.OPENED;
548 this.ndn.onopen();
549
Jeff Thompsona5668d52013-01-26 16:23:27 -0800550 this.onConnected();
Jeff Thompsond3a80dc2012-12-16 17:52:43 -0800551
552 return Closure.RESULT_OK;
553};
554
Jeff Thompson75771cb2013-01-20 23:27:38 -0800555/*
556 * A BinaryXmlElementReader lets you call onReceivedData multiple times which uses a
557 * BinaryXMLStructureDecoder to detect the end of a binary XML element and calls
558 * elementListener.onReceivedElement(element) with the element.
559 * This handles the case where a single call to onReceivedData may contain multiple elements.
560 */
561var BinaryXmlElementReader = function BinaryXmlElementReader(elementListener) {
562 this.elementListener = elementListener;
563 this.dataParts = [];
564 this.structureDecoder = new BinaryXMLStructureDecoder();
565};
566
567BinaryXmlElementReader.prototype.onReceivedData = function(/* Uint8Array */ rawData) {
Jeff Thompson75771cb2013-01-20 23:27:38 -0800568 // Process multiple objects in the data.
569 while(true) {
570 // Scan the input to check if a whole ccnb object has been read.
571 this.structureDecoder.seek(0);
572 if (this.structureDecoder.findElementEnd(rawData)) {
573 // Got the remainder of an object. Report to the caller.
574 this.dataParts.push(rawData.subarray(0, this.structureDecoder.offset));
Jeff Thompson0790af82013-01-26 19:54:27 -0800575 var element = DataUtils.concatArrays(this.dataParts);
576 this.dataParts = [];
577 try {
578 this.elementListener.onReceivedElement(element);
579 } catch (ex) {
580 console.log("BinaryXmlElementReader: ignoring exception from onReceivedElement: " + ex);
581 }
Jeff Thompson75771cb2013-01-20 23:27:38 -0800582
583 // Need to read a new object.
584 rawData = rawData.subarray(this.structureDecoder.offset, rawData.length);
Jeff Thompson75771cb2013-01-20 23:27:38 -0800585 this.structureDecoder = new BinaryXMLStructureDecoder();
586 if (rawData.length == 0)
587 // No more data in the packet.
588 return;
589
590 // else loop back to decode.
591 }
592 else {
593 // Save for a later call to concatArrays so that we only copy data once.
594 this.dataParts.push(rawData);
Jeff Thompsona46083c2013-01-20 23:55:21 -0800595 if (LOG>3) console.log('Incomplete packet received. Length ' + rawData.length + '. Wait for more input.');
Jeff Thompson75771cb2013-01-20 23:27:38 -0800596 return;
597 }
598 }
599}