Browse Source

[http] Rewrite HTTP core to support content encodings

Rewrite the HTTP core to allow for the addition of arbitrary content
encoding mechanisms, such as PeerDist and gzip.

The core now exposes http_open() which can be used to create requests
with an explicitly selected HTTP method, an optional requested content
range, and an optional request body.  A simple wrapper provides the
preexisting behaviour of creating either a GET request or an
application/x-www-form-urlencoded POST request (if the URI includes
parameters).

The HTTP SAN interface is now implemented using the generic block
device translator.  Individual blocks are requested using http_open()
to create a range request.

Server connections are now managed via a connection pool; this allows
for multiple requests to the same server (e.g. for SAN blocks) to be
completely unaware of each other.  Repeated HTTPS connections to the
same server can reuse a pooled connection, avoiding the per-connection
overhead of establishing a TLS session (which can take several seconds
if using a client certificate).

Support for HTTP SAN booting and for the Basic and Digest
authentication schemes is now optional and can be controlled via the
SANBOOT_PROTO_HTTP, HTTP_AUTH_BASIC, and HTTP_AUTH_DIGEST build
configuration options in config/general.h.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
518a98eb56

+ 3
- 0
src/config/config.c View File

@@ -140,6 +140,9 @@ REQUIRE_OBJECT ( slam );
140 140
 #ifdef SANBOOT_PROTO_ISCSI
141 141
 REQUIRE_OBJECT ( iscsi );
142 142
 #endif
143
+#ifdef SANBOOT_PROTO_HTTP
144
+REQUIRE_OBJECT ( httpblock );
145
+#endif
143 146
 
144 147
 /*
145 148
  * Drag in all requested resolvers

+ 42
- 0
src/config/config_http.c View File

@@ -0,0 +1,42 @@
1
+/*
2
+ * This program is free software; you can redistribute it and/or
3
+ * modify it under the terms of the GNU General Public License as
4
+ * published by the Free Software Foundation; either version 2 of the
5
+ * License, or (at your option) any later version.
6
+ *
7
+ * This program is distributed in the hope that it will be useful, but
8
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10
+ * General Public License for more details.
11
+ *
12
+ * You should have received a copy of the GNU General Public License
13
+ * along with this program; if not, write to the Free Software
14
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
+ * 02110-1301, USA.
16
+ *
17
+ * You can also choose to distribute this program under the terms of
18
+ * the Unmodified Binary Distribution Licence (as given in the file
19
+ * COPYING.UBDL), provided that you have satisfied its requirements.
20
+ */
21
+
22
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
23
+
24
+#include <config/general.h>
25
+
26
+/** @file
27
+ *
28
+ * HTTP extensions
29
+ *
30
+ */
31
+
32
+PROVIDE_REQUIRING_SYMBOL();
33
+
34
+/*
35
+ * Drag in HTTP extensions
36
+ */
37
+#ifdef HTTP_AUTH_BASIC
38
+REQUIRE_OBJECT ( httpbasic );
39
+#endif
40
+#ifdef HTTP_AUTH_DIGEST
41
+REQUIRE_OBJECT ( httpdigest );
42
+#endif

+ 1
- 0
src/config/defaults/pcbios.h View File

@@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
35 35
 #define	SANBOOT_PROTO_AOE	/* AoE protocol */
36 36
 #define	SANBOOT_PROTO_IB_SRP	/* Infiniband SCSI RDMA protocol */
37 37
 #define	SANBOOT_PROTO_FCP	/* Fibre Channel protocol */
38
+#define SANBOOT_PROTO_HTTP	/* HTTP SAN protocol */
38 39
 
39 40
 #define	USB_HCD_XHCI		/* xHCI USB host controller */
40 41
 #define	USB_HCD_EHCI		/* EHCI USB host controller */

+ 8
- 0
src/config/general.h View File

@@ -67,6 +67,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
67 67
 //#undef	SANBOOT_PROTO_AOE	/* AoE protocol */
68 68
 //#undef	SANBOOT_PROTO_IB_SRP	/* Infiniband SCSI RDMA protocol */
69 69
 //#undef	SANBOOT_PROTO_FCP	/* Fibre Channel protocol */
70
+//#undef	SANBOOT_PROTO_HTTP	/* HTTP SAN protocol */
71
+
72
+/*
73
+ * HTTP extensions
74
+ *
75
+ */
76
+#define HTTP_AUTH_BASIC		/* Basic authentication */
77
+#define HTTP_AUTH_DIGEST	/* Digest authentication */
70 78
 
71 79
 /*
72 80
  * 802.11 cryptosystems and handshaking protocols

+ 4
- 0
src/include/ipxe/errfile.h View File

@@ -248,6 +248,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
248 248
 #define ERRFILE_pccrc			( ERRFILE_NET | 0x003e0000 )
249 249
 #define ERRFILE_stp			( ERRFILE_NET | 0x003f0000 )
250 250
 #define ERRFILE_pccrd			( ERRFILE_NET | 0x00400000 )
251
+#define ERRFILE_httpconn		( ERRFILE_NET | 0x00410000 )
252
+#define ERRFILE_httpauth		( ERRFILE_NET | 0x00420000 )
253
+#define ERRFILE_httpbasic		( ERRFILE_NET | 0x00430000 )
254
+#define ERRFILE_httpdigest		( ERRFILE_NET | 0x00440000 )
251 255
 
252 256
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
253 257
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 483
- 5
src/include/ipxe/http.h View File

@@ -9,16 +9,494 @@
9 9
 
10 10
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11 11
 
12
+#include <stdint.h>
13
+#include <ipxe/refcnt.h>
14
+#include <ipxe/interface.h>
15
+#include <ipxe/iobuf.h>
16
+#include <ipxe/process.h>
17
+#include <ipxe/retry.h>
18
+#include <ipxe/linebuf.h>
19
+#include <ipxe/pool.h>
20
+#include <ipxe/tables.h>
21
+
22
+struct http_transaction;
23
+
24
+/******************************************************************************
25
+ *
26
+ * HTTP URI schemes
27
+ *
28
+ ******************************************************************************
29
+ */
30
+
12 31
 /** HTTP default port */
13 32
 #define HTTP_PORT 80
14 33
 
15 34
 /** HTTPS default port */
16 35
 #define HTTPS_PORT 443
17 36
 
