Annotation of qemu/roms/ipxe/src/net/tcp/http.c, revision 1.1

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: };

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.