1 /*
2  * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <ipxe/http.h>
30 #include <ipxe/iobuf.h>
31 #include <ipxe/xfer.h>
32 #include <ipxe/uri.h>
33 #include <ipxe/timer.h>
34 #include <ipxe/profile.h>
35 #include <ipxe/fault.h>
36 #include <ipxe/pccrr.h>
37 #include <ipxe/peerblk.h>
38 
39 /** @file
40  *
41  * Peer Content Caching and Retrieval (PeerDist) protocol block downloads
42  *
43  */
44 
45 /** PeerDist decryption chunksize
46  *
47  * This is a policy decision.
48  */
49 #define PEERBLK_DECRYPT_CHUNKSIZE 2048
50 
51 /** PeerDist maximum number of concurrent raw block downloads
52  *
53  * Raw block downloads are expensive if the origin server uses HTTPS,
54  * since each concurrent download will require local TLS resources
55  * (including potentially large received encrypted data buffers).
56  *
57  * Raw block downloads may also be prohibitively slow to initiate when
58  * the origin server is using HTTPS and client certificates.  Origin
59  * servers for PeerDist downloads are likely to be running IIS, which
60  * has a bug that breaks session resumption and requires each
61  * connection to go through the full client certificate verification.
62  *
63  * Limit the total number of concurrent raw block downloads to
64  * ameliorate these problems.
65  *
66  * This is a policy decision.
67  */
68 #define PEERBLK_RAW_MAX 2
69 
70 /** PeerDist raw block download attempt initial progress timeout
71  *
72  * This is a policy decision.
73  */
74 #define PEERBLK_RAW_OPEN_TIMEOUT ( 10 * TICKS_PER_SEC )
75 
76 /** PeerDist raw block download attempt ongoing progress timeout
77  *
78  * This is a policy decision.
79  */
80 #define PEERBLK_RAW_RX_TIMEOUT ( 15 * TICKS_PER_SEC )
81 
82 /** PeerDist retrieval protocol block download attempt initial progress timeout
83  *
84  * This is a policy decision.
85  */
86 #define PEERBLK_RETRIEVAL_OPEN_TIMEOUT ( 3 * TICKS_PER_SEC )
87 
88 /** PeerDist retrieval protocol block download attempt ongoing progress timeout
89  *
90  * This is a policy decision.
91  */
92 #define PEERBLK_RETRIEVAL_RX_TIMEOUT ( 5 * TICKS_PER_SEC )
93 
94 /** PeerDist maximum number of full download attempt cycles
95  *
96  * This is the maximum number of times that we will try a full cycle
97  * of download attempts (i.e. a retrieval protocol download attempt
98  * from each discovered peer plus a raw download attempt from the
99  * origin server).
100  *
101  * This is a policy decision.
102  */
103 #define PEERBLK_MAX_ATTEMPT_CYCLES 4
104 
105 /** PeerDist block download profiler */
106 static struct profiler peerblk_download_profiler __profiler =
107 	{ .name = "peerblk.download" };
108 
109 /** PeerDist block download attempt success profiler */
110 static struct profiler peerblk_attempt_success_profiler __profiler =
111 	{ .name = "peerblk.attempt.success" };
112 
113 /** PeerDist block download attempt failure profiler */
114 static struct profiler peerblk_attempt_failure_profiler __profiler =
115 	{ .name = "peerblk.attempt.failure" };
116 
117 /** PeerDist block download attempt timeout profiler */
118 static struct profiler peerblk_attempt_timeout_profiler __profiler =
119 	{ .name = "peerblk.attempt.timeout" };
120 
121 /** PeerDist block download discovery success profiler */
122 static struct profiler peerblk_discovery_success_profiler __profiler =
123 	{ .name = "peerblk.discovery.success" };
124 
125 /** PeerDist block download discovery timeout profiler */
126 static struct profiler peerblk_discovery_timeout_profiler __profiler =
127 	{ .name = "peerblk.discovery.timeout" };
128 
129 static void peerblk_dequeue ( struct peerdist_block *peerblk );
130 
131 /**
132  * Get profiling timestamp
133  *
134  * @ret timestamp	Timestamp
135  */
136 static inline __attribute__ (( always_inline )) unsigned long
peerblk_timestamp(void)137 peerblk_timestamp ( void ) {
138 
139 	if ( PROFILING ) {
140 		return currticks();
141 	} else {
142 		return 0;
143 	}
144 }
145 
146 /**
147  * Free PeerDist block download
148  *
149  * @v refcnt		Reference count
150  */
peerblk_free(struct refcnt * refcnt)151 static void peerblk_free ( struct refcnt *refcnt ) {
152 	struct peerdist_block *peerblk =
153 		container_of ( refcnt, struct peerdist_block, refcnt );
154 
155 	uri_put ( peerblk->uri );
156 	free ( peerblk->cipherctx );
157 	free ( peerblk );
158 }
159 
160 /**
161  * Reset PeerDist block download attempt
162  *
163  * @v peerblk		PeerDist block download
164  * @v rc		Reason for reset
165  */
peerblk_reset(struct peerdist_block * peerblk,int rc)166 static void peerblk_reset ( struct peerdist_block *peerblk, int rc ) {
167 
168 	/* Stop decryption process */
169 	process_del ( &peerblk->process );
170 
171 	/* Stop timer */
172 	stop_timer ( &peerblk->timer );
173 
174 	/* Abort any current download attempt */
175 	intf_restart ( &peerblk->raw, rc );
176 	intf_restart ( &peerblk->retrieval, rc );
177 
178 	/* Remove from download queue, if applicable */
179 	if ( peerblk->queue )
180 		peerblk_dequeue ( peerblk );
181 
182 	/* Empty received data buffer */
183 	xferbuf_free ( &peerblk->buffer );
184 	peerblk->pos = 0;
185 
186 	/* Reset digest and free cipher context */
187 	digest_init ( peerblk->digest, peerblk->digestctx );
188 	free ( peerblk->cipherctx );
189 	peerblk->cipherctx = NULL;
190 	peerblk->cipher = NULL;
191 
192 	/* Reset trim thresholds */
193 	peerblk->start = ( peerblk->trim.start - peerblk->range.start );
194 	peerblk->end = ( peerblk->trim.end - peerblk->range.start );
195 	assert ( peerblk->start <= peerblk->end );
196 }
197 
198 /**
199  * Close PeerDist block download
200  *
201  * @v peerblk		PeerDist block download
202  * @v rc		Reason for close
203  */
peerblk_close(struct peerdist_block * peerblk,int rc)204 static void peerblk_close ( struct peerdist_block *peerblk, int rc ) {
205 	unsigned long now = peerblk_timestamp();
206 
207 	/* Profile overall block download */
208 	profile_custom ( &peerblk_download_profiler,
209 			 ( now - peerblk->started ) );
210 
211 	/* Reset download attempt */
212 	peerblk_reset ( peerblk, rc );
213 
214 	/* Close discovery */
215 	peerdisc_close ( &peerblk->discovery );
216 
217 	/* Shut down all interfaces */
218 	intf_shutdown ( &peerblk->retrieval, rc );
219 	intf_shutdown ( &peerblk->raw, rc );
220 	intf_shutdown ( &peerblk->xfer, rc );
221 }
222 
223 /**
224  * Calculate offset within overall download
225  *
226  * @v peerblk		PeerDist block download
227  * @v pos		Position within incoming data stream
228  * @ret offset		Offset within overall download
229  */
230 static inline __attribute__ (( always_inline )) size_t
peerblk_offset(struct peerdist_block * peerblk,size_t pos)231 peerblk_offset ( struct peerdist_block *peerblk, size_t pos ) {
232 
233 	return ( ( pos - peerblk->start ) + peerblk->offset );
234 }
235 
236 /**
237  * Deliver download attempt data block
238  *
239  * @v peerblk		PeerDist block download
240  * @v iobuf		I/O buffer
241  * @v meta		Original data transfer metadata
242  * @v pos		Position within incoming data stream
243  * @ret rc		Return status code
244  */
peerblk_deliver(struct peerdist_block * peerblk,struct io_buffer * iobuf,struct xfer_metadata * meta,size_t pos)245 static int peerblk_deliver ( struct peerdist_block *peerblk,
246 			     struct io_buffer *iobuf,
247 			     struct xfer_metadata *meta, size_t pos ) {
248 	struct xfer_metadata xfer_meta;
249 	size_t len = iob_len ( iobuf );
250 	size_t start = pos;
251 	size_t end = ( pos + len );
252 	int rc;
253 
254 	/* Discard zero-length packets and packets which lie entirely
255 	 * outside the trimmed range.
256 	 */
257 	if ( ( start >= peerblk->end ) || ( end <= peerblk->start ) ||
258 	     ( len == 0 ) ) {
259 		free_iob ( iobuf );
260 		return 0;
261 	}
262 
263 	/* Truncate data to within trimmed range */
264 	if ( start < peerblk->start ) {
265 		iob_pull ( iobuf, ( peerblk->start - start ) );
266 		start = peerblk->start;
267 	}
268 	if ( end > peerblk->end ) {
269 		iob_unput ( iobuf, ( end - peerblk->end ) );
270 		end = peerblk->end;
271 	}
272 
273 	/* Construct metadata */
274 	memcpy ( &xfer_meta, meta, sizeof ( xfer_meta ) );
275 	xfer_meta.flags |= XFER_FL_ABS_OFFSET;
276 	xfer_meta.offset = peerblk_offset ( peerblk, start );
277 
278 	/* Deliver data */
279 	if ( ( rc = xfer_deliver ( &peerblk->xfer, iob_disown ( iobuf ),
280 				   &xfer_meta ) ) != 0 ) {
281 		DBGC ( peerblk, "PEERBLK %p %d.%d could not deliver data: %s\n",
282 		       peerblk, peerblk->segment, peerblk->block,
283 		       strerror ( rc ) );
284 		return rc;
285 	}
286 
287 	return 0;
288 }
289 
290 /**
291  * Finish PeerDist block download attempt
292  *
293  * @v peerblk		PeerDist block download
294  * @v rc		Reason for close
295  */
peerblk_done(struct peerdist_block * peerblk,int rc)296 static void peerblk_done ( struct peerdist_block *peerblk, int rc ) {
297 	struct digest_algorithm *digest = peerblk->digest;
298 	struct peerdisc_segment *segment = peerblk->discovery.segment;
299 	struct peerdisc_peer *head;
300 	struct peerdisc_peer *peer;
301 	uint8_t hash[digest->digestsize];
302 	unsigned long now = peerblk_timestamp();
303 
304 	/* Check for errors on completion */
305 	if ( rc != 0 ) {
306 		DBGC ( peerblk, "PEERBLK %p %d.%d attempt failed: %s\n",
307 		       peerblk, peerblk->segment, peerblk->block,
308 		       strerror ( rc ) );
309 		goto err;
310 	}
311 
312 	/* Check digest */
313 	digest_final ( digest, peerblk->digestctx, hash );
314 	if ( memcmp ( hash, peerblk->hash, peerblk->digestsize ) != 0 ) {
315 		DBGC ( peerblk, "PEERBLK %p %d.%d digest mismatch:\n",
316 		       peerblk, peerblk->segment, peerblk->block );
317 		DBGC_HDA ( peerblk, 0, hash, peerblk->digestsize );
318 		DBGC_HDA ( peerblk, 0, peerblk->hash, peerblk->digestsize );
319 		rc = -EIO;
320 		goto err;
321 	}
322 
323 	/* Profile successful attempt */
324 	profile_custom ( &peerblk_attempt_success_profiler,
325 			 ( now - peerblk->attempted ) );
326 
327 	/* Report peer statistics */
328 	head = list_entry ( &segment->peers, struct peerdisc_peer, list );
329 	peer = ( ( peerblk->peer == head ) ? NULL : peerblk->peer );
330 	peerdisc_stat ( &peerblk->xfer, peer, &segment->peers );
331 
332 	/* Close download */
333 	peerblk_close ( peerblk, 0 );
334 	return;
335 
336  err:
337 	/* Record failure reason and schedule a retry attempt */
338 	profile_custom ( &peerblk_attempt_failure_profiler,
339 			 ( now - peerblk->attempted ) );
340 	peerblk_reset ( peerblk, rc );
341 	peerblk->rc = rc;
342 	start_timer_nodelay ( &peerblk->timer );
343 }
344 
345 /******************************************************************************
346  *
347  * Raw block download attempts (using an HTTP range request)
348  *
349  ******************************************************************************
350  */
351 
352 /**
353  * Open PeerDist raw block download attempt
354  *
355  * @v peerblk		PeerDist block download
356  * @ret rc		Return status code
357  */
peerblk_raw_open(struct peerdist_block * peerblk)358 static int peerblk_raw_open ( struct peerdist_block *peerblk ) {
359 	struct http_request_range range;
360 	int rc;
361 
362 	DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting raw range request\n",
363 		peerblk, peerblk->segment, peerblk->block );
364 
365 	/* Construct HTTP range */
366 	memset ( &range, 0, sizeof ( range ) );
367 	range.start = peerblk->range.start;
368 	range.len = ( peerblk->range.end - peerblk->range.start );
369 
370 	/* Initiate range request to retrieve block */
371 	if ( ( rc = http_open ( &peerblk->raw, &http_get, peerblk->uri,
372 				&range, NULL ) ) != 0 ) {
373 		DBGC ( peerblk, "PEERBLK %p %d.%d could not create range "
374 		       "request: %s\n", peerblk, peerblk->segment,
375 		       peerblk->block, strerror ( rc ) );
376 		return rc;
377 	}
378 
379 	/* Annul HTTP connection (for testing) if applicable.  Do not
380 	 * report as an immediate error, in order to test our ability
381 	 * to recover from a totally unresponsive HTTP server.
382 	 */
383 	if ( inject_fault ( PEERBLK_ANNUL_RATE ) )
384 		intf_restart ( &peerblk->raw, 0 );
385 
386 	/* Start download attempt timer */
387 	peerblk->rc = -ETIMEDOUT;
388 	start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_OPEN_TIMEOUT );
389 
390 	return 0;
391 }
392 
393 /**
394  * Receive PeerDist raw data
395  *
396  * @v peerblk		PeerDist block download
397  * @v iobuf		I/O buffer
398  * @v meta		Data transfer metadata
399  * @ret rc		Return status code
400  */
peerblk_raw_rx(struct peerdist_block * peerblk,struct io_buffer * iobuf,struct xfer_metadata * meta)401 static int peerblk_raw_rx ( struct peerdist_block *peerblk,
402 			    struct io_buffer *iobuf,
403 			    struct xfer_metadata *meta ) {
404 	size_t len = iob_len ( iobuf );
405 	size_t pos = peerblk->pos;
406 	size_t mid = ( ( peerblk->range.end - peerblk->range.start ) / 2 );
407 	int rc;
408 
409 	/* Corrupt received data (for testing) if applicable */
410 	inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len );
411 
412 	/* Fail if data is delivered out of order, since the streaming
413 	 * digest requires strict ordering.
414 	 */
415 	if ( ( rc = xfer_check_order ( meta, &peerblk->pos, len ) ) != 0 )
416 		goto err;
417 
418 	/* Add data to digest */
419 	digest_update ( peerblk->digest, peerblk->digestctx, iobuf->data, len );
420 
421 	/* Deliver data */
422 	if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta,
423 				      pos ) ) != 0 )
424 		goto err;
425 
426 	/* Extend download attempt timer */
427 	start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_RX_TIMEOUT );
428 
429 	/* Stall download attempt (for testing) if applicable */
430 	if ( ( pos < mid ) && ( ( pos + len ) >= mid ) &&
431 	     ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) {
432 		intf_restart ( &peerblk->raw, rc );
433 	}
434 
435 	return 0;
436 
437  err:
438 	free_iob ( iobuf );
439 	peerblk_done ( peerblk, rc );
440 	return rc;
441 }
442 
443 /**
444  * Close PeerDist raw block download attempt
445  *
446  * @v peerblk		PeerDist block download
447  * @v rc		Reason for close
448  */
peerblk_raw_close(struct peerdist_block * peerblk,int rc)449 static void peerblk_raw_close ( struct peerdist_block *peerblk, int rc ) {
450 
451 	/* Restart interface */
452 	intf_restart ( &peerblk->raw, rc );
453 
454 	/* Fail immediately if we have an error */
455 	if ( rc != 0 )
456 		goto done;
457 
458 	/* Abort download attempt (for testing) if applicable */
459 	if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 )
460 		goto done;
461 
462  done:
463 	/* Complete download attempt */
464 	peerblk_done ( peerblk, rc );
465 }
466 
467 /******************************************************************************
468  *
469  * Block download queue
470  *
471  ******************************************************************************
472  */
473 
474 /**
475  * PeerDist block download queue process
476  *
477  * @v queue		Block download queue
478  */
peerblk_step(struct peerdist_block_queue * queue)479 static void peerblk_step ( struct peerdist_block_queue *queue ) {
480 	struct peerdist_block *peerblk;
481 	int rc;
482 
483 	/* Do nothing yet if we have too many open block downloads */
484 	if ( queue->count >= queue->max )
485 		return;
486 
487 	/* Do nothing unless there are queued block downloads */
488 	peerblk = list_first_entry ( &queue->list, struct peerdist_block,
489 				     queued );
490 	if ( ! peerblk )
491 		return;
492 
493 	/* Reschedule queue process */
494 	process_add ( &queue->process );
495 
496 	/* Remove block from queue */
497 	list_del ( &peerblk->queued );
498 	INIT_LIST_HEAD ( &peerblk->queued );
499 
500 	/* Attempt download */
501 	if ( ( rc = queue->open ( peerblk ) ) != 0 ) {
502 		peerblk_close ( peerblk, rc );
503 		return;
504 	}
505 
506 	/* Increment open block download count */
507 	queue->count++;
508 }
509 
510 /**
511  * Add block to download queue
512  *
513  * @v peerblk		PeerDist block download
514  * @v queue		Block download queue
515  */
peerblk_enqueue(struct peerdist_block * peerblk,struct peerdist_block_queue * queue)516 static void peerblk_enqueue ( struct peerdist_block *peerblk,
517 			      struct peerdist_block_queue *queue ) {
518 
519 	/* Sanity checks */
520 	assert ( peerblk->queue == NULL );
521 	assert ( list_empty ( &peerblk->queued ) );
522 
523 	/* Add block to queue */
524 	peerblk->queue = queue;
525 	list_add_tail ( &peerblk->queued, &queue->list );
526 
527 	/* Schedule queue process */
528 	process_add ( &queue->process );
529 }
530 
531 /**
532  * Remove block from download queue
533  *
534  * @v peerblk		PeerDist block download
535  */
peerblk_dequeue(struct peerdist_block * peerblk)536 static void peerblk_dequeue ( struct peerdist_block *peerblk ) {
537 	struct peerdist_block_queue *queue = peerblk->queue;
538 
539 	/* Sanity checks */
540 	assert ( queue != NULL );
541 
542 	/* Remove block from queue */
543 	peerblk->queue = NULL;
544 	if ( list_empty ( &peerblk->queued ) ) {
545 
546 		/* Open download: decrement count and reschedule queue */
547 		queue->count--;
548 		process_add ( &queue->process );
549 
550 	} else {
551 
552 		/* Queued download: remove from queue */
553 		list_del ( &peerblk->queued );
554 		INIT_LIST_HEAD ( &peerblk->queued );
555 	}
556 }
557 
558 /** PeerDist block download queue process descriptor */
559 static struct process_descriptor peerblk_queue_desc =
560 	PROC_DESC_ONCE ( struct peerdist_block_queue, process, peerblk_step );
561 
562 /** Raw block download queue */
563 static struct peerdist_block_queue peerblk_raw_queue = {
564 	.process = PROC_INIT ( peerblk_raw_queue.process, &peerblk_queue_desc ),
565 	.list = LIST_HEAD_INIT ( peerblk_raw_queue.list ),
566 	.max = PEERBLK_RAW_MAX,
567 	.open = peerblk_raw_open,
568 };
569 
570 /******************************************************************************
571  *
572  * Retrieval protocol block download attempts (using HTTP POST)
573  *
574  ******************************************************************************
575  */
576 
577 /**
578  * Construct PeerDist retrieval protocol URI
579  *
580  * @v location		Peer location
581  * @ret uri		Retrieval URI, or NULL on error
582  */
peerblk_retrieval_uri(const char * location)583 static struct uri * peerblk_retrieval_uri ( const char *location ) {
584 	char uri_string[ 7 /* "http://" */ + strlen ( location ) +
585 			 sizeof ( PEERDIST_MAGIC_PATH /* includes NUL */ ) ];
586 
587 	/* Construct URI string */
588 	snprintf ( uri_string, sizeof ( uri_string ),
589 		   ( "http://%s" PEERDIST_MAGIC_PATH ), location );
590 
591 	/* Parse URI string */
592 	return parse_uri ( uri_string );
593 }
594 
595 /**
596  * Open PeerDist retrieval protocol block download attempt
597  *
598  * @v peerblk		PeerDist block download
599  * @v location		Peer location
600  * @ret rc		Return status code
601  */
peerblk_retrieval_open(struct peerdist_block * peerblk,const char * location)602 static int peerblk_retrieval_open ( struct peerdist_block *peerblk,
603 				    const char *location ) {
604 	size_t digestsize = peerblk->digestsize;
605 	peerdist_msg_getblks_t ( digestsize, 1, 0 ) req;
606 	peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *rsp;
607 	struct http_request_content content;
608 	struct uri *uri;
609 	int rc;
610 
611 	DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting retrieval from %s\n",
612 		peerblk, peerblk->segment, peerblk->block, location );
613 
614 	/* Construct block fetch request */
615 	memset ( &req, 0, sizeof ( req ) );
616 	req.getblks.hdr.version.raw = htonl ( PEERDIST_MSG_GETBLKS_VERSION );
617 	req.getblks.hdr.type = htonl ( PEERDIST_MSG_GETBLKS_TYPE );
618 	req.getblks.hdr.len = htonl ( sizeof ( req ) );
619 	req.getblks.hdr.algorithm = htonl ( PEERDIST_MSG_AES_128_CBC );
620 	req.segment.segment.digestsize = htonl ( digestsize );
621 	memcpy ( req.segment.id, peerblk->id, digestsize );
622 	req.ranges.ranges.count = htonl ( 1 );
623 	req.ranges.range[0].first = htonl ( peerblk->block );
624 	req.ranges.range[0].count = htonl ( 1 );
625 
626 	/* Construct POST request content */
627 	memset ( &content, 0, sizeof ( content ) );
628 	content.data = &req;
629 	content.len = sizeof ( req );
630 
631 	/* Construct URI */
632 	if ( ( uri = peerblk_retrieval_uri ( location ) ) == NULL ) {
633 		rc = -ENOMEM;
634 		goto err_uri;
635 	}
636 
637 	/* Update trim thresholds */
638 	peerblk->start += offsetof ( typeof ( *rsp ), msg.vrf );
639 	peerblk->end += offsetof ( typeof ( *rsp ), msg.vrf );
640 
641 	/* Initiate HTTP POST to retrieve block */
642 	if ( ( rc = http_open ( &peerblk->retrieval, &http_post, uri,
643 				NULL, &content ) ) != 0 ) {
644 		DBGC ( peerblk, "PEERBLK %p %d.%d could not create retrieval "
645 		       "request: %s\n", peerblk, peerblk->segment,
646 		       peerblk->block, strerror ( rc ) );
647 		goto err_open;
648 	}
649 
650 	/* Annul HTTP connection (for testing) if applicable.  Do not
651 	 * report as an immediate error, in order to test our ability
652 	 * to recover from a totally unresponsive HTTP server.
653 	 */
654 	if ( inject_fault ( PEERBLK_ANNUL_RATE ) )
655 		intf_restart ( &peerblk->retrieval, 0 );
656 
657 	/* Start download attempt timer */
658 	peerblk->rc = -ETIMEDOUT;
659 	start_timer_fixed ( &peerblk->timer, PEERBLK_RETRIEVAL_OPEN_TIMEOUT );
660 
661  err_open:
662 	uri_put ( uri );
663  err_uri:
664 	return rc;
665 }
666 
667 /**
668  * Receive PeerDist retrieval protocol data
669  *
670  * @v peerblk		PeerDist block download
671  * @v iobuf		I/O buffer
672  * @v meta		Data transfer metadata
673  * @ret rc		Return status code
674  */
peerblk_retrieval_rx(struct peerdist_block * peerblk,struct io_buffer * iobuf,struct xfer_metadata * meta)675 static int peerblk_retrieval_rx ( struct peerdist_block *peerblk,
676 				  struct io_buffer *iobuf,
677 				  struct xfer_metadata *meta ) {
678 	size_t len = iob_len ( iobuf );
679 	size_t start;
680 	size_t end;
681 	size_t before;
682 	size_t after;
683 	size_t cut;
684 	int rc;
685 
686 	/* Some genius at Microsoft thought it would be a great idea
687 	 * to place the AES-CBC initialisation vector *after* the
688 	 * encrypted data, thereby making it logically impossible to
689 	 * decrypt each packet as it arrives.
690 	 *
691 	 * To work around this mindless stupidity, we deliver the
692 	 * ciphertext as-is and later use xfer_buffer() to obtain
693 	 * access to the underlying data transfer buffer in order to
694 	 * perform the decryption.
695 	 *
696 	 * There will be some data both before and after the bytes
697 	 * corresponding to the trimmed plaintext: a MSG_BLK
698 	 * header/footer, some block padding for the AES-CBC cipher,
699 	 * and a possibly large quantity of unwanted ciphertext which
700 	 * is excluded from the trimmed content range.  We store this
701 	 * data in a local data transfer buffer.  If the amount of
702 	 * data to be stored is too large, we will fail allocation and
703 	 * so eventually fall back to using a range request (which
704 	 * does not require this kind of temporary storage
705 	 * allocation).
706 	 */
707 
708 	/* Corrupt received data (for testing) if applicable */
709 	inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len );
710 
711 	/* Calculate start and end positions of this buffer */
712 	start = peerblk->pos;
713 	if ( meta->flags & XFER_FL_ABS_OFFSET )
714 		start = 0;
715 	start += meta->offset;
716 	end = ( start + len );
717 
718 	/* Buffer any data before the trimmed content */
719 	if ( ( start < peerblk->start ) && ( len > 0 ) ) {
720 
721 		/* Calculate length of data before the trimmed content */
722 		before = ( peerblk->start - start );
723 		if ( before > len )
724 			before = len;
725 
726 		/* Buffer data before the trimmed content */
727 		if ( ( rc = xferbuf_write ( &peerblk->buffer, start,
728 					    iobuf->data, before ) ) != 0 ) {
729 			DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer "
730 			       "data: %s\n", peerblk, peerblk->segment,
731 			       peerblk->block, strerror ( rc ) );
732 			goto err;
733 		}
734 	}
735 
736 	/* Buffer any data after the trimmed content */
737 	if ( ( end > peerblk->end ) && ( len > 0 ) ) {
738 
739 		/* Calculate length of data after the trimmed content */
740 		after = ( end - peerblk->end );
741 		if ( after > len )
742 			after = len;
743 
744 		/* Buffer data after the trimmed content */
745 		cut = ( peerblk->end - peerblk->start );
746 		if ( ( rc = xferbuf_write ( &peerblk->buffer,
747 					    ( end - after - cut ),
748 					    ( iobuf->data + len - after ),
749 					    after ) ) != 0 ) {
750 			DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer "
751 			       "data: %s\n", peerblk, peerblk->segment,
752 			       peerblk->block, strerror ( rc ) );
753 			goto err;
754 		}
755 	}
756 
757 	/* Deliver any remaining data */
758 	if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta,
759 				      start ) ) != 0 )
760 		goto err;
761 
762 	/* Update position */
763 	peerblk->pos = end;
764 
765 	/* Extend download attempt timer */
766 	start_timer_fixed ( &peerblk->timer, PEERBLK_RETRIEVAL_RX_TIMEOUT );
767 
768 	/* Stall download attempt (for testing) if applicable */
769 	if ( ( start < peerblk->end ) && ( end >= peerblk->end ) &&
770 	     ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) {
771 		intf_restart ( &peerblk->retrieval, rc );
772 	}
773 
774 	return 0;
775 
776  err:
777 	free_iob ( iobuf );
778 	peerblk_done ( peerblk, rc );
779 	return rc;
780 }
781 
782 /**
783  * Parse retrieval protocol message header
784  *
785  * @v peerblk		PeerDist block download
786  * @ret rc		Return status code
787  */
peerblk_parse_header(struct peerdist_block * peerblk)788 static int peerblk_parse_header ( struct peerdist_block *peerblk ) {
789 	struct {
790 		struct peerdist_msg_transport_header hdr;
791 		struct peerdist_msg_header msg;
792 	} __attribute__ (( packed )) *msg = peerblk->buffer.data;
793 	struct cipher_algorithm *cipher;
794 	size_t len = peerblk->buffer.len;
795 	size_t keylen = 0;
796 	int rc;
797 
798 	/* Check message length */
799 	if ( len < sizeof ( *msg ) ) {
800 		DBGC ( peerblk, "PEERBLK %p %d.%d message too short for header "
801 		       "(%zd bytes)\n", peerblk, peerblk->segment,
802 		       peerblk->block, len );
803 		return -ERANGE;
804 	}
805 
806 	/* Check message type */
807 	if ( msg->msg.type != htonl ( PEERDIST_MSG_BLK_TYPE ) ) {
808 		DBGC ( peerblk, "PEERBLK %p %d.%d unexpected message type "
809 		       "%#08x\n", peerblk, peerblk->segment, peerblk->block,
810 		       ntohl ( msg->msg.type ) );
811 		return -EPROTO;
812 	}
813 
814 	/* Determine cipher algorithm and key length */
815 	cipher = &aes_cbc_algorithm;
816 	switch ( msg->msg.algorithm ) {
817 	case htonl ( PEERDIST_MSG_PLAINTEXT ) :
818 		cipher = NULL;
819 		break;
820 	case htonl ( PEERDIST_MSG_AES_128_CBC ) :
821 		keylen = ( 128 / 8 );
822 		break;
823 	case htonl ( PEERDIST_MSG_AES_192_CBC ) :
824 		keylen = ( 192 / 8 );
825 		break;
826 	case htonl ( PEERDIST_MSG_AES_256_CBC ) :
827 		keylen = ( 256 / 8 );
828 		break;
829 	default:
830 		DBGC ( peerblk, "PEERBLK %p %d.%d unrecognised algorithm "
831 		       "%#08x\n", peerblk, peerblk->segment, peerblk->block,
832 		       ntohl ( msg->msg.algorithm ) );
833 		return -ENOTSUP;
834 	}
835 	DBGC2 ( peerblk, "PEERBLK %p %d.%d using %s with %zd-bit key\n",
836 		peerblk, peerblk->segment, peerblk->block,
837 		( cipher ? cipher->name : "plaintext" ), ( 8 * keylen ) );
838 
839 	/* Sanity check key length against maximum secret length */
840 	if ( keylen > peerblk->digestsize ) {
841 		DBGC ( peerblk, "PEERBLK %p %d.%d %zd-byte secret too short "
842 		       "for %zd-bit key\n", peerblk, peerblk->segment,
843 		       peerblk->block, peerblk->digestsize, ( 8 * keylen ) );
844 		return -EPROTO;
845 	}
846 
847 	/* Allocate cipher context, if applicable.  Freeing the cipher
848 	 * context (on error or otherwise) is handled by peerblk_reset().
849 	 */
850 	peerblk->cipher = cipher;
851 	assert ( peerblk->cipherctx == NULL );
852 	if ( cipher ) {
853 		peerblk->cipherctx = malloc ( cipher->ctxsize );
854 		if ( ! peerblk->cipherctx )
855 			return -ENOMEM;
856 	}
857 
858 	/* Initialise cipher, if applicable */
859 	if ( cipher &&
860 	     ( rc = cipher_setkey ( cipher, peerblk->cipherctx, peerblk->secret,
861 				    keylen ) ) != 0 ) {
862 		DBGC ( peerblk, "PEERBLK %p %d.%d could not set key: %s\n",
863 		       peerblk, peerblk->segment, peerblk->block,
864 		       strerror ( rc ) );
865 		return rc;
866 	}
867 
868 	return 0;
869 }
870 
871 /**
872  * Parse retrieval protocol message segment and block details
873  *
874  * @v peerblk		PeerDist block download
875  * @v buf_len		Length of buffered data to fill in
876  * @ret rc		Return status code
877  */
peerblk_parse_block(struct peerdist_block * peerblk,size_t * buf_len)878 static int peerblk_parse_block ( struct peerdist_block *peerblk,
879 				 size_t *buf_len ) {
880 	size_t digestsize = peerblk->digestsize;
881 	peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *msg = peerblk->buffer.data;
882 	size_t len = peerblk->buffer.len;
883 	size_t data_len;
884 	size_t total;
885 
886 	/* Check message length */
887 	if ( len < offsetof ( typeof ( *msg ), msg.block.data ) ) {
888 		DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
889 		       "zero-length data (%zd bytes)\n", peerblk,
890 		       peerblk->segment, peerblk->block, len );
891 		return -ERANGE;
892 	}
893 
894 	/* Check digest size */
895 	if ( ntohl ( msg->msg.segment.segment.digestsize ) != digestsize ) {
896 		DBGC ( peerblk, "PEERBLK %p %d.%d incorrect digest size %d\n",
897 		       peerblk, peerblk->segment, peerblk->block,
898 		       ntohl ( msg->msg.segment.segment.digestsize ) );
899 		return -EPROTO;
900 	}
901 
902 	/* Check segment ID */
903 	if ( memcmp ( msg->msg.segment.id, peerblk->id, digestsize ) != 0 ) {
904 		DBGC ( peerblk, "PEERBLK %p %d.%d segment ID mismatch\n",
905 		       peerblk, peerblk->segment, peerblk->block );
906 		return -EPROTO;
907 	}
908 
909 	/* Check block ID */
910 	if ( ntohl ( msg->msg.index ) != peerblk->block ) {
911 		DBGC ( peerblk, "PEERBLK %p %d.%d block ID mismatch (got %d)\n",
912 		       peerblk, peerblk->segment, peerblk->block,
913 		       ntohl ( msg->msg.index ) );
914 		return -EPROTO;
915 	}
916 
917 	/* Check for missing blocks */
918 	data_len = be32_to_cpu ( msg->msg.block.block.len );
919 	if ( ! data_len ) {
920 		DBGC ( peerblk, "PEERBLK %p %d.%d block not found\n",
921 		       peerblk, peerblk->segment, peerblk->block );
922 		return -ENOENT;
923 	}
924 
925 	/* Check for underlength blocks */
926 	if ( data_len < ( peerblk->range.end - peerblk->range.start ) ) {
927 		DBGC ( peerblk, "PEERBLK %p %d.%d underlength block (%zd "
928 		       "bytes)\n", peerblk, peerblk->segment, peerblk->block,
929 		       data_len );
930 		return -ERANGE;
931 	}
932 
933 	/* Calculate buffered data length (i.e. excluding data which
934 	 * was delivered to the final data transfer buffer).
935 	 */
936 	*buf_len = ( data_len - ( peerblk->end - peerblk->start ) );
937 
938 	/* Describe data before the trimmed content */
939 	peerblk->decrypt[PEERBLK_BEFORE].xferbuf = &peerblk->buffer;
940 	peerblk->decrypt[PEERBLK_BEFORE].offset =
941 		offsetof ( typeof ( *msg ), msg.block.data );
942 	peerblk->decrypt[PEERBLK_BEFORE].len =
943 		( peerblk->start -
944 		  offsetof ( typeof ( *msg ), msg.block.data ) );
945 	total = peerblk->decrypt[PEERBLK_BEFORE].len;
946 
947 	/* Describe data within the trimmed content */
948 	peerblk->decrypt[PEERBLK_DURING].offset =
949 		peerblk_offset ( peerblk, peerblk->start );
950 	peerblk->decrypt[PEERBLK_DURING].len =
951 		( peerblk->end - peerblk->start );
952 	total += peerblk->decrypt[PEERBLK_DURING].len;
953 
954 	/* Describe data after the trimmed content */
955 	peerblk->decrypt[PEERBLK_AFTER].xferbuf = &peerblk->buffer;
956 	peerblk->decrypt[PEERBLK_AFTER].offset = peerblk->start;
957 	peerblk->decrypt[PEERBLK_AFTER].len =
958 		( offsetof ( typeof ( *msg ), msg.block.data )
959 		  + *buf_len - peerblk->start );
960 	total += peerblk->decrypt[PEERBLK_AFTER].len;
961 
962 	/* Sanity check */
963 	assert ( total == be32_to_cpu ( msg->msg.block.block.len ) );
964 
965 	/* Initialise cipher and digest lengths */
966 	peerblk->cipher_remaining = total;
967 	peerblk->digest_remaining =
968 		( peerblk->range.end - peerblk->range.start );
969 	assert ( peerblk->cipher_remaining >= peerblk->digest_remaining );
970 
971 	return 0;
972 }
973 
974 /**
975  * Parse retrieval protocol message useless details
976  *
977  * @v peerblk		PeerDist block download
978  * @v buf_len		Length of buffered data
979  * @v vrf_len		Length of uselessness to fill in
980  * @ret rc		Return status code
981  */
peerblk_parse_useless(struct peerdist_block * peerblk,size_t buf_len,size_t * vrf_len)982 static int peerblk_parse_useless ( struct peerdist_block *peerblk,
983 				   size_t buf_len, size_t *vrf_len ) {
984 	size_t digestsize = peerblk->digestsize;
985 	peerblk_msg_blk_t ( digestsize, buf_len, 0, 0 ) *msg =
986 		peerblk->buffer.data;
987 	size_t len = peerblk->buffer.len;
988 
989 	/* Check message length */
990 	if ( len < offsetof ( typeof ( *msg ), msg.vrf.data ) ) {
991 		DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
992 		       "zero-length uselessness (%zd bytes)\n", peerblk,
993 		       peerblk->segment, peerblk->block, len );
994 		return -ERANGE;
995 	}
996 
997 	/* Extract length of uselessness */
998 	*vrf_len = be32_to_cpu ( msg->msg.vrf.vrf.len );
999 
1000 	return 0;
1001 }
1002 
1003 /**
1004  * Parse retrieval protocol message initialisation vector details
1005  *
1006  * @v peerblk		PeerDist block download
1007  * @v buf_len		Length of buffered data
1008  * @v vrf_len		Length of uselessness
1009  * @ret rc		Return status code
1010  */
peerblk_parse_iv(struct peerdist_block * peerblk,size_t buf_len,size_t vrf_len)1011 static int peerblk_parse_iv ( struct peerdist_block *peerblk, size_t buf_len,
1012 			      size_t vrf_len ) {
1013 	size_t digestsize = peerblk->digestsize;
1014 	size_t blksize = peerblk->cipher->blocksize;
1015 	peerblk_msg_blk_t ( digestsize, buf_len, vrf_len, blksize ) *msg =
1016 		peerblk->buffer.data;
1017 	size_t len = peerblk->buffer.len;
1018 
1019 	/* Check message length */
1020 	if ( len < sizeof ( *msg ) ) {
1021 		DBGC ( peerblk, "PEERBLK %p %d.%d message too short for "
1022 		       "initialisation vector (%zd bytes)\n", peerblk,
1023 		       peerblk->segment, peerblk->block, len );
1024 		return -ERANGE;
1025 	}
1026 
1027 	/* Check initialisation vector size */
1028 	if ( ntohl ( msg->msg.iv.iv.blksize ) != blksize ) {
1029 		DBGC ( peerblk, "PEERBLK %p %d.%d incorrect IV size %d\n",
1030 		       peerblk, peerblk->segment, peerblk->block,
1031 		       ntohl ( msg->msg.iv.iv.blksize ) );
1032 		return -EPROTO;
1033 	}
1034 
1035 	/* Set initialisation vector */
1036 	cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data );
1037 
1038 	return 0;
1039 }
1040 
1041 /**
1042  * Read from decryption buffers
1043  *
1044  * @v peerblk		PeerDist block download
1045  * @v data		Data buffer
1046  * @v len		Length to read
1047  * @ret rc		Return status code
1048  */
peerblk_decrypt_read(struct peerdist_block * peerblk,void * data,size_t len)1049 static int peerblk_decrypt_read ( struct peerdist_block *peerblk,
1050 				  void *data, size_t len ) {
1051 	struct peerdist_block_decrypt *decrypt = peerblk->decrypt;
1052 	size_t frag_len;
1053 	int rc;
1054 
1055 	/* Read from each decryption buffer in turn */
1056 	for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) {
1057 
1058 		/* Calculate length to use from this buffer */
1059 		frag_len = decrypt->len;
1060 		if ( frag_len > len )
1061 			frag_len = len;
1062 		if ( ! frag_len )
1063 			continue;
1064 
1065 		/* Read from this buffer */
1066 		if ( ( rc = xferbuf_read ( decrypt->xferbuf, decrypt->offset,
1067 					   data, frag_len ) ) != 0 )
1068 			return rc;
1069 	}
1070 
1071 	return 0;
1072 }
1073 
1074 /**
1075  * Write to decryption buffers and update offsets and lengths
1076  *
1077  * @v peerblk		PeerDist block download
1078  * @v data		Data buffer
1079  * @v len		Length to read
1080  * @ret rc		Return status code
1081  */
peerblk_decrypt_write(struct peerdist_block * peerblk,const void * data,size_t len)1082 static int peerblk_decrypt_write ( struct peerdist_block *peerblk,
1083 				   const void *data, size_t len ) {
1084 	struct peerdist_block_decrypt *decrypt = peerblk->decrypt;
1085 	size_t frag_len;
1086 	int rc;
1087 
1088 	/* Write to each decryption buffer in turn */
1089 	for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) {
1090 
1091 		/* Calculate length to use from this buffer */
1092 		frag_len = decrypt->len;
1093 		if ( frag_len > len )
1094 			frag_len = len;
1095 		if ( ! frag_len )
1096 			continue;
1097 
1098 		/* Write to this buffer */
1099 		if ( ( rc = xferbuf_write ( decrypt->xferbuf, decrypt->offset,
1100 					    data, frag_len ) ) != 0 )
1101 			return rc;
1102 
1103 		/* Update offset and length */
1104 		decrypt->offset += frag_len;
1105 		decrypt->len -= frag_len;
1106 	}
1107 
1108 	return 0;
1109 }
1110 
1111 /**
1112  * Decrypt one chunk of PeerDist retrieval protocol data
1113  *
1114  * @v peerblk		PeerDist block download
1115  */
peerblk_decrypt(struct peerdist_block * peerblk)1116 static void peerblk_decrypt ( struct peerdist_block *peerblk ) {
1117 	struct cipher_algorithm *cipher = peerblk->cipher;
1118 	struct digest_algorithm *digest = peerblk->digest;
1119 	struct xfer_buffer *xferbuf;
1120 	size_t cipher_len;
1121 	size_t digest_len;
1122 	void *data;
1123 	int rc;
1124 
1125 	/* Sanity check */
1126 	assert ( ( PEERBLK_DECRYPT_CHUNKSIZE % cipher->blocksize ) == 0 );
1127 
1128 	/* Get the underlying data transfer buffer */
1129 	xferbuf = xfer_buffer ( &peerblk->xfer );
1130 	if ( ! xferbuf ) {
1131 		DBGC ( peerblk, "PEERBLK %p %d.%d has no underlying data "
1132 		       "transfer buffer\n", peerblk, peerblk->segment,
1133 		       peerblk->block );
1134 		rc = -ENOTSUP;
1135 		goto err_xfer_buffer;
1136 	}
1137 	peerblk->decrypt[PEERBLK_DURING].xferbuf = xferbuf;
1138 
1139 	/* Calculate cipher and digest lengths */
1140 	cipher_len = PEERBLK_DECRYPT_CHUNKSIZE;
1141 	if ( cipher_len > peerblk->cipher_remaining )
1142 		cipher_len = peerblk->cipher_remaining;
1143 	digest_len = cipher_len;
1144 	if ( digest_len > peerblk->digest_remaining )
1145 		digest_len = peerblk->digest_remaining;
1146 	assert ( ( cipher_len & ( cipher->blocksize - 1 ) ) == 0 );
1147 
1148 	/* Allocate temporary data buffer */
1149 	data = malloc ( cipher_len );
1150 	if ( ! data ) {
1151 		rc = -ENOMEM;
1152 		goto err_alloc_data;
1153 	}
1154 
1155 	/* Read ciphertext */
1156 	if ( ( rc = peerblk_decrypt_read ( peerblk, data, cipher_len ) ) != 0 ){
1157 		DBGC ( peerblk, "PEERBLK %p %d.%d could not read ciphertext: "
1158 		       "%s\n", peerblk, peerblk->segment, peerblk->block,
1159 		       strerror ( rc ) );
1160 		goto err_read;
1161 	}
1162 
1163 	/* Decrypt data */
1164 	cipher_decrypt ( cipher, peerblk->cipherctx, data, data, cipher_len );
1165 
1166 	/* Add data to digest */
1167 	digest_update ( digest, peerblk->digestctx, data, digest_len );
1168 
1169 	/* Write plaintext */
1170 	if ( ( rc = peerblk_decrypt_write ( peerblk, data, cipher_len ) ) != 0){
1171 		DBGC ( peerblk, "PEERBLK %p %d.%d could not write plaintext: "
1172 		       "%s\n", peerblk, peerblk->segment, peerblk->block,
1173 		       strerror ( rc ) );
1174 		goto err_write;
1175 	}
1176 
1177 	/* Consume input */
1178 	peerblk->cipher_remaining -= cipher_len;
1179 	peerblk->digest_remaining -= digest_len;
1180 
1181 	/* Free temporary data buffer */
1182 	free ( data );
1183 
1184 	/* Continue processing until all input is consumed */
1185 	if ( peerblk->cipher_remaining )
1186 		return;
1187 
1188 	/* Complete download attempt */
1189 	peerblk_done ( peerblk, 0 );
1190 	return;
1191 
1192  err_write:
1193  err_read:
1194 	free ( data );
1195  err_alloc_data:
1196  err_xfer_buffer:
1197 	peerblk_done ( peerblk, rc );
1198 }
1199 
1200 /**
1201  * Close PeerDist retrieval protocol block download attempt
1202  *
1203  * @v peerblk		PeerDist block download
1204  * @v rc		Reason for close
1205  */
peerblk_retrieval_close(struct peerdist_block * peerblk,int rc)1206 static void peerblk_retrieval_close ( struct peerdist_block *peerblk, int rc ) {
1207 	size_t buf_len;
1208 	size_t vrf_len;
1209 
1210 	/* Restart interface */
1211 	intf_restart ( &peerblk->retrieval, rc );
1212 
1213 	/* Fail immediately if we have an error */
1214 	if ( rc != 0 )
1215 		goto done;
1216 
1217 	/* Abort download attempt (for testing) if applicable */
1218 	if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 )
1219 		goto done;
1220 
1221 	/* Parse message header */
1222 	if ( ( rc = peerblk_parse_header ( peerblk ) ) != 0 )
1223 		goto done;
1224 
1225 	/* Parse message segment and block details */
1226 	if ( ( rc = peerblk_parse_block ( peerblk, &buf_len ) ) != 0 )
1227 		goto done;
1228 
1229 	/* If the block was plaintext, then there is nothing more to do */
1230 	if ( ! peerblk->cipher )
1231 		goto done;
1232 
1233 	/* Parse message useless details */
1234 	if ( ( rc = peerblk_parse_useless ( peerblk, buf_len, &vrf_len ) ) != 0)
1235 		goto done;
1236 
1237 	/* Parse message initialisation vector details */
1238 	if ( ( rc = peerblk_parse_iv ( peerblk, buf_len, vrf_len ) ) != 0 )
1239 		goto done;
1240 
1241 	/* Fail if decryption length is not aligned to the cipher block size */
1242 	if ( peerblk->cipher_remaining & ( peerblk->cipher->blocksize - 1 ) ) {
1243 		DBGC ( peerblk, "PEERBLK %p %d.%d unaligned data length %zd\n",
1244 		       peerblk, peerblk->segment, peerblk->block,
1245 		       peerblk->cipher_remaining );
1246 		rc = -EPROTO;
1247 		goto done;
1248 	}
1249 
1250 	/* Stop the download attempt timer: there is no point in
1251 	 * timing out while decrypting.
1252 	 */
1253 	stop_timer ( &peerblk->timer );
1254 
1255 	/* Start decryption process */
1256 	process_add ( &peerblk->process );
1257 	return;
1258 
1259  done:
1260 	/* Complete download attempt */
1261 	peerblk_done ( peerblk, rc );
1262 }
1263 
1264 /******************************************************************************
1265  *
1266  * Retry policy
1267  *
1268  ******************************************************************************
1269  */
1270 
1271 /**
1272  * Handle PeerDist retry timer expiry
1273  *
1274  * @v timer		Retry timer
1275  * @v over		Failure indicator
1276  */
peerblk_expired(struct retry_timer * timer,int over __unused)1277 static void peerblk_expired ( struct retry_timer *timer, int over __unused ) {
1278 	struct peerdist_block *peerblk =
1279 		container_of ( timer, struct peerdist_block, timer );
1280 	struct peerdisc_segment *segment = peerblk->discovery.segment;
1281 	struct peerdisc_peer *head;
1282 	unsigned long now = peerblk_timestamp();
1283 	const char *location;
1284 	int rc;
1285 
1286 	/* Profile discovery timeout, if applicable */
1287 	if ( ( peerblk->peer == NULL ) && ( timer->timeout != 0 ) ) {
1288 		profile_custom ( &peerblk_discovery_timeout_profiler,
1289 				 ( now - peerblk->started ) );
1290 		DBGC ( peerblk, "PEERBLK %p %d.%d discovery timed out after "
1291 		       "%ld ticks\n", peerblk, peerblk->segment,
1292 		       peerblk->block, timer->timeout );
1293 	}
1294 
1295 	/* Profile download timeout, if applicable */
1296 	if ( ( peerblk->peer != NULL ) && ( timer->timeout != 0 ) ) {
1297 		profile_custom ( &peerblk_attempt_timeout_profiler,
1298 				 ( now - peerblk->attempted ) );
1299 		DBGC ( peerblk, "PEERBLK %p %d.%d timed out after %ld ticks\n",
1300 		       peerblk, peerblk->segment, peerblk->block,
1301 		       timer->timeout );
1302 	}
1303 
1304 	/* Abort any current download attempt */
1305 	peerblk_reset ( peerblk, -ETIMEDOUT );
1306 
1307 	/* Record attempt start time */
1308 	peerblk->attempted = now;
1309 
1310 	/* If we have exceeded our maximum number of attempt cycles
1311 	 * (each cycle comprising a retrieval protocol download from
1312 	 * each peer in the list followed by a raw download from the
1313 	 * origin server), then abort the overall download.
1314 	 */
1315 	head = list_entry ( &segment->peers, struct peerdisc_peer, list );
1316 	if ( ( peerblk->peer == head ) &&
1317 	     ( ++peerblk->cycles >= PEERBLK_MAX_ATTEMPT_CYCLES ) ) {
1318 		rc = peerblk->rc;
1319 		assert ( rc != 0 );
1320 		goto err;
1321 	}
1322 
1323 	/* If we have not yet made any download attempts, then move to
1324 	 * the start of the peer list.
1325 	 */
1326 	if ( peerblk->peer == NULL )
1327 		peerblk->peer = head;
1328 
1329 	/* Attempt retrieval protocol download from next usable peer */
1330 	list_for_each_entry_continue ( peerblk->peer, &segment->peers, list ) {
1331 
1332 		/* Attempt retrieval protocol download from this peer */
1333 		location = peerblk->peer->location;
1334 		if ( ( rc = peerblk_retrieval_open ( peerblk,
1335 						     location ) ) != 0 ) {
1336 			/* Non-fatal: continue to try next peer */
1337 			continue;
1338 		}
1339 
1340 		/* Peer download started */
1341 		return;
1342 	}
1343 
1344 	/* Add to raw download queue */
1345 	peerblk_enqueue ( peerblk, &peerblk_raw_queue );
1346 
1347 	return;
1348 
1349  err:
1350 	peerblk_close ( peerblk, rc );
1351 }
1352 
1353 /**
1354  * Handle PeerDist peer discovery
1355  *
1356  * @v discovery		PeerDist discovery client
1357  */
peerblk_discovered(struct peerdisc_client * discovery)1358 static void peerblk_discovered ( struct peerdisc_client *discovery ) {
1359 	struct peerdist_block *peerblk =
1360 		container_of ( discovery, struct peerdist_block, discovery );
1361 	unsigned long now = peerblk_timestamp();
1362 
1363 	/* Do nothing unless we are still waiting for the initial
1364 	 * discovery timeout.
1365 	 */
1366 	if ( ( peerblk->peer != NULL ) || ( peerblk->timer.timeout == 0 ) )
1367 		return;
1368 
1369 	/* Schedule an immediate retry */
1370 	start_timer_nodelay ( &peerblk->timer );
1371 
1372 	/* Profile discovery success */
1373 	profile_custom ( &peerblk_discovery_success_profiler,
1374 			 ( now - peerblk->started ) );
1375 }
1376 
1377 /******************************************************************************
1378  *
1379  * Opener
1380  *
1381  ******************************************************************************
1382  */
1383 
1384 /** PeerDist block download data transfer interface operations */
1385 static struct interface_operation peerblk_xfer_operations[] = {
1386 	INTF_OP ( intf_close, struct peerdist_block *, peerblk_close ),
1387 };
1388 
1389 /** PeerDist block download data transfer interface descriptor */
1390 static struct interface_descriptor peerblk_xfer_desc =
1391 	INTF_DESC ( struct peerdist_block, xfer, peerblk_xfer_operations );
1392 
1393 /** PeerDist block download raw data interface operations */
1394 static struct interface_operation peerblk_raw_operations[] = {
1395 	INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_raw_rx ),
1396 	INTF_OP ( intf_close, struct peerdist_block *, peerblk_raw_close ),
1397 };
1398 
1399 /** PeerDist block download raw data interface descriptor */
1400 static struct interface_descriptor peerblk_raw_desc =
1401 	INTF_DESC ( struct peerdist_block, raw, peerblk_raw_operations );
1402 
1403 /** PeerDist block download retrieval protocol interface operations */
1404 static struct interface_operation peerblk_retrieval_operations[] = {
1405 	INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_retrieval_rx ),
1406 	INTF_OP ( intf_close, struct peerdist_block *, peerblk_retrieval_close),
1407 };
1408 
1409 /** PeerDist block download retrieval protocol interface descriptor */
1410 static struct interface_descriptor peerblk_retrieval_desc =
1411 	INTF_DESC ( struct peerdist_block, retrieval,
1412 		    peerblk_retrieval_operations );
1413 
1414 /** PeerDist block download decryption process descriptor */
1415 static struct process_descriptor peerblk_process_desc =
1416 	PROC_DESC ( struct peerdist_block, process, peerblk_decrypt );
1417 
1418 /** PeerDist block download discovery operations */
1419 static struct peerdisc_client_operations peerblk_discovery_operations = {
1420 	.discovered = peerblk_discovered,
1421 };
1422 
1423 /**
1424  * Open PeerDist block download
1425  *
1426  * @v xfer		Data transfer interface
1427  * @v uri		Original URI
1428  * @v info		Content information block
1429  * @ret rc		Return status code
1430  */
peerblk_open(struct interface * xfer,struct uri * uri,struct peerdist_info_block * block)1431 int peerblk_open ( struct interface *xfer, struct uri *uri,
1432 		   struct peerdist_info_block *block ) {
1433 	const struct peerdist_info_segment *segment = block->segment;
1434 	const struct peerdist_info *info = segment->info;
1435 	struct digest_algorithm *digest = info->digest;
1436 	struct peerdist_block *peerblk;
1437 	unsigned long timeout;
1438 	size_t digestsize;
1439 	int rc;
1440 
1441 	/* Allocate and initialise structure */
1442 	peerblk = zalloc ( sizeof ( *peerblk ) + digest->ctxsize );
1443 	if ( ! peerblk ) {
1444 		rc = -ENOMEM;
1445 		goto err_alloc;
1446 	}
1447 	ref_init ( &peerblk->refcnt, peerblk_free );
1448 	intf_init ( &peerblk->xfer, &peerblk_xfer_desc, &peerblk->refcnt );
1449 	intf_init ( &peerblk->raw, &peerblk_raw_desc, &peerblk->refcnt );
1450 	intf_init ( &peerblk->retrieval, &peerblk_retrieval_desc,
1451 		    &peerblk->refcnt );
1452 	peerblk->uri = uri_get ( uri );
1453 	memcpy ( &peerblk->range, &block->range, sizeof ( peerblk->range ) );
1454 	memcpy ( &peerblk->trim, &block->trim, sizeof ( peerblk->trim ) );
1455 	peerblk->offset = ( block->trim.start - info->trim.start );
1456 	peerblk->digest = info->digest;
1457 	peerblk->digestsize = digestsize = info->digestsize;
1458 	peerblk->digestctx = ( ( ( void * ) peerblk ) + sizeof ( *peerblk ) );
1459 	peerblk->segment = segment->index;
1460 	memcpy ( peerblk->id, segment->id, sizeof ( peerblk->id ) );
1461 	memcpy ( peerblk->secret, segment->secret, sizeof ( peerblk->secret ) );
1462 	peerblk->block = block->index;
1463 	memcpy ( peerblk->hash, block->hash, sizeof ( peerblk->hash ) );
1464 	xferbuf_malloc_init ( &peerblk->buffer );
1465 	process_init_stopped ( &peerblk->process, &peerblk_process_desc,
1466 			       &peerblk->refcnt );
1467 	peerdisc_init ( &peerblk->discovery, &peerblk_discovery_operations );
1468 	INIT_LIST_HEAD ( &peerblk->queued );
1469 	timer_init ( &peerblk->timer, peerblk_expired, &peerblk->refcnt );
1470 	DBGC2 ( peerblk, "PEERBLK %p %d.%d id %02x%02x%02x%02x%02x..."
1471 		"%02x%02x%02x [%08zx,%08zx)", peerblk, peerblk->segment,
1472 		peerblk->block, peerblk->id[0], peerblk->id[1], peerblk->id[2],
1473 		peerblk->id[3], peerblk->id[4], peerblk->id[ digestsize - 3 ],
1474 		peerblk->id[ digestsize - 2 ], peerblk->id[ digestsize - 1 ],
1475 		peerblk->range.start, peerblk->range.end );
1476 	if ( ( peerblk->trim.start != peerblk->range.start ) ||
1477 	     ( peerblk->trim.end != peerblk->range.end ) ) {
1478 		DBGC2 ( peerblk, " covers [%08zx,%08zx)",
1479 			peerblk->trim.start, peerblk->trim.end );
1480 	}
1481 	DBGC2 ( peerblk, "\n" );
1482 
1483 	/* Open discovery */
1484 	if ( ( rc = peerdisc_open ( &peerblk->discovery, peerblk->id,
1485 				    peerblk->digestsize ) ) != 0 )
1486 		goto err_open_discovery;
1487 
1488 	/* Schedule a retry attempt either immediately (if we already
1489 	 * have some peers) or after the discovery timeout.
1490 	 */
1491 	timeout = ( list_empty ( &peerblk->discovery.segment->peers ) ?
1492 		    ( peerdisc_timeout_secs * TICKS_PER_SEC ) : 0 );
1493 	start_timer_fixed ( &peerblk->timer, timeout );
1494 
1495 	/* Record start time */
1496 	peerblk->started = peerblk_timestamp();
1497 
1498 	/* Attach to parent interface, mortalise self, and return */
1499 	intf_plug_plug ( xfer, &peerblk->xfer );
1500 	ref_put ( &peerblk->refcnt );
1501 	return 0;
1502 
1503  err_open_discovery:
1504 	peerblk_close ( peerblk, rc );
1505  err_alloc:
1506 	return rc;
1507 }
1508