18
-extern int http_open_filter ( struct interface *xfer, struct uri *uri,
19
-			      unsigned int default_port,
20
-			      int ( * filter ) ( struct interface *,
21
-						 const char *,
22
-						 struct interface ** ) );
37
+/** An HTTP URI scheme */
38
+struct http_scheme {
39
+	/** Scheme name (e.g. "http" or "https") */
40
+	const char *name;
41
+	/** Default port */
42
+	unsigned int port;
43
+	/** Transport-layer filter (if any)
44
+	 *
45
+	 * @v xfer		Data transfer interface
46
+	 * @v name		Host name
47
+	 * @v next		Next interface
48
+	 * @ret rc		Return status code
49
+	 */
50
+	int ( * filter ) ( struct interface *xfer, const char *name,
51
+			   struct interface **next );
52
+};
53
+
54
+/** HTTP scheme table */
55
+#define HTTP_SCHEMES __table ( struct http_scheme, "http_schemes" )
56
+
57
+/** Declare an HTTP scheme */
58
+#define __http_scheme __table_entry ( HTTP_SCHEMES, 01 )
59
+
60
+/******************************************************************************
61
+ *
62
+ * Connections
63
+ *
64
+ ******************************************************************************
65
+ */
66
+
67
+/** An HTTP connection
68
+ *
69
+ * This represents a potentially reusable connection to an HTTP
70
+ * server.
71
+ */
72
+struct http_connection {
73
+	/** Reference count */
74
+	struct refcnt refcnt;
75
+	/** Connection URI
76
+	 *
77
+	 * This encapsulates the server (and protocol) used for the
78
+	 * connection.  This may be the origin server or a proxy
79
+	 * server.
80
+	 */
81
+	struct uri *uri;
82
+	/** HTTP scheme */
83
+	struct http_scheme *scheme;
84
+	/** Transport layer interface */
85
+	struct interface socket;
86
+	/** Data transfer interface */
87
+	struct interface xfer;
88
+	/** Pooled connection */
89
+	struct pooled_connection pool;
90
+};
91
+
92
+/******************************************************************************
93
+ *
94
+ * HTTP methods
95
+ *
96
+ ******************************************************************************
97
+ */
98
+
99
+/** An HTTP method */
100
+struct http_method {
101
+	/** Method name (e.g. "GET" or "POST") */
102
+	const char *name;
103
+};
104
+
105
+extern struct http_method http_head;
106
+extern struct http_method http_get;
107
+extern struct http_method http_post;
108
+
109
+/******************************************************************************
110
+ *
111
+ * Requests
112
+ *
113
+ ******************************************************************************
114
+ */
115
+
116
+/** HTTP Digest authentication client nonce count
117
+ *
118
+ * We choose to generate a new client nonce each time.
119
+ */
120
+#define HTTP_DIGEST_NC "00000001"
121
+
122
+/** HTTP Digest authentication client nonce length
123
+ *
124
+ * We choose to use a 32-bit hex client nonce.
125
+ */
126
+#define HTTP_DIGEST_CNONCE_LEN 8
127
+
128
+/** HTTP Digest authentication response length
129
+ *
130
+ * The Digest authentication response is a Base16-encoded 16-byte MD5
131
+ * checksum.
132
+ */
133
+#define HTTP_DIGEST_RESPONSE_LEN 32
134
+
135
+/** HTTP request range descriptor */
136
+struct http_request_range {
137
+	/** Range start */
138
+	size_t start;
139
+	/** Range length, or zero for no range request */
140
+	size_t len;
141
+};
142
+
143
+/** HTTP request content descriptor */
144
+struct http_request_content {
145
+	/** Content type (if any) */
146
+	const char *type;
147
+	/** Content data (if any) */
148
+	const void *data;
149
+	/** Content length */
150
+	size_t len;
151
+};
152
+
153
+/** HTTP request authentication descriptor */
154
+struct http_request_auth {
155
+	/** Authentication scheme (if any) */
156
+	struct http_authentication *auth;
157
+	/** Username */
158
+	const char *username;
159
+	/** Password */
160
+	const char *password;
161
+	/** Quality of protection */
162
+	const char *qop;
163
+	/** Algorithm */
164
+	const char *algorithm;
165
+	/** Client nonce */
166
+	char cnonce[ HTTP_DIGEST_CNONCE_LEN + 1 /* NUL */ ];
167
+	/** Response */
168
+	char response[ HTTP_DIGEST_RESPONSE_LEN + 1 /* NUL */ ];
169
+};
170
+
171
+/** An HTTP request
172
+ *
173
+ * This represents a single request to be sent to a server, including
174
+ * the values required to construct all headers.
175
+ *
176
+ * Pointers within this structure must point to storage which is
177
+ * guaranteed to remain valid for the lifetime of the containing HTTP
178
+ * transaction.
179
+ */
180
+struct http_request {
181
+	/** Method */
182
+	struct http_method *method;
183
+	/** Request URI string */
184
+	const char *uri;
185
+	/** Server host name */
186
+	const char *host;
187
+	/** Range descriptor */
188
+	struct http_request_range range;
189
+	/** Content descriptor */
190
+	struct http_request_content content;
191
+	/** Authentication descriptor */
192
+	struct http_request_auth auth;
193
+};
194
+
195
+/** An HTTP request header */
196
+struct http_request_header {
197
+	/** Header name (e.g. "User-Agent") */
198
+	const char *name;
199
+	/** Construct remaining header line
200
+	 *
201
+	 * @v http		HTTP transaction
202
+	 * @v buf		Buffer
203
+	 * @v len		Length of buffer
204
+	 * @ret len		Header length if present, or negative error
205
+	 */
206
+	int ( * format ) ( struct http_transaction *http, char *buf,
207
+			   size_t len );
208
+};
209
+
210
+/** HTTP request header table */
211
+#define HTTP_REQUEST_HEADERS \
212
+	__table ( struct http_request_header, "http_request_headers" )
213
+
214
+/** Declare an HTTP request header */
215
+#define __http_request_header __table_entry ( HTTP_REQUEST_HEADERS, 01 )
216
+
217
+/******************************************************************************
218
+ *
219
+ * Responses
220
+ *
221
+ ******************************************************************************
222
+ */
223
+
224
+/** HTTP response transfer descriptor */
225
+struct http_response_transfer {
226
+	/** Transfer encoding */
227
+	struct http_transfer_encoding *encoding;
228
+};
229
+
230
+/** HTTP response content descriptor */
231
+struct http_response_content {
232
+	/** Content length (may be zero) */
233
+	size_t len;
234
+	/** Content encoding */
235
+	struct http_content_encoding *encoding;
236
+};
237
+
238
+/** HTTP response authorization descriptor */
239
+struct http_response_auth {
240
+	/** Authentication scheme (if any) */
241
+	struct http_authentication *auth;
242
+	/** Realm */
243
+	const char *realm;
244
+	/** Quality of protection */
245
+	const char *qop;
246
+	/** Algorithm */
247
+	const char *algorithm;
248
+	/** Nonce */
249
+	const char *nonce;
250
+	/** Opaque */
251
+	const char *opaque;
252
+};
253
+
254
+/** An HTTP response
255
+ *
256
+ * This represents a single response received from the server,
257
+ * including all values parsed from headers.
258
+ *
259
+ * Pointers within this structure may point into the raw response
260
+ * buffer, and so should be invalidated when the response buffer is
261
+ * modified or discarded.
262
+ */
263
+struct http_response {
264
+	/** Raw response header lines
265
+	 *
266
+	 * This is the raw response data received from the server, up
267
+	 * to and including the terminating empty line.  String
268
+	 * pointers within the response may point into this data
269
+	 * buffer; NUL terminators will be added (overwriting the
270
+	 * original terminating characters) as needed.
271
+	 */
272
+	struct line_buffer headers;
273
+	/** Status code
274
+	 *
275
+	 * This is the raw HTTP numeric status code (e.g. 404).
276
+	 */
277
+	unsigned int status;
278
+	/** Return status code
279
+	 *
280
+	 * This is the iPXE return status code corresponding to the
281
+	 * HTTP status code (e.g. -ENOENT).
282
+	 */
283
+	int rc;
284
+	/** Redirection location */
285
+	const char *location;
286
+	/** Transfer descriptor */
287
+	struct http_response_transfer transfer;
288
+	/** Content descriptor */
289
+	struct http_response_content content;
290
+	/** Authorization descriptor */
291
+	struct http_response_auth auth;
292
+	/** Retry delay (in seconds) */
293
+	unsigned int retry_after;
294
+	/** Flags */
295
+	unsigned int flags;
296
+};
297
+
298
+/** HTTP response flags */
299
+enum http_response_flags {
300
+	/** Keep connection alive after close */
301
+	HTTP_RESPONSE_KEEPALIVE = 0x0001,
302
+	/** Content length specified */
303
+	HTTP_RESPONSE_CONTENT_LEN = 0x0002,
304
+	/** Transaction may be retried on failure */
305
+	HTTP_RESPONSE_RETRY = 0x0004,
306
+};
307
+
308
+/** An HTTP response header */
309
+struct http_response_header {
310
+	/** Header name (e.g. "Transfer-Encoding") */
311
+	const char *name;
312
+	/** Parse header line
313
+	 *
314
+	 * @v http		HTTP transaction
315
+	 * @v line		Remaining header line
316
+	 * @ret rc		Return status code
317
+	 */
318
+	int ( * parse ) ( struct http_transaction *http, char *line );
319
+};
320
+
321
+/** HTTP response header table */
322
+#define HTTP_RESPONSE_HEADERS \
323
+	__table ( struct http_response_header, "http_response_headers" )
324
+
325
+/** Declare an HTTP response header */
326
+#define __http_response_header __table_entry ( HTTP_RESPONSE_HEADERS, 01 )
327
+
328
+/******************************************************************************
329
+ *
330
+ * Transactions
331
+ *
332
+ ******************************************************************************
333
+ */
334
+
335
+/** HTTP transaction state */
336
+struct http_state {
337
+	/** Transmit data
338
+	 *
339
+	 * @v http		HTTP transaction
340
+	 * @ret rc		Return status code
341
+	 */
342
+	int ( * tx ) ( struct http_transaction *http );
343
+	/** Receive data
344
+	 *
345
+	 * @v http		HTTP transaction
346
+	 * @v iobuf		I/O buffer (may be claimed)
347
+	 * @ret rc		Return status code
348
+	 */
349
+	int ( * rx ) ( struct http_transaction *http,
350
+		       struct io_buffer **iobuf );
351
+	/** Server connection closed
352
+	 *
353
+	 * @v http		HTTP transaction
354
+	 * @v rc		Reason for close
355
+	 */
356
+	void ( * close ) ( struct http_transaction *http, int rc );
357
+};
358
+
359
+/** An HTTP transaction */
360
+struct http_transaction {
361
+	/** Reference count */
362
+	struct refcnt refcnt;
363
+	/** Data transfer interface */
364
+	struct interface xfer;
365
+	/** Content-decoded interface */
366
+	struct interface content;
367
+	/** Transfer-decoded interface */
368
+	struct interface transfer;
369
+	/** Server connection */
370
+	struct interface conn;
371
+	/** Transmit process */
372
+	struct process process;
373
+	/** Reconnection timer */
374
+	struct retry_timer timer;
375
+
376
+	/** Request URI */
377
+	struct uri *uri;
378
+	/** Request */
379
+	struct http_request request;
380
+	/** Response */
381
+	struct http_response response;
382
+	/** Temporary line buffer */
383
+	struct line_buffer linebuf;
384
+
385
+	/** Transaction state */
386
+	struct http_state *state;
387
+	/** Accumulated transfer-decoded length */
388
+	size_t len;
389
+	/** Chunk length remaining */
390
+	size_t remaining;
391
+};
392
+
393
+/******************************************************************************
394
+ *
395
+ * Transfer encoding
396
+ *
397
+ ******************************************************************************
398
+ */
399
+
400
+/** An HTTP transfer encoding */
401
+struct http_transfer_encoding {
402
+	/** Name */
403
+	const char *name;
404
+	/** Initialise transfer encoding
405
+	 *
406
+	 * @v http		HTTP transaction
407
+	 * @ret rc		Return status code
408
+	 */
409
+	int ( * init ) ( struct http_transaction *http );
410
+	/** Receive data state */
411
+	struct http_state state;
412
+};
413
+
414
+/** HTTP transfer encoding table */
415
+#define HTTP_TRANSFER_ENCODINGS \
416
+	__table ( struct http_transfer_encoding, "http_transfer_encodings" )
417
+
418
+/** Declare an HTTP transfer encoding */
419
+#define __http_transfer_encoding __table_entry ( HTTP_TRANSFER_ENCODINGS, 01 )
420
+
421
+/******************************************************************************
422
+ *
423
+ * Content encoding
424
+ *
425
+ ******************************************************************************
426
+ */
427
+
428
+/** An HTTP content encoding */
429
+struct http_content_encoding {
430
+	/** Name */
431
+	const char *name;
432
+	/** Check if content encoding is supported for this request
433
+	 *
434
+	 * @v http		HTTP transaction
435
+	 * @ret supported	Content encoding is supported for this request
436
+	 */
437
+	int ( * supported ) ( struct http_transaction *http );
438
+	/** Initialise content encoding
439
+	 *
440
+	 * @v http		HTTP transaction
441
+	 * @ret rc		Return status code
442
+	 */
443
+	int ( * init ) ( struct http_transaction *http );
444
+};
445
+
446
+/** HTTP content encoding table */
447
+#define HTTP_CONTENT_ENCODINGS \
448
+	__table ( struct http_content_encoding, "http_content_encodings" )
449
+
450
+/** Declare an HTTP content encoding */
451
+#define __http_content_encoding __table_entry ( HTTP_CONTENT_ENCODINGS, 01 )
452
+
453
+/******************************************************************************
454
+ *
455
+ * Authentication
456
+ *
457
+ ******************************************************************************
458
+ */
459
+
460
+/** An HTTP authentication scheme */
461
+struct http_authentication {
462
+	/** Name (e.g. "Digest") */
463
+	const char *name;
464
+	/** Perform authentication
465
+	 *
466
+	 * @v http		HTTP transaction
467
+	 * @ret rc		Return status code
468
+	 */
469
+	int ( * authenticate ) ( struct http_transaction *http );
470
+	/** Construct remaining "Authorization" header line
471
+	 *
472
+	 * @v http		HTTP transaction
473
+	 * @v buf		Buffer
474
+	 * @v len		Length of buffer
475
+	 * @ret len		Header length if present, or negative error
476
+	 */
477
+	int ( * format ) ( struct http_transaction *http, char *buf,
478
+			   size_t len );
479
+};
480
+
481
+/** HTTP authentication scheme table */
482
+#define HTTP_AUTHENTICATIONS \
483
+	__table ( struct http_authentication, "http_authentications" )
484
+
485
+/** Declare an HTTP authentication scheme */
486
+#define __http_authentication __table_entry ( HTTP_AUTHENTICATIONS, 01 )
487
+
488
+/******************************************************************************
489
+ *
490
+ * General
491
+ *
492
+ ******************************************************************************
493
+ */
494
+
495
+extern char * http_token ( char **line, char **value );
496
+extern int http_connect ( struct interface *xfer, struct uri *uri );
497
+extern int http_open ( struct interface *xfer, struct http_method *method,
498
+		       struct uri *uri, struct http_request_range *range,
499
+		       struct http_request_content *content );
500
+extern int http_open_uri ( struct interface *xfer, struct uri *uri );
23 501
 
