From patchwork Tue Jul 2 07:19:21 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fam Zheng X-Patchwork-Id: 256294 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 380A02C02A1 for ; Tue, 2 Jul 2013 17:19:51 +1000 (EST) Received: from localhost ([::1]:48306 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UtusO-00086k-K8 for incoming@patchwork.ozlabs.org; Tue, 02 Jul 2013 03:19:48 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:58437) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Utus7-00086a-9P for qemu-devel@nongnu.org; Tue, 02 Jul 2013 03:19:35 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Utus3-0000HZ-MQ for qemu-devel@nongnu.org; Tue, 02 Jul 2013 03:19:31 -0400 Received: from mx1.redhat.com ([209.132.183.28]:1651) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Utus3-0000HP-FX for qemu-devel@nongnu.org; Tue, 02 Jul 2013 03:19:27 -0400 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r627JPJM014627 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Tue, 2 Jul 2013 03:19:26 -0400 Received: from t430s.redhat.com ([10.66.6.13]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id r627JM0I014136; Tue, 2 Jul 2013 03:19:23 -0400 From: Fam Zheng To: qemu-devel@nongnu.org Date: Tue, 2 Jul 2013 15:19:21 +0800 Message-Id: <1372749561-11117-1-git-send-email-famz@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: Kevin Wolf , Fam Zheng , rjones@redhat.com, stefanha@redhat.com Subject: [Qemu-devel] [PATCH v3] curl: refuse to open URL from HTTP server without range support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org CURL driver requests partial data from server on guest IO req. For HTTP and HTTPS, it uses "Range: ***" in requests, and this will not work if server not accepting range. This patch does this check when open. * Removed curl_size_cb, which is not used: On one hand it's registered to libcurl as CURLOPT_WRITEFUNCTION, instead of CURLOPT_HEADERFUNCTION, which will get called with *data*, not *header*. On the other hand the s->len is assigned unconditionally later. In this gone function, the sscanf for "Content-Length: %zd", on (void *)ptr, which is not guaranteed to be zero-terminated, is potentially a security bug. So this patch fixes it as a side-effect. The bug is reported as: https://bugs.launchpad.net/qemu/+bug/1188943 (Note the bug is marked "private" so you might not be able to see it) * Introduced curl_header_cb, which is used to parse header and mark the server as accepting range if "Accept-Ranges: bytes" line is seen from response header. If protocol is HTTP or HTTPS, but server response has no not this support, refuse to open this URL. Note that python builtin module SimpleHTTPServer is an example of not supporting range, if you need to test this driver, get a better server or use internet URLs. Signed-off-by: Fam Zheng --- v3: Fix copy&paste error. Improve error text as suggested by Rich. --- block/curl.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/block/curl.c b/block/curl.c index b8935fd..5f75fd9 100644 --- a/block/curl.c +++ b/block/curl.c @@ -81,6 +81,7 @@ typedef struct BDRVCURLState { CURLState states[CURL_NUM_STATES]; char *url; size_t readahead_size; + bool accept_range; } BDRVCURLState; static void curl_clean_state(CURLState *s); @@ -110,14 +111,15 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, return 0; } -static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque) +static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque) { - CURLState *s = ((CURLState*)opaque); + BDRVCURLState *s = opaque; size_t realsize = size * nmemb; - size_t fsize; + const char *accept_line = "Accept-Ranges: bytes"; - if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) { - s->s->len = fsize; + if (realsize >= strlen(accept_line) + && strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) { + s->accept_range = true; } return realsize; @@ -441,8 +443,11 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags) // Get file size + s->accept_range = false; curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1); - curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb); + curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, + curl_header_cb); + curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s); if (curl_easy_perform(state->curl)) goto out; curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d); @@ -452,6 +457,13 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags) s->len = (size_t)d; else if(!s->len) goto out; + if ((!strncasecmp(s->url, "http://", strlen("http://")) + || !strncasecmp(s->url, "https://", strlen("https://"))) + && !s->accept_range) { + pstrcpy(state->errmsg, CURL_ERROR_SIZE, + "Server does not support 'range' (byte ranges)."); + goto out; + } DPRINTF("CURL: Size = %zd\n", s->len); curl_clean_state(state);