|
|
1.1 ! root 1: /* ! 2: * Copyright (C) 2007 Michael Brown <[email protected]>. ! 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., 675 Mass Ave, Cambridge, MA 02139, USA. ! 17: */ ! 18: ! 19: FILE_LICENCE ( GPL2_OR_LATER ); ! 20: ! 21: /** ! 22: * @file ! 23: * ! 24: * Hyper Text Transfer Protocol (HTTP) ! 25: * ! 26: */ ! 27: ! 28: #include <stdint.h> ! 29: #include <stdlib.h> ! 30: #include <stdio.h> ! 31: #include <string.h> ! 32: #include <strings.h> ! 33: #include <byteswap.h> ! 34: #include <errno.h> ! 35: #include <assert.h> ! 36: #include <ipxe/uri.h> ! 37: #include <ipxe/refcnt.h> ! 38: #include <ipxe/iobuf.h> ! 39: #include <ipxe/xfer.h> ! 40: #include <ipxe/open.h> ! 41: #include <ipxe/socket.h> ! 42: #include <ipxe/tcpip.h> ! 43: #include <ipxe/process.h> ! 44: #include <ipxe/linebuf.h> ! 45: #include <ipxe/features.h> ! 46: #include <ipxe/base64.h> ! 47: #include <ipxe/http.h> ! 48: ! 49: FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 ); ! 50: ! 51: /** HTTP receive state */ ! 52: enum http_rx_state { ! 53: HTTP_RX_RESPONSE = 0, ! 54: HTTP_RX_HEADER, ! 55: HTTP_RX_DATA, ! 56: HTTP_RX_DEAD, ! 57: }; ! 58: ! 59: /** ! 60: * An HTTP request ! 61: * ! 62: */ ! 63: struct http_request { ! 64: /** Reference count */ ! 65: struct refcnt refcnt; ! 66: /** Data transfer interface */ ! 67: struct interface xfer; ! 68: ! 69: /** URI being fetched */ ! 70: struct uri *uri; ! 71: /** Transport layer interface */ ! 72: struct interface socket; ! 73: ! 74: /** TX process */ ! 75: struct process process; ! 76: ! 77: /** HTTP response code */ ! 78: unsigned int response; ! 79: /** HTTP Content-Length */ ! 80: size_t content_length; ! 81: /** Received length */ ! 82: size_t rx_len; ! 83: /** RX state */ ! 84: enum http_rx_state rx_state; ! 85: /** Line buffer for received header lines */ ! 86: struct line_buffer linebuf; ! 87: }; ! 88: ! 89: /** ! 90: * Free HTTP request ! 91: * ! 92: * @v refcnt Reference counter ! 93: */ ! 94: static void http_free ( struct refcnt *refcnt ) { ! 95: struct http_request *http = ! 96: container_of ( refcnt, struct http_request, refcnt ); ! 97: ! 98: uri_put ( http->uri ); ! 99: empty_line_buffer ( &http->linebuf ); ! 100: free ( http ); ! 101: }; ! 102: ! 103: /** ! 104: * Mark HTTP request as complete ! 105: * ! 106: * @v http HTTP request ! 107: * @v rc Return status code ! 108: */ ! 109: static void http_done ( struct http_request *http, int rc ) { ! 110: ! 111: /* Prevent further processing of any current packet */ ! 112: http->rx_state = HTTP_RX_DEAD; ! 113: ! 114: /* If we had a Content-Length, and the received content length ! 115: * isn't correct, flag an error ! 116: */ ! 117: if ( http->content_length && ! 118: ( http->content_length != http->rx_len ) ) { ! 119: DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n", ! 120: http, http->rx_len, http->content_length ); ! 121: rc = -EIO; ! 122: } ! 123: ! 124: /* Remove process */ ! 125: process_del ( &http->process ); ! 126: ! 127: /* Close all data transfer interfaces */ ! 128: intf_shutdown ( &http->socket, rc ); ! 129: intf_shutdown ( &http->xfer, rc ); ! 130: } ! 131: ! 132: /** ! 133: * Convert HTTP response code to return status code ! 134: * ! 135: * @v response HTTP response code ! 136: * @ret rc Return status code ! 137: */ ! 138: static int http_response_to_rc ( unsigned int response ) { ! 139: switch ( response ) { ! 140: case 200: ! 141: case 301: ! 142: case 302: ! 143: return 0; ! 144: case 404: ! 145: return -ENOENT; ! 146: case 403: ! 147: return -EPERM; ! 148: case 401: ! 149: return -EACCES; ! 150: default: ! 151: return -EIO; ! 152: } ! 153: } ! 154: ! 155: /** ! 156: * Handle HTTP response ! 157: * ! 158: * @v http HTTP request ! 159: * @v response HTTP response ! 160: * @ret rc Return status code ! 161: */ ! 162: static int http_rx_response ( struct http_request *http, char *response ) { ! 163: char *spc; ! 164: int rc; ! 165: ! 166: DBGC ( http, "HTTP %p response \"%s\"\n", http, response ); ! 167: ! 168: /* Check response starts with "HTTP/" */ ! 169: if ( strncmp ( response, "HTTP/", 5 ) != 0 ) ! 170: return -EIO; ! 171: ! 172: /* Locate and check response code */ ! 173: spc = strchr ( response, ' ' ); ! 174: if ( ! spc ) ! 175: return -EIO; ! 176: http->response = strtoul ( spc, NULL, 10 ); ! 177: if ( ( rc = http_response_to_rc ( http->response ) ) != 0 ) ! 178: return rc; ! 179: ! 180: /* Move to received headers */ ! 181: http->rx_state = HTTP_RX_HEADER; ! 182: return 0; ! 183: } ! 184: ! 185: /** ! 186: * Handle HTTP Location header ! 187: * ! 188: * @v http HTTP request ! 189: * @v value HTTP header value ! 190: * @ret rc Return status code ! 191: */ ! 192: static int http_rx_location ( struct http_request *http, const char *value ) { ! 193: int rc; ! 194: ! 195: /* Redirect to new location */ ! 196: DBGC ( http, "HTTP %p redirecting to %s\n", http, value ); ! 197: if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING, ! 198: value ) ) != 0 ) { ! 199: DBGC ( http, "HTTP %p could not redirect: %s\n", ! 200: http, strerror ( rc ) ); ! 201: return rc; ! 202: } ! 203: ! 204: return 0; ! 205: } ! 206: ! 207: /** ! 208: * Handle HTTP Content-Length header ! 209: * ! 210: * @v http HTTP request ! 211: * @v value HTTP header value ! 212: * @ret rc Return status code ! 213: */ ! 214: static int http_rx_content_length ( struct http_request *http, ! 215: const char *value ) { ! 216: char *endp; ! 217: ! 218: http->content_length = strtoul ( value, &endp, 10 ); ! 219: if ( *endp != '\0' ) { ! 220: DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n", ! 221: http, value ); ! 222: return -EIO; ! 223: } ! 224: ! 225: /* Use seek() to notify recipient of filesize */ ! 226: xfer_seek ( &http->xfer, http->content_length ); ! 227: xfer_seek ( &http->xfer, 0 ); ! 228: ! 229: return 0; ! 230: } ! 231: ! 232: /** An HTTP header handler */ ! 233: struct http_header_handler { ! 234: /** Name (e.g. "Content-Length") */ ! 235: const char *header; ! 236: /** Handle received header ! 237: * ! 238: * @v http HTTP request ! 239: * @v value HTTP header value ! 240: * @ret rc Return status code ! 241: * ! 242: * If an error is returned, the download will be aborted. ! 243: */ ! 244: int ( * rx ) ( struct http_request *http, const char *value ); ! 245: }; ! 246: ! 247: /** List of HTTP header handlers */ ! 248: static struct http_header_handler http_header_handlers[] = { ! 249: { ! 250: .header = "Location", ! 251: .rx = http_rx_location, ! 252: }, ! 253: { ! 254: .header = "Content-Length", ! 255: .rx = http_rx_content_length, ! 256: }, ! 257: { NULL, NULL } ! 258: }; ! 259: ! 260: /** ! 261: * Handle HTTP header ! 262: * ! 263: * @v http HTTP request ! 264: * @v header HTTP header ! 265: * @ret rc Return status code ! 266: */ ! 267: static int http_rx_header ( struct http_request *http, char *header ) { ! 268: struct http_header_handler *handler; ! 269: char *separator; ! 270: char *value; ! 271: int rc; ! 272: ! 273: /* An empty header line marks the transition to the data phase */ ! 274: if ( ! header[0] ) { ! 275: DBGC ( http, "HTTP %p start of data\n", http ); ! 276: empty_line_buffer ( &http->linebuf ); ! 277: http->rx_state = HTTP_RX_DATA; ! 278: return 0; ! 279: } ! 280: ! 281: DBGC ( http, "HTTP %p header \"%s\"\n", http, header ); ! 282: ! 283: /* Split header at the ": " */ ! 284: separator = strstr ( header, ": " ); ! 285: if ( ! separator ) { ! 286: DBGC ( http, "HTTP %p malformed header\n", http ); ! 287: return -EIO; ! 288: } ! 289: *separator = '\0'; ! 290: value = ( separator + 2 ); ! 291: ! 292: /* Hand off to header handler, if one exists */ ! 293: for ( handler = http_header_handlers ; handler->header ; handler++ ) { ! 294: if ( strcasecmp ( header, handler->header ) == 0 ) { ! 295: if ( ( rc = handler->rx ( http, value ) ) != 0 ) ! 296: return rc; ! 297: break; ! 298: } ! 299: } ! 300: return 0; ! 301: } ! 302: ! 303: /** An HTTP line-based data handler */ ! 304: struct http_line_handler { ! 305: /** Handle line ! 306: * ! 307: * @v http HTTP request ! 308: * @v line Line to handle ! 309: * @ret rc Return status code ! 310: */ ! 311: int ( * rx ) ( struct http_request *http, char *line ); ! 312: }; ! 313: ! 314: /** List of HTTP line-based data handlers */ ! 315: static struct http_line_handler http_line_handlers[] = { ! 316: [HTTP_RX_RESPONSE] = { .rx = http_rx_response }, ! 317: [HTTP_RX_HEADER] = { .rx = http_rx_header }, ! 318: }; ! 319: ! 320: /** ! 321: * Handle new data arriving via HTTP connection in the data phase ! 322: * ! 323: * @v http HTTP request ! 324: * @v iobuf I/O buffer ! 325: * @ret rc Return status code ! 326: */ ! 327: static int http_rx_data ( struct http_request *http, ! 328: struct io_buffer *iobuf ) { ! 329: int rc; ! 330: ! 331: /* Update received length */ ! 332: http->rx_len += iob_len ( iobuf ); ! 333: ! 334: /* Hand off data buffer */ ! 335: if ( ( rc = xfer_deliver_iob ( &http->xfer, iobuf ) ) != 0 ) ! 336: return rc; ! 337: ! 338: /* If we have reached the content-length, stop now */ ! 339: if ( http->content_length && ! 340: ( http->rx_len >= http->content_length ) ) { ! 341: http_done ( http, 0 ); ! 342: } ! 343: ! 344: return 0; ! 345: } ! 346: ! 347: /** ! 348: * Handle new data arriving via HTTP connection ! 349: * ! 350: * @v http HTTP request ! 351: * @v iobuf I/O buffer ! 352: * @v meta Data transfer metadata ! 353: * @ret rc Return status code ! 354: */ ! 355: static int http_socket_deliver ( struct http_request *http, ! 356: struct io_buffer *iobuf, ! 357: struct xfer_metadata *meta __unused ) { ! 358: struct http_line_handler *lh; ! 359: char *line; ! 360: ssize_t len; ! 361: int rc = 0; ! 362: ! 363: while ( iob_len ( iobuf ) ) { ! 364: switch ( http->rx_state ) { ! 365: case HTTP_RX_DEAD: ! 366: /* Do no further processing */ ! 367: goto done; ! 368: case HTTP_RX_DATA: ! 369: /* Once we're into the data phase, just fill ! 370: * the data buffer ! 371: */ ! 372: rc = http_rx_data ( http, iob_disown ( iobuf ) ); ! 373: goto done; ! 374: case HTTP_RX_RESPONSE: ! 375: case HTTP_RX_HEADER: ! 376: /* In the other phases, buffer and process a ! 377: * line at a time ! 378: */ ! 379: len = line_buffer ( &http->linebuf, iobuf->data, ! 380: iob_len ( iobuf ) ); ! 381: if ( len < 0 ) { ! 382: rc = len; ! 383: DBGC ( http, "HTTP %p could not buffer line: " ! 384: "%s\n", http, strerror ( rc ) ); ! 385: goto done; ! 386: } ! 387: iob_pull ( iobuf, len ); ! 388: line = buffered_line ( &http->linebuf ); ! 389: if ( line ) { ! 390: lh = &http_line_handlers[http->rx_state]; ! 391: if ( ( rc = lh->rx ( http, line ) ) != 0 ) ! 392: goto done; ! 393: } ! 394: break; ! 395: default: ! 396: assert ( 0 ); ! 397: break; ! 398: } ! 399: } ! 400: ! 401: done: ! 402: if ( rc ) ! 403: http_done ( http, rc ); ! 404: free_iob ( iobuf ); ! 405: return rc; ! 406: } ! 407: ! 408: /** ! 409: * HTTP process ! 410: * ! 411: * @v process Process ! 412: */ ! 413: static void http_step ( struct process *process ) { ! 414: struct http_request *http = ! 415: container_of ( process, struct http_request, process ); ! 416: const char *host = http->uri->host; ! 417: const char *user = http->uri->user; ! 418: const char *password = ! 419: ( http->uri->password ? http->uri->password : "" ); ! 420: size_t user_pw_len = ( user ? ( strlen ( user ) + 1 /* ":" */ + ! 421: strlen ( password ) ) : 0 ); ! 422: size_t user_pw_base64_len = base64_encoded_len ( user_pw_len ); ! 423: uint8_t user_pw[ user_pw_len + 1 /* NUL */ ]; ! 424: char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ]; ! 425: int rc; ! 426: int request_len = unparse_uri ( NULL, 0, http->uri, ! 427: URI_PATH_BIT | URI_QUERY_BIT ); ! 428: ! 429: if ( xfer_window ( &http->socket ) ) { ! 430: char request[request_len + 1]; ! 431: ! 432: /* Construct path?query request */ ! 433: unparse_uri ( request, sizeof ( request ), http->uri, ! 434: URI_PATH_BIT | URI_QUERY_BIT ); ! 435: ! 436: /* We want to execute only once */ ! 437: process_del ( &http->process ); ! 438: ! 439: /* Construct authorisation, if applicable */ ! 440: if ( user ) { ! 441: /* Make "user:password" string from decoded fields */ ! 442: snprintf ( ( ( char * ) user_pw ), sizeof ( user_pw ), ! 443: "%s:%s", user, password ); ! 444: ! 445: /* Base64-encode the "user:password" string */ ! 446: base64_encode ( user_pw, user_pw_len, user_pw_base64 ); ! 447: } ! 448: ! 449: /* Send GET request */ ! 450: if ( ( rc = xfer_printf ( &http->socket, ! 451: "GET %s%s HTTP/1.0\r\n" ! 452: "User-Agent: iPXE/" VERSION "\r\n" ! 453: "%s%s%s" ! 454: "Host: %s\r\n" ! 455: "\r\n", ! 456: http->uri->path ? "" : "/", ! 457: request, ! 458: ( user ? ! 459: "Authorization: Basic " : "" ), ! 460: ( user ? user_pw_base64 : "" ), ! 461: ( user ? "\r\n" : "" ), ! 462: host ) ) != 0 ) { ! 463: http_done ( http, rc ); ! 464: } ! 465: } ! 466: } ! 467: ! 468: /** HTTP socket interface operations */ ! 469: static struct interface_operation http_socket_operations[] = { ! 470: INTF_OP ( xfer_deliver, struct http_request *, http_socket_deliver ), ! 471: INTF_OP ( intf_close, struct http_request *, http_done ), ! 472: }; ! 473: ! 474: /** HTTP socket interface descriptor */ ! 475: static struct interface_descriptor http_socket_desc = ! 476: INTF_DESC_PASSTHRU ( struct http_request, socket, ! 477: http_socket_operations, xfer ); ! 478: ! 479: /** HTTP data transfer interface operations */ ! 480: static struct interface_operation http_xfer_operations[] = { ! 481: INTF_OP ( intf_close, struct http_request *, http_done ), ! 482: }; ! 483: ! 484: /** HTTP data transfer interface descriptor */ ! 485: static struct interface_descriptor http_xfer_desc = ! 486: INTF_DESC_PASSTHRU ( struct http_request, xfer, ! 487: http_xfer_operations, socket ); ! 488: ! 489: /** ! 490: * Initiate an HTTP connection, with optional filter ! 491: * ! 492: * @v xfer Data transfer interface ! 493: * @v uri Uniform Resource Identifier ! 494: * @v default_port Default port number ! 495: * @v filter Filter to apply to socket, or NULL ! 496: * @ret rc Return status code ! 497: */ ! 498: int http_open_filter ( struct interface *xfer, struct uri *uri, ! 499: unsigned int default_port, ! 500: int ( * filter ) ( struct interface *xfer, ! 501: struct interface **next ) ) { ! 502: struct http_request *http; ! 503: struct sockaddr_tcpip server; ! 504: struct interface *socket; ! 505: int rc; ! 506: ! 507: /* Sanity checks */ ! 508: if ( ! uri->host ) ! 509: return -EINVAL; ! 510: ! 511: /* Allocate and populate HTTP structure */ ! 512: http = zalloc ( sizeof ( *http ) ); ! 513: if ( ! http ) ! 514: return -ENOMEM; ! 515: ref_init ( &http->refcnt, http_free ); ! 516: intf_init ( &http->xfer, &http_xfer_desc, &http->refcnt ); ! 517: http->uri = uri_get ( uri ); ! 518: intf_init ( &http->socket, &http_socket_desc, &http->refcnt ); ! 519: process_init ( &http->process, http_step, &http->refcnt ); ! 520: ! 521: /* Open socket */ ! 522: memset ( &server, 0, sizeof ( server ) ); ! 523: server.st_port = htons ( uri_port ( http->uri, default_port ) ); ! 524: socket = &http->socket; ! 525: if ( filter ) { ! 526: if ( ( rc = filter ( socket, &socket ) ) != 0 ) ! 527: goto err; ! 528: } ! 529: if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, ! 530: ( struct sockaddr * ) &server, ! 531: uri->host, NULL ) ) != 0 ) ! 532: goto err; ! 533: ! 534: /* Attach to parent interface, mortalise self, and return */ ! 535: intf_plug_plug ( &http->xfer, xfer ); ! 536: ref_put ( &http->refcnt ); ! 537: return 0; ! 538: ! 539: err: ! 540: DBGC ( http, "HTTP %p could not create request: %s\n", ! 541: http, strerror ( rc ) ); ! 542: http_done ( http, rc ); ! 543: ref_put ( &http->refcnt ); ! 544: return rc; ! 545: } ! 546: ! 547: /** ! 548: * Initiate an HTTP connection ! 549: * ! 550: * @v xfer Data transfer interface ! 551: * @v uri Uniform Resource Identifier ! 552: * @ret rc Return status code ! 553: */ ! 554: static int http_open ( struct interface *xfer, struct uri *uri ) { ! 555: return http_open_filter ( xfer, uri, HTTP_PORT, NULL ); ! 556: } ! 557: ! 558: /** HTTP URI opener */ ! 559: struct uri_opener http_uri_opener __uri_opener = { ! 560: .scheme = "http", ! 561: .open = http_open, ! 562: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.