24 502
 #endif /* _IPXE_HTTP_H */

+ 7
- 13
src/net/tcp/http.c View File

@@ -30,26 +30,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
30 30
  *
31 31
  */
32 32
 
33
-#include <stddef.h>
34 33
 #include <ipxe/open.h>
35 34
 #include <ipxe/http.h>
36 35
 #include <ipxe/features.h>
37 36
 
38 37
 FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 );
39 38
 
40
-/**
41
- * Initiate an HTTP connection
42
- *
43
- * @v xfer		Data transfer interface
44
- * @v uri		Uniform Resource Identifier
45
- * @ret rc		Return status code
46
- */
47
-static int http_open ( struct interface *xfer, struct uri *uri ) {
48
-	return http_open_filter ( xfer, uri, HTTP_PORT, NULL );
49
-}
50
-
51 39
 /** HTTP URI opener */
52 40
 struct uri_opener http_uri_opener __uri_opener = {
53 41
 	.scheme	= "http",
54
-	.open	= http_open,
42
+	.open	= http_open_uri,
43
+};
44
+
45
+/** HTTP URI scheme */
46
+struct http_scheme http_scheme __http_scheme = {
47
+	.name = "http",
48
+	.port = HTTP_PORT,
55 49
 };

+ 190
- 0
src/net/tcp/httpauth.c View File

