byterange_filter.c.cve3192ver3 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /* Licensed to the Apache Software Foundation (ASF) under one or more
  2. * contributor license agreements. See the NOTICE file distributed with
  3. * this work for additional information regarding copyright ownership.
  4. * The ASF licenses this file to You under the Apache License, Version 2.0
  5. * (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * byterange_filter.c --- HTTP byterange filter and friends.
  18. */
  19. #include "apr.h"
  20. #include "apr_strings.h"
  21. #include "apr_buckets.h"
  22. #include "apr_lib.h"
  23. #include "apr_signal.h"
  24. #define APR_WANT_STDIO /* for sscanf */
  25. #define APR_WANT_STRFUNC
  26. #define APR_WANT_MEMFUNC
  27. #include "apr_want.h"
  28. #define CORE_PRIVATE
  29. #include "util_filter.h"
  30. #include "ap_config.h"
  31. #include "httpd.h"
  32. #include "http_config.h"
  33. #include "http_core.h"
  34. #include "http_protocol.h"
  35. #include "http_main.h"
  36. #include "http_request.h"
  37. #include "http_vhost.h"
  38. #include "http_log.h" /* For errors detected in basic auth common
  39. * support code... */
  40. #include "apr_date.h" /* For apr_date_parse_http and APR_DATE_BAD */
  41. #include "util_charset.h"
  42. #include "util_ebcdic.h"
  43. #include "util_time.h"
  44. #include "mod_core.h"
  45. #if APR_HAVE_STDARG_H
  46. #include <stdarg.h>
  47. #endif
  48. #if APR_HAVE_UNISTD_H
  49. #include <unistd.h>
  50. #endif
  51. static int parse_byterange(char *range, apr_off_t clength,
  52. apr_off_t *start, apr_off_t *end)
  53. {
  54. char *dash = strchr(range, '-');
  55. char *errp;
  56. apr_off_t number;
  57. if (!dash) {
  58. return 0;
  59. }
  60. if ((dash == range)) {
  61. /* In the form "-5" */
  62. if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
  63. return 0;
  64. }
  65. *start = clength - number;
  66. *end = clength - 1;
  67. }
  68. else {
  69. *dash++ = '\0';
  70. if (apr_strtoff(&number, range, &errp, 10) || *errp) {
  71. return 0;
  72. }
  73. *start = number;
  74. if (*dash) {
  75. if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
  76. return 0;
  77. }
  78. *end = number;
  79. }
  80. else { /* "5-" */
  81. *end = clength - 1;
  82. }
  83. }
  84. if (*start < 0) {
  85. *start = 0;
  86. }
  87. if (*end >= clength) {
  88. *end = clength - 1;
  89. }
  90. if (*start > *end) {
  91. return -1;
  92. }
  93. return (*start > 0 || *end < clength);
  94. }
  95. static int ap_set_byterange(request_rec *r);
  96. typedef struct byterange_ctx {
  97. apr_bucket_brigade *bb;
  98. int num_ranges;
  99. char *boundary;
  100. char *bound_head;
  101. } byterange_ctx;
  102. /*
  103. * Here we try to be compatible with clients that want multipart/x-byteranges
  104. * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
  105. * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
  106. * that the browser supports an older protocol. We also check User-Agent
  107. * for Microsoft Internet Explorer 3, which needs this as well.
  108. */
  109. static int use_range_x(request_rec *r)
  110. {
  111. const char *ua;
  112. return (apr_table_get(r->headers_in, "Request-Range")
  113. || ((ua = apr_table_get(r->headers_in, "User-Agent"))
  114. && ap_strstr_c(ua, "MSIE 3")));
  115. }
  116. #define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
  117. #define PARTITION_ERR_FMT "apr_brigade_partition() failed " \
  118. "[%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]"
  119. AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
  120. apr_bucket_brigade *bb)
  121. {
  122. #define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
  123. request_rec *r = f->r;
  124. conn_rec *c = r->connection;
  125. byterange_ctx *ctx;
  126. apr_bucket *e;
  127. apr_bucket_brigade *bsend;
  128. apr_off_t range_start;
  129. apr_off_t range_end;
  130. char *current;
  131. apr_off_t clength = 0;
  132. apr_status_t rv;
  133. int found = 0;
  134. int num_ranges;
  135. /* Iterate through the brigade until reaching EOS or a bucket with
  136. * unknown length. */
  137. for (e = APR_BRIGADE_FIRST(bb);
  138. (e != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(e)
  139. && e->length != (apr_size_t)-1);
  140. e = APR_BUCKET_NEXT(e)) {
  141. clength += e->length;
  142. }
  143. /* Don't attempt to do byte range work if this brigade doesn't
  144. * contain an EOS, or if any of the buckets has an unknown length;
  145. * this avoids the cases where it is expensive to perform
  146. * byteranging (i.e. may require arbitrary amounts of memory). */
  147. if (!APR_BUCKET_IS_EOS(e) || clength <= 0) {
  148. ap_remove_output_filter(f);
  149. return ap_pass_brigade(f->next, bb);
  150. }
  151. num_ranges = ap_set_byterange(r);
  152. /* We have nothing to do, get out of the way. */
  153. if (num_ranges == 0) {
  154. ap_remove_output_filter(f);
  155. return ap_pass_brigade(f->next, bb);
  156. }
  157. ctx = apr_pcalloc(r->pool, sizeof(*ctx));
  158. ctx->num_ranges = num_ranges;
  159. /* create a brigade in case we never call ap_save_brigade() */
  160. ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
  161. if (ctx->num_ranges > 1) {
  162. /* Is ap_make_content_type required here? */
  163. const char *orig_ct = ap_make_content_type(r, r->content_type);
  164. ctx->boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx",
  165. (apr_uint64_t)r->request_time, (long) getpid());
  166. ap_set_content_type(r, apr_pstrcat(r->pool, "multipart",
  167. use_range_x(r) ? "/x-" : "/",
  168. "byteranges; boundary=",
  169. ctx->boundary, NULL));
  170. if (strcasecmp(orig_ct, NO_CONTENT_TYPE)) {
  171. ctx->bound_head = apr_pstrcat(r->pool,
  172. CRLF "--", ctx->boundary,
  173. CRLF "Content-type: ",
  174. orig_ct,
  175. CRLF "Content-range: bytes ",
  176. NULL);
  177. }
  178. else {
  179. /* if we have no type for the content, do our best */
  180. ctx->bound_head = apr_pstrcat(r->pool,
  181. CRLF "--", ctx->boundary,
  182. CRLF "Content-range: bytes ",
  183. NULL);
  184. }
  185. ap_xlate_proto_to_ascii(ctx->bound_head, strlen(ctx->bound_head));
  186. }
  187. /* this brigade holds what we will be sending */
  188. bsend = apr_brigade_create(r->pool, c->bucket_alloc);
  189. while ((current = ap_getword(r->pool, &r->range, ','))
  190. && (rv = parse_byterange(current, clength, &range_start,
  191. &range_end))) {
  192. apr_bucket *e2;
  193. apr_bucket *ec;
  194. if (rv == -1) {
  195. continue;
  196. }
  197. /* These calls to apr_brigage_partition should only fail in
  198. * pathological cases, e.g. a file being truncated whilst
  199. * being served. */
  200. if ((rv = apr_brigade_partition(bb, range_start, &ec)) != APR_SUCCESS) {
  201. ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  202. PARTITION_ERR_FMT, range_start, clength);
  203. continue;
  204. }
  205. if ((rv = apr_brigade_partition(bb, range_end+1, &e2)) != APR_SUCCESS) {
  206. ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
  207. PARTITION_ERR_FMT, range_end+1, clength);
  208. continue;
  209. }
  210. found = 1;
  211. /* For single range requests, we must produce Content-Range header.
  212. * Otherwise, we need to produce the multipart boundaries.
  213. */
  214. if (ctx->num_ranges == 1) {
  215. apr_table_setn(r->headers_out, "Content-Range",
  216. apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
  217. range_start, range_end, clength));
  218. }
  219. else {
  220. char *ts;
  221. e = apr_bucket_pool_create(ctx->bound_head, strlen(ctx->bound_head),
  222. r->pool, c->bucket_alloc);
  223. APR_BRIGADE_INSERT_TAIL(bsend, e);
  224. ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF,
  225. range_start, range_end, clength);
  226. ap_xlate_proto_to_ascii(ts, strlen(ts));
  227. e = apr_bucket_pool_create(ts, strlen(ts), r->pool,
  228. c->bucket_alloc);
  229. APR_BRIGADE_INSERT_TAIL(bsend, e);
  230. }
  231. do {
  232. apr_bucket *foo;
  233. const char *str;
  234. apr_size_t len;
  235. if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
  236. /* As above; this should not fail since the bucket has
  237. * a known length, but just to be sure, this takes
  238. * care of uncopyable buckets that do somehow manage
  239. * to slip through. */
  240. /* XXX: check for failure? */
  241. apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
  242. apr_bucket_copy(ec, &foo);
  243. }
  244. APR_BRIGADE_INSERT_TAIL(bsend, foo);
  245. ec = APR_BUCKET_NEXT(ec);
  246. } while (ec != e2);
  247. }
  248. if (found == 0) {
  249. ap_remove_output_filter(f);
  250. r->status = HTTP_OK;
  251. /* bsend is assumed to be empty if we get here. */
  252. e = ap_bucket_error_create(HTTP_RANGE_NOT_SATISFIABLE, NULL,
  253. r->pool, c->bucket_alloc);
  254. APR_BRIGADE_INSERT_TAIL(bsend, e);
  255. e = apr_bucket_eos_create(c->bucket_alloc);
  256. APR_BRIGADE_INSERT_TAIL(bsend, e);
  257. return ap_pass_brigade(f->next, bsend);
  258. }
  259. if (ctx->num_ranges > 1) {
  260. char *end;
  261. /* add the final boundary */
  262. end = apr_pstrcat(r->pool, CRLF "--", ctx->boundary, "--" CRLF, NULL);
  263. ap_xlate_proto_to_ascii(end, strlen(end));
  264. e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc);
  265. APR_BRIGADE_INSERT_TAIL(bsend, e);
  266. }
  267. e = apr_bucket_eos_create(c->bucket_alloc);
  268. APR_BRIGADE_INSERT_TAIL(bsend, e);
  269. /* we're done with the original content - all of our data is in bsend. */
  270. apr_brigade_cleanup(bb);
  271. /* send our multipart output */
  272. return ap_pass_brigade(f->next, bsend);
  273. }
  274. static int ap_set_byterange(request_rec *r)
  275. {
  276. const char *range;
  277. const char *if_range;
  278. const char *match;
  279. const char *ct;
  280. int num_ranges;
  281. if (r->assbackwards) {
  282. return 0;
  283. }
  284. /* Check for Range request-header (HTTP/1.1) or Request-Range for
  285. * backwards-compatibility with second-draft Luotonen/Franks
  286. * byte-ranges (e.g. Netscape Navigator 2-3).
  287. *
  288. * We support this form, with Request-Range, and (farther down) we
  289. * send multipart/x-byteranges instead of multipart/byteranges for
  290. * Request-Range based requests to work around a bug in Netscape
  291. * Navigator 2-3 and MSIE 3.
  292. */
  293. if (!(range = apr_table_get(r->headers_in, "Range"))) {
  294. range = apr_table_get(r->headers_in, "Request-Range");
  295. }
  296. if (!range || strncasecmp(range, "bytes=", 6) || r->status != HTTP_OK) {
  297. return 0;
  298. }
  299. /* is content already a single range? */
  300. if (apr_table_get(r->headers_out, "Content-Range")) {
  301. return 0;
  302. }
  303. /* is content already a multiple range? */
  304. if ((ct = apr_table_get(r->headers_out, "Content-Type"))
  305. && (!strncasecmp(ct, "multipart/byteranges", 20)
  306. || !strncasecmp(ct, "multipart/x-byteranges", 22))) {
  307. return 0;
  308. }
  309. /* Check the If-Range header for Etag or Date.
  310. * Note that this check will return false (as required) if either
  311. * of the two etags are weak.
  312. */
  313. if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
  314. if (if_range[0] == '"') {
  315. if (!(match = apr_table_get(r->headers_out, "Etag"))
  316. || (strcmp(if_range, match) != 0)) {
  317. return 0;
  318. }
  319. }
  320. else if (!(match = apr_table_get(r->headers_out, "Last-Modified"))
  321. || (strcmp(if_range, match) != 0)) {
  322. return 0;
  323. }
  324. }
  325. if (!ap_strchr_c(range, ',')) {
  326. /* a single range */
  327. num_ranges = 1;
  328. }
  329. else {
  330. /* a multiple range */
  331. num_ranges = 2;
  332. }
  333. r->status = HTTP_PARTIAL_CONTENT;
  334. r->range = range + 6;
  335. return num_ranges;
  336. }