@@ -0,0 +1,190 @@
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 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
+/**
27
+ * @file
28
+ *
29
+ * Hyper Text Transfer Protocol (HTTP) authentication
30
+ *
31
+ */
32
+
33
+#include <stdio.h>
34
+#include <strings.h>
35
+#include <errno.h>
36
+#include <ipxe/http.h>
37
+
38
+/**
39
+ * Identify authentication scheme
40
+ *
41
+ * @v http		HTTP transaction
42
+ * @v name		Scheme name
43
+ * @ret auth		Authentication scheme, or NULL
44
+ */
45
+static struct http_authentication * http_authentication ( const char *name ) {
46
+	struct http_authentication *auth;
47
+
48
+	/* Identify authentication scheme */
49
+	for_each_table_entry ( auth, HTTP_AUTHENTICATIONS ) {
50
+		if ( strcasecmp ( name, auth->name ) == 0 )
51
+			return auth;
52
+	}
53
+
54
+	return NULL;
55
+}
56
+
57
+/** An HTTP "WWW-Authenticate" response field */
58
+struct http_www_authenticate_field {
59
+	/** Name */
60
+	const char *name;
61
+	/** Offset */
62
+	size_t offset;
63
+};
64
+
65
+/** Define an HTTP "WWW-Authenticate" response field */
66
+#define HTTP_WWW_AUTHENTICATE_FIELD( _name ) {				\
67
+		.name = #_name,						\
68
+		.offset = offsetof ( struct http_transaction,		\
69
+				     response.auth._name ),		\
70
+	}
71
+
72
+/**
73
+ * Set HTTP "WWW-Authenticate" response field value
74
+ *
75
+ * @v http		HTTP transaction
76
+ * @v field		Response field
77
+ * @v value		Field value
78
+ */
79
+static inline void
80
+http_www_auth_field ( struct http_transaction *http,
81
+		      struct http_www_authenticate_field *field, char *value ) {
82
+	char **ptr;
83
+
84
+	ptr = ( ( ( void * ) http ) + field->offset );
85
+	*ptr = value;
86
+}
87
+
88
+/** HTTP "WWW-Authenticate" fields */
89
+static struct http_www_authenticate_field http_www_auth_fields[] = {
90
+	HTTP_WWW_AUTHENTICATE_FIELD ( realm ),
91
+	HTTP_WWW_AUTHENTICATE_FIELD ( qop ),
92
+	HTTP_WWW_AUTHENTICATE_FIELD ( algorithm ),
93
+	HTTP_WWW_AUTHENTICATE_FIELD ( nonce ),
94
+	HTTP_WWW_AUTHENTICATE_FIELD ( opaque ),
95
+};
96
+
97
+/**
98
+ * Parse HTTP "WWW-Authenticate" header
99
+ *
100
+ * @v http		HTTP transaction
101
+ * @v line		Remaining header line
102
+ * @ret rc		Return status code
103
+ */
104
+static int http_parse_www_authenticate ( struct http_transaction *http,
105
+					 char *line ) {
106
+	struct http_www_authenticate_field *field;
107
+	char *name;
108
+	char *key;
109
+	char *value;
110
+	unsigned int i;
111
+
112
+	/* Get scheme name */
113
+	name = http_token ( &line, NULL );
114
+	if ( ! name ) {
115
+		DBGC ( http, "HTTP %p malformed WWW-Authenticate \"%s\"\n",
116
+		       http, value );
117
+		return -EPROTO;
118
+	}
119
+
120
+	/* Identify scheme */
121
+	http->response.auth.auth = http_authentication ( name );
122
+	if ( ! http->response.auth.auth ) {
123
+		DBGC ( http, "HTTP %p unrecognised authentication scheme "
124
+		       "\"%s\"\n", http, name );
125
+		return -ENOTSUP;
126
+	}
127
+
128
+	/* Process fields */
129
+	while ( ( key = http_token ( &line, &value ) ) ) {
130
+		for ( i = 0 ; i < ( sizeof ( http_www_auth_fields ) /
131
+				    sizeof ( http_www_auth_fields[0] ) ) ; i++){
132
+			field = &http_www_auth_fields[i];
133
+			if ( strcasecmp ( key, field->name ) == 0 )
134
+				http_www_auth_field ( http, field, value );
135
+		}
136
+	}
137
+
138
+	/* Allow HTTP request to be retried if the request had not
139
+	 * already tried authentication.
140
+	 */
141
+	if ( ! http->request.auth.auth )
142
+		http->response.flags |= HTTP_RESPONSE_RETRY;
143
+
144
+	return 0;
145
+}
146
+
147
+/** HTTP "WWW-Authenticate" header */
148
+struct http_response_header
149
+http_response_www_authenticate __http_response_header = {
150
+	.name = "WWW-Authenticate",
151
+	.parse = http_parse_www_authenticate,
152
+};
153
+
154
+/**
155
+ * Construct HTTP "Authorization" header
156
+ *
157
+ * @v http		HTTP transaction
158
+ * @v buf		Buffer
159
+ * @v len		Length of buffer
160
+ * @ret len		Length of header value, or negative error
161
+ */
162
+static int http_format_authorization ( struct http_transaction *http,
163
+				       char *buf, size_t len ) {
164
+	struct http_authentication *auth = http->request.auth.auth;
165
+	size_t used;
166
+	int auth_len;
167
+	int rc;
168
+
169
+	/* Do nothing unless we have an authentication scheme */
170
+	if ( ! auth )
171
+		return 0;
172
+
173
+	/* Construct header */
174
+	used = snprintf ( buf, len, "%s ", auth->name );
175
+	auth_len = auth->format ( http, ( buf + used ),
176
+				  ( ( used < len ) ? ( len - used ) : 0 ) );
177
+	if ( auth_len < 0 ) {
178
+		rc = auth_len;
179
+		return rc;
180
+	}
181
+	used += auth_len;
182
+
183
+	return used;
184
+}
185
+
186
+/** HTTP "Authorization" header */
187
+struct http_request_header http_request_authorization __http_request_header = {
188
+	.name = "Authorization",
189
+	.format = http_format_authorization,
190
+};

+ 102
- 0
src/net/tcp/httpbasic.c View File

@@ -0,0 +1,102 @@
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 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
+/**
27
+ * @file
28
+ *
29
+ * Hyper Text Transfer Protocol (HTTP) Basic authentication
30
+ *
31
+ */
32
+
33
+#include <stdio.h>
34
+#include <errno.h>
35
+#include <ipxe/uri.h>
36
+#include <ipxe/base64.h>
37
+#include <ipxe/http.h>
38
+
39
+/* Disambiguate the various error causes */
40
+#define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME )
41
+#define EINFO_EACCES_USERNAME						\
42
+	__einfo_uniqify ( EINFO_EACCES, 0x01,				\
43
+			  "No username available for Basic authentication" )
44
+
45
+/**
46
+ * Perform HTTP Basic authentication
47
+ *
48
+ * @v http		HTTP transaction
49
+ * @ret rc		Return status code
50
+ */
51
+static int http_basic_authenticate ( struct http_transaction *http ) {
52
+	struct http_request_auth *req = &http->request.auth;
53
+
54
+	/* Record username and password */
55
+	if ( ! http->uri->user ) {
56
+		DBGC ( http, "HTTP %p has no username for Basic "
57
+		       "authentication\n", http );
58
+		return -EACCES_USERNAME;
59
+	}
60
+	req->username = http->uri->user;
61
+	req->password = ( http->uri->password ? http->uri->password : "" );
62
+
63
+	return 0;
64
+}
65
+
66
+/**
67
+ * Construct HTTP "Authorization" header for Basic authentication
68
+ *
69
+ * @v http		HTTP transaction
70
+ * @v buf		Buffer
71
+ * @v len		Length of buffer
72
+ * @ret len		Length of header value, or negative error
73
+ */
74
+static int http_format_basic_auth ( struct http_transaction *http,
75
+				    char *buf, size_t len ) {
76
+	struct http_request_auth *req = &http->request.auth;
77
+	size_t user_pw_len = ( strlen ( req->username ) + 1 /* ":" */ +
78
+			       strlen ( req->password ) );
79
+	char user_pw[ user_pw_len + 1 /* NUL */ ];
80
+
81
+	/* Sanity checks */
82
+	assert ( req->username != NULL );
83
+	assert ( req->password != NULL );
84
+
85
+	/* Construct "user:password" string */
86
+	snprintf ( user_pw, sizeof ( user_pw ), "%s:%s",
87
+		   req->username, req->password );
88
+
89
+	/* Construct response */
90
+	return base64_encode ( user_pw, user_pw_len, buf, len );
91
+}
92
+
93
+/** HTTP Basic authentication scheme */
94
+struct http_authentication http_basic_auth __http_authentication = {
95
+	.name = "Basic",
96
+	.authenticate = http_basic_authenticate,
97
+	.format = http_format_basic_auth,
98
+};
99
+
100
+/* Drag in HTTP authentication support */
101
+REQUIRING_SYMBOL ( http_basic_auth );
102
+REQUIRE_OBJECT ( httpauth );

+ 134
- 0
src/net/tcp/httpblock.c View File

@@ -0,0 +1,134 @@
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 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
+/**
27
+ * @file
28
+ *
29
+ * Hyper Text Transfer Protocol (HTTP) block device
30
+ *
31
+ */
32
+
33
+#include <stdint.h>
34
+#include <ipxe/uaccess.h>
35
+#include <ipxe/blocktrans.h>
36
+#include <ipxe/blockdev.h>
37
+#include <ipxe/acpi.h>
38
+#include <ipxe/http.h>
39
+
40
+/** Block size used for HTTP block device requests */
41
+#define HTTP_BLKSIZE 512
42
+
43
+/**
44
+ * Read from block device
45
+ *
46
+ * @v http		HTTP transaction
47
+ * @v data		Data interface
48
+ * @v lba		Starting logical block address
49
+ * @v count		Number of logical blocks
50
+ * @v buffer		Data buffer
51
+ * @v len		Length of data buffer
52
+ * @ret rc		Return status code
53
+ */
54
+int http_block_read ( struct http_transaction *http, struct interface *data,
55
+		      uint64_t lba, unsigned int count, userptr_t buffer,
56
+		      size_t len ) {
57
+	struct http_request_range range;
58
+	int rc;
59
+
60
+	/* Sanity check */
61
+	assert ( len == ( count * HTTP_BLKSIZE ) );
62
+
63
+	/* Construct request range descriptor */
64
+	range.start = ( lba * HTTP_BLKSIZE );
65
+	range.len = len;
66
+
67
+	/* Start a range request to retrieve the block(s) */
68
+	if ( ( rc = http_open ( data, &http_get, http->uri, &range,
69
+				NULL ) ) != 0 )
70
+		goto err_open;
71
+
72
+	/* Insert block device translator */
73
+	if ( ( rc = block_translate ( data, buffer, len ) ) != 0 ) {
74
+		DBGC ( http, "HTTP %p could not insert block translator: %s\n",
75
+		       http, strerror ( rc ) );
76
+		goto err_translate;
77
+	}
78
+
79
+	return 0;
80
+
81
+ err_translate:
82
+	intf_restart ( data, rc );
83
+ err_open:
84
+	return rc;
85
+}
86
+
87
+/**
88
+ * Read block device capacity
89
+ *
90
+ * @v control		Control interface
91
+ * @v data		Data interface
92
+ * @ret rc		Return status code
93
+ */
94
+int http_block_read_capacity ( struct http_transaction *http,
95
+			       struct interface *data ) {
96
+	int rc;
97
+
98
+	/* Start a HEAD request to retrieve the capacity */
99
+	if ( ( rc = http_open ( data, &http_head, http->uri, NULL,
100
+				NULL ) ) != 0 )
101
+		goto err_open;
102
+
103
+	/* Insert block device translator */
104
+	if ( ( rc = block_translate ( data, UNULL, HTTP_BLKSIZE ) ) != 0 ) {
105
+		DBGC ( http, "HTTP %p could not insert block translator: %s\n",
106
+		       http, strerror ( rc ) );
107
+		goto err_translate;
108
+	}
109
+
110
+	return 0;
111
+
112
+ err_translate:
113
+	intf_restart ( data, rc );
114
+ err_open:
115
+	return rc;
116
+}
117
+
118
+/**
119
+ * Describe device in ACPI table
120
+ *
121
+ * @v http		HTTP transaction
122
+ * @v acpi		ACPI table
123
+ * @v len		Length of ACPI table
124
+ * @ret rc		Return status code
125
+ */
126
+int http_acpi_describe ( struct http_transaction *http,
127
+			 struct acpi_description_header *acpi, size_t len ) {
128
+
129
+	DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n",
130
+	       http );
131
+	( void ) acpi;
132
+	( void ) len;
133
+	return 0;
134
+}

+ 309
- 0
src/net/tcp/httpconn.c View File

@@ -0,0 +1,309 @@
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 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
+/**
27
+ * @file
28
+ *
29
+ * Hyper Text Transfer Protocol (HTTP) connection management
30
+ *
31
+ */
32
+
33
+#include <stdlib.h>
34
+#include <string.h>
35
+#include <errno.h>
36
+#include <byteswap.h>
37
+#include <ipxe/tcpip.h>
38
+#include <ipxe/uri.h>
39
+#include <ipxe/timer.h>
40
+#include <ipxe/xfer.h>
41
+#include <ipxe/open.h>
42
+#include <ipxe/pool.h>
43
+#include <ipxe/http.h>
44
+
45
+/** HTTP pooled connection expiry time */
46
+#define HTTP_CONN_EXPIRY ( 10 * TICKS_PER_SEC )
47
+
48
+/** HTTP connection pool */
49
+static LIST_HEAD ( http_connection_pool );
50
+
51
+/**
52
+ * Identify HTTP scheme
53
+ *
54
+ * @v uri		URI
55
+ * @ret scheme		HTTP scheme, or NULL
56
+ */
57
+static struct http_scheme * http_scheme ( struct uri *uri ) {
58
+	struct http_scheme *scheme;
59
+
60
+	/* Sanity check */
61
+	if ( ! uri->scheme )
62
+		return NULL;
63
+
64
+	/* Identify scheme */
65
+	for_each_table_entry ( scheme, HTTP_SCHEMES ) {
66
+		if ( strcmp ( uri->scheme, scheme->name ) == 0 )
67
+			return scheme;
68
+	}
69
+
70
+	return NULL;
71
+}
72
+
73
+/**
74
+ * Free HTTP connection
75
+ *
76
+ * @v refcnt		Reference count
77
+ */
78
+static void http_conn_free ( struct refcnt *refcnt ) {
79
+	struct http_connection *conn =
80
+		container_of ( refcnt, struct http_connection, refcnt );
81
+
82
+	/* Free connection */
83
+	uri_put ( conn->uri );
84
+	free ( conn );
85
+}
86
+
87
+/**
88
+ * Close HTTP connection
89
+ *
90
+ * @v conn		HTTP connection
91
+ * @v rc		Reason for close
92
+ */
93
+static void http_conn_close ( struct http_connection *conn, int rc ) {
94
+
95
+	/* Remove from connection pool, if applicable */
96
+	pool_del ( &conn->pool );
97
+
98
+	/* Shut down interfaces */
99
+	intf_shutdown ( &conn->socket, rc );
100
+	intf_shutdown ( &conn->xfer, rc );
101
+	if ( rc == 0 ) {
102
+		DBGC2 ( conn, "HTTPCONN %p closed %s://%s\n",
103
+			conn, conn->scheme->name, conn->uri->host );
104
+	} else {
105
+		DBGC ( conn, "HTTPCONN %p closed %s://%s: %s\n",
106
+		       conn, conn->scheme->name, conn->uri->host,
107
+		       strerror ( rc ) );
108
+	}
109
+}
110
+
111
+/**
112
+ * Disconnect idle HTTP connection
113
+ *
114
+ * @v pool		Pooled connection
115
+ */
116
+static void http_conn_expired ( struct pooled_connection *pool ) {
117
+	struct http_connection *conn =
118
+		container_of ( pool, struct http_connection, pool );
119
+
120
+	/* Close connection */
121
+	http_conn_close ( conn, 0 /* Not an error to close idle connection */ );
122
+}
123
+
124
+/**
125
+ * Receive data from transport layer interface
126
+ *
127
+ * @v http		HTTP connection
128
+ * @v iobuf		I/O buffer
129
+ * @v meta		Transfer metadata
130
+ * @ret rc		Return status code
131
+ */
132
+static int http_conn_socket_deliver ( struct http_connection *conn,
133
+				      struct io_buffer *iobuf,
134
+				      struct xfer_metadata *meta ) {
135
+
136
+	/* Mark connection as alive */
137
+	pool_alive ( &conn->pool );
138
+
139
+	/* Pass on to data transfer interface */
140
+	return xfer_deliver ( &conn->xfer, iobuf, meta );
141
+}
142
+
143
+/**
144
+ * Close HTTP connection transport layer interface
145
+ *
146
+ * @v http		HTTP connection
147
+ * @v rc		Reason for close
148
+ */
149
+static void http_conn_socket_close ( struct http_connection *conn, int rc ) {
150
+
151
+	/* If we are reopenable (i.e. we are a recycled connection
152
+	 * from the connection pool, and we have received no data from
153
+	 * the underlying socket since we were pooled), then suggest
154
+	 * that the client should reopen the connection.
155
+	 */
156
+	if ( pool_is_reopenable ( &conn->pool ) )
157
+		pool_reopen ( &conn->xfer );
158
+
159
+	/* Close the connection */
160
+	http_conn_close ( conn, rc );
161
+}
162
+
163
+/**
164
+ * Recycle this connection after closing
165
+ *
166
+ * @v http		HTTP connection
167
+ */
168
+static void http_conn_xfer_recycle ( struct http_connection *conn ) {
169
+
170
+	/* Mark connection as recyclable */
171
+	pool_recyclable ( &conn->pool );
172
+	DBGC2 ( conn, "HTTPCONN %p keepalive enabled\n", conn );
173
+}
174
+
175
+/**
176
+ * Close HTTP connection data transfer interface
177
+ *
178
+ * @v conn		HTTP connection
179
+ * @v rc		Reason for close
180
+ */
181
+static void http_conn_xfer_close ( struct http_connection *conn, int rc ) {
182
+
183
+	/* Add to the connection pool if keepalive is enabled and no
184
+	 * error occurred.
185
+	 */
186
+	if ( ( rc == 0 ) && pool_is_recyclable ( &conn->pool ) ) {
187
+		intf_restart ( &conn->xfer, rc );
188
+		pool_add ( &conn->pool, &http_connection_pool,
189
+			   HTTP_CONN_EXPIRY );
190
+		DBGC2 ( conn, "HTTPCONN %p pooled %s://%s\n",
191
+			conn, conn->scheme->name, conn->uri->host );
192
+		return;
193
+	}
194
+
195
+	/* Otherwise, close the connection */
196
+	http_conn_close ( conn, rc );
197
+}
198
+
199
+/** HTTP connection socket interface operations */
200
+static struct interface_operation http_conn_socket_operations[] = {
201
+	INTF_OP ( xfer_deliver, struct http_connection *,
202
+		  http_conn_socket_deliver ),
203
+	INTF_OP ( intf_close, struct http_connection *,
204
+		  http_conn_socket_close ),
205
+};
206
+
207
+/** HTTP connection socket interface descriptor */
208
+static struct interface_descriptor http_conn_socket_desc =
209
+	INTF_DESC_PASSTHRU ( struct http_connection, socket,
210
+			     http_conn_socket_operations, xfer );
211
+
212
+/** HTTP connection data transfer interface operations */
213
+static struct interface_operation http_conn_xfer_operations[] = {
214
+	INTF_OP ( pool_recycle, struct http_connection *,
215
+		  http_conn_xfer_recycle ),
216
+	INTF_OP ( intf_close, struct http_connection *,
217
+		  http_conn_xfer_close ),
218
+};
219
+
220
+/** HTTP connection data transfer interface descriptor */
221
+static struct interface_descriptor http_conn_xfer_desc =
222
+	INTF_DESC_PASSTHRU ( struct http_connection, xfer,
223
+			     http_conn_xfer_operations, socket );
224
+
225
+/**
226
+ * Connect to an HTTP server
227
+ *
228
+ * @v xfer		Data transfer interface
229
+ * @v uri		Connection URI
230
+ * @ret rc		Return status code
231
+ *
232
+ * HTTP connections are pooled.  The caller should be prepared to
233
+ * receive a pool_reopen() message.
234
+ */
235
+int http_connect ( struct interface *xfer, struct uri *uri ) {
236
+	struct http_connection *conn;
237
+	struct http_scheme *scheme;
238
+	struct sockaddr_tcpip server;
239
+	struct interface *socket;
240
+	int rc;
241
+
242
+	/* Identify scheme */
243
+	scheme = http_scheme ( uri );
244
+	if ( ! scheme )
245
+		return -ENOTSUP;
246
+
247
+	/* Sanity check */
248
+	if ( ! uri->host )
249
+		return -EINVAL;
250
+
251
+	/* Look for a reusable connection in the pool */
252
+	list_for_each_entry ( conn, &http_connection_pool, pool.list ) {
253
+
254
+		/* Sanity checks */
255
+		assert ( conn->uri != NULL );
256
+		assert ( conn->uri->host != NULL );
257
+
258
+		/* Reuse connection, if possible */
259
+		if ( ( scheme == conn->scheme ) &&
260
+		     ( strcmp ( uri->host, conn->uri->host ) == 0 ) ) {
261
+
262
+			/* Remove from connection pool, stop timer,
263
+			 * attach to parent interface, and return.
264
+			 */
265
+			pool_del ( &conn->pool );
266
+			intf_plug_plug ( &conn->xfer, xfer );
267
+			DBGC2 ( conn, "HTTPCONN %p reused %s://%s\n",
268
+				conn, conn->scheme->name, conn->uri->host );
269
+			return 0;
270
+		}
271
+	}
272
+
273
+	/* Allocate and initialise structure */
274
+	conn = zalloc ( sizeof ( *conn ) );
275
+	ref_init ( &conn->refcnt, http_conn_free );
276
+	conn->uri = uri_get ( uri );
277
+	conn->scheme = scheme;
278
+	intf_init ( &conn->socket, &http_conn_socket_desc, &conn->refcnt );
279
+	intf_init ( &conn->xfer, &http_conn_xfer_desc, &conn->refcnt );
280
+	pool_init ( &conn->pool, http_conn_expired, &conn->refcnt );
281
+
282
+	/* Open socket */
283
+	memset ( &server, 0, sizeof ( server ) );
284
+	server.st_port = htons ( uri_port ( uri, scheme->port ) );
285
+	socket = &conn->socket;
286
+	if ( scheme->filter &&
287
+	     ( ( rc = scheme->filter ( socket, uri->host, &socket ) ) != 0 ) )
288
+		goto err_filter;
289
+	if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM,
290
+					     ( struct sockaddr * ) &server,
291
+					     uri->host, NULL ) ) != 0 )
292
+		goto err_open;
293
+
294
+	/* Attach to parent interface, mortalise self, and return */
295
+	intf_plug_plug ( &conn->xfer, xfer );
296
+	ref_put ( &conn->refcnt );
297
+
298
+	DBGC2 ( conn, "HTTPCONN %p created %s://%s:%d\n", conn,
299
+		conn->scheme->name, conn->uri->host, ntohs ( server.st_port ) );
300
+	return 0;
301
+
302
+ err_open:
303
+ err_filter:
304
+	DBGC2 ( conn, "HTTPCONN %p could not create %s://%s: %s\n",
305
+		conn, conn->scheme->name, conn->uri->host, strerror ( rc ) );
306
+	http_conn_close ( conn, rc );
307
+	ref_put ( &conn->refcnt );
308
+	return rc;
309
+}

+ 1503
- 1222
src/net/tcp/httpcore.c
File diff suppressed because it is too large
View File


+ 234
- 0
src/net/tcp/httpdigest.c View File

@@ -0,0 +1,234 @@
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 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
+/**
27
+ * @file
28
+ *
29
+ * Hyper Text Transfer Protocol (HTTP) Digest authentication
30
+ *
31
+ */
32
+
33
+#include <stdio.h>
34
+#include <errno.h>
35
+#include <strings.h>
36
+#include <ipxe/uri.h>
37
+#include <ipxe/md5.h>
38
+#include <ipxe/base16.h>
39
+#include <ipxe/vsprintf.h>
40
+#include <ipxe/http.h>
41
+
42
+/* Disambiguate the various error causes */
43
+#define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME )
44
+#define EINFO_EACCES_USERNAME						\
45
+	__einfo_uniqify ( EINFO_EACCES, 0x01,				\
46
+			  "No username available for Digest authentication" )
47
+
48
+/**
49
+ * Initialise HTTP Digest
50
+ *
51
+ * @v ctx		Digest context
52
+ * @v string		Initial string
53
+ */
54
+static void http_digest_init ( struct md5_context *ctx ) {
55
+
56
+	/* Initialise MD5 digest */
57
+	digest_init ( &md5_algorithm, ctx );
58
+}
59
+
60
+/**
61
+ * Update HTTP Digest with new data
62
+ *
63
+ * @v ctx		Digest context
64
+ * @v string		String to append
65
+ */
66
+static void http_digest_update ( struct md5_context *ctx, const char *string ) {
67
+	static const char colon = ':';
68
+
69
+	/* Add (possibly colon-separated) field to MD5 digest */
70
+	if ( ctx->len )
71
+		digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) );
72
+	digest_update ( &md5_algorithm, ctx, string, strlen ( string ) );
73
+}
74
+
75
+/**
76
+ * Finalise HTTP Digest
77
+ *
78
+ * @v ctx		Digest context
79
+ * @v out		Buffer for digest output
80
+ * @v len		Buffer length
81
+ */
82
+static void http_digest_final ( struct md5_context *ctx, char *out,
83
+				size_t len ) {
84
+	uint8_t digest[MD5_DIGEST_SIZE];
85
+
86
+	/* Finalise and base16-encode MD5 digest */
87
+	digest_final ( &md5_algorithm, ctx, digest );
88
+	base16_encode ( digest, sizeof ( digest ), out, len );
89
+}
90
+
91
+/**
92
+ * Perform HTTP Digest authentication
93
+ *
94
+ * @v http		HTTP transaction
95
+ * @ret rc		Return status code
96
+ */
97
+static int http_digest_authenticate ( struct http_transaction *http ) {
98
+	struct http_request_auth *req = &http->request.auth;
99
+	struct http_response_auth *rsp = &http->response.auth;
100
+	char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ];
101
+	char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ];
102
+	static const char md5sess[] = "MD5-sess";
103
+	static const char md5[] = "MD5";
104
+	struct md5_context ctx;
105
+
106
+	/* Check for required response parameters */
107
+	if ( ! rsp->realm ) {
108
+		DBGC ( http, "HTTP %p has no realm for Digest authentication\n",
109
+		       http );
110
+		return -EINVAL;
111
+	}
112
+	if ( ! rsp->nonce ) {
113
+		DBGC ( http, "HTTP %p has no nonce for Digest authentication\n",
114
+		       http );
115
+		return -EINVAL;
116
+	}
117
+
118
+	/* Record username and password */
119
+	if ( ! http->uri->user ) {
120
+		DBGC ( http, "HTTP %p has no username for Digest "
121
+		       "authentication\n", http );
122
+		return -EACCES_USERNAME;
123
+	}
124
+	req->username = http->uri->user;
125
+	req->password = ( http->uri->password ? http->uri->password : "" );
126
+
127
+	/* Handle quality of protection */
128
+	if ( rsp->qop ) {
129
+
130
+		/* Use "auth" in subsequent request */
131
+		req->qop = "auth";
132
+
133
+		/* Generate a client nonce */
134
+		snprintf ( req->cnonce, sizeof ( req->cnonce ),
135
+			   "%08lx", random() );
136
+
137
+		/* Determine algorithm */
138
+		req->algorithm = md5;
139
+		if ( rsp->algorithm &&
140
+		     ( strcasecmp ( rsp->algorithm, md5sess ) == 0 ) ) {
141
+			req->algorithm = md5sess;
142
+		}
143
+	}
144
+
145
+	/* Generate HA1 */
146
+	http_digest_init ( &ctx );
147
+	http_digest_update ( &ctx, req->username );
148
+	http_digest_update ( &ctx, rsp->realm );
149
+	http_digest_update ( &ctx, req->password );
150
+	http_digest_final ( &ctx, ha1, sizeof ( ha1 ) );
151
+	if ( req->algorithm == md5sess ) {
152
+		http_digest_init ( &ctx );
153
+		http_digest_update ( &ctx, ha1 );
154
+		http_digest_update ( &ctx, rsp->nonce );
155
+		http_digest_update ( &ctx, req->cnonce );
156
+		http_digest_final ( &ctx, ha1, sizeof ( ha1 ) );
157
+	}
158
+
159
+	/* Generate HA2 */
160
+	http_digest_init ( &ctx );
161
+	http_digest_update ( &ctx, http->request.method->name );
162
+	http_digest_update ( &ctx, http->request.uri );
163
+	http_digest_final ( &ctx, ha2, sizeof ( ha2 ) );
164
+
165
+	/* Generate response */
166
+	http_digest_init ( &ctx );
167
+	http_digest_update ( &ctx, ha1 );
168
+	http_digest_update ( &ctx, rsp->nonce );
169
+	if ( req->qop ) {
170
+		http_digest_update ( &ctx, HTTP_DIGEST_NC );
171
+		http_digest_update ( &ctx, req->cnonce );
172
+		http_digest_update ( &ctx, req->qop );
173
+	}
174
+	http_digest_update ( &ctx, ha2 );
175
+	http_digest_final ( &ctx, req->response, sizeof ( req->response ) );
176
+
177
+	return 0;
178
+}
179
+
180
+/**
181
+ * Construct HTTP "Authorization" header for Digest authentication
182
+ *
183
+ * @v http		HTTP transaction
184
+ * @v buf		Buffer
185
+ * @v len		Length of buffer
186
+ * @ret len		Length of header value, or negative error
187
+ */
188
+static int http_format_digest_auth ( struct http_transaction *http,
189
+				     char *buf, size_t len ) {
190
+	struct http_request_auth *req = &http->request.auth;
191
+	struct http_response_auth *rsp = &http->response.auth;
192
+	size_t used = 0;
193
+
194
+	/* Sanity checks */
195
+	assert ( rsp->realm != NULL );
196
+	assert ( rsp->nonce != NULL );
197
+	assert ( req->username != NULL );
198
+	if ( req->qop ) {
199
+		assert ( req->algorithm != NULL );
200
+		assert ( req->cnonce[0] != '\0' );
201
+	}
202
+	assert ( req->response[0] != '\0' );
203
+
204
+	/* Construct response */
205
+	used += ssnprintf ( ( buf + used ), ( len - used ),
206
+			    "realm=\"%s\", nonce=\"%s\", uri=\"%s\", "
207
+			    "username=\"%s\"", rsp->realm, rsp->nonce,
208
+			    http->request.uri, req->username );
209
+	if ( rsp->opaque ) {
210
+		used += ssnprintf ( ( buf + used ), ( len - used ),
211
+				    ", opaque=\"%s\"", rsp->opaque );
212
+	}
213
+	if ( req->qop ) {
214
+		used += ssnprintf ( ( buf + used ), ( len - used ),
215
+				    ", qop=%s, algorithm=%s, cnonce=\"%s\", "
216
+				    "nc=" HTTP_DIGEST_NC, req->qop,
217
+				    req->algorithm, req->cnonce );
218
+	}
219
+	used += ssnprintf ( ( buf + used ), ( len - used ),
220
+			    ", response=\"%s\"", req->response );
221
+
222
+	return used;
223
+}
224
+
225
+/** HTTP Digest authentication scheme */
226
+struct http_authentication http_digest_auth __http_authentication = {
227
+	.name = "Digest",
228
+	.authenticate = http_digest_authenticate,
229
+	.format = http_format_digest_auth,
230
+};
231
+
232
+/* Drag in HTTP authentication support */
233
+REQUIRING_SYMBOL ( http_digest_auth );
234
+REQUIRE_OBJECT ( httpauth );

+ 8
- 13
src/net/tcp/https.c View File

@@ -30,7 +30,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
30 30
  *
31 31
  */
32 32
 
33
-#include <stddef.h>
34 33
 #include <ipxe/open.h>
35 34
 #include <ipxe/tls.h>
36 35
 #include <ipxe/http.h>
@@ -38,19 +37,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
38 37
 
39 38
 FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 );
40 39
 
41
-/**
42
- * Initiate an HTTPS connection
43
- *
44
- * @v xfer		Data transfer interface
45
- * @v uri		Uniform Resource Identifier
46
- * @ret rc		Return status code
47
- */
48
-static int https_open ( struct interface *xfer, struct uri *uri ) {
49
-	return http_open_filter ( xfer, uri, HTTPS_PORT, add_tls );
50
-}
51
-
52 40
 /** HTTPS URI opener */
53 41
 struct uri_opener https_uri_opener __uri_opener = {
54 42
 	.scheme	= "https",
55
-	.open	= https_open,
43
+	.open	= http_open_uri,
44
+};
45
+
46
+/** HTTP URI scheme */
47
+struct http_scheme https_scheme __http_scheme = {
48
+	.name = "https",
49
+	.port = HTTPS_PORT,
50
+	.filter = add_tls,
56 51
 };

Loading…
Cancel
Save