示例#1
0
文件: server_file.c 项目: Nshk/httpd
int
server_partial_file_request(struct httpd *env, struct client *clt, char *path,
                            struct stat *st, char *range_str)
{
    struct http_descriptor	*resp = clt->clt_descresp;
    struct http_descriptor	*desc = clt->clt_descreq;
    struct media_type	*media, multipart_media;
    struct range		*range;
    struct evbuffer		*evb = NULL;
    size_t			 content_length;
    int		 	 code = 500, fd = -1, i, nranges, ret;
    uint32_t		 boundary;
    char		 	 content_range[64];
    const char		*errstr = NULL;

    /* Ignore range request for methods other than GET */
    if (desc->http_method != HTTP_METHOD_GET)
        return server_file_request(env, clt, path, st);

    if ((range = parse_range(range_str, st->st_size, &nranges)) == NULL) {
        code = 416;
        (void)snprintf(content_range, sizeof(content_range),
                       "bytes */%lld", st->st_size);
        errstr = content_range;
        goto abort;
    }

    /* Now open the file, should be readable or we have another problem */
    if ((fd = open(path, O_RDONLY)) == -1)
        goto abort;

    media = media_find(env->sc_mediatypes, path);
    if ((evb = evbuffer_new()) == NULL) {
        errstr = "failed to allocate file buffer";
        goto abort;
    }

    if (nranges == 1) {
        (void)snprintf(content_range, sizeof(content_range),
                       "bytes %lld-%lld/%lld", range->start, range->end,
                       st->st_size);
        if (kv_add(&resp->http_headers, "Content-Range",
                   content_range) == NULL)
            goto abort;

        content_length = range->end - range->start + 1;
        if (buffer_add_range(fd, evb, range) == 0)
            goto abort;

    } else {
        content_length = 0;
        boundary = arc4random();
        /* Generate a multipart payload of byteranges */
        while (nranges--) {
            if ((i = evbuffer_add_printf(evb, "\r\n--%ud\r\n",
                                         boundary)) == -1)
                goto abort;

            content_length += i;
            if ((i = evbuffer_add_printf(evb,
                                         "Content-Type: %s/%s\r\n",
                                         media == NULL ? "application" : media->media_type,
                                         media == NULL ?
                                         "octet-stream" : media->media_subtype)) == -1)
                goto abort;

            content_length += i;
            if ((i = evbuffer_add_printf(evb,
                                         "Content-Range: bytes %lld-%lld/%lld\r\n\r\n",
                                         range->start, range->end, st->st_size)) == -1)
                goto abort;

            content_length += i;
            if (buffer_add_range(fd, evb, range) == 0)
                goto abort;

            content_length += range->end - range->start + 1;
            range++;
        }

        if ((i = evbuffer_add_printf(evb, "\r\n--%ud--\r\n",
                                     boundary)) == -1)
            goto abort;

        content_length += i;

        /* prepare multipart/byteranges media type */
        (void)strlcpy(multipart_media.media_type, "multipart",
                      sizeof(multipart_media.media_type));
        (void)snprintf(multipart_media.media_subtype,
                       sizeof(multipart_media.media_subtype),
                       "byteranges; boundary=%ud", boundary);
        media = &multipart_media;
    }

    ret = server_response_http(clt, 206, media, content_length,
                               MINIMUM(time(NULL), st->st_mtim.tv_sec));
    switch (ret) {
    case -1:
        goto fail;
    case 0:
        /* Connection is already finished */
        close(fd);
        evbuffer_free(evb);
        evb = NULL;
        goto done;
    default:
        break;
    }

    if (server_bufferevent_write_buffer(clt, evb) == -1)
        goto fail;

    evbuffer_free(evb);
    evb = NULL;

    bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE);
    if (clt->clt_persist)
        clt->clt_toread = TOREAD_HTTP_HEADER;
    else
        clt->clt_toread = TOREAD_HTTP_NONE;
    clt->clt_done = 0;

done:
    server_reset_http(clt);
    return (0);
fail:
    bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE);
    bufferevent_free(clt->clt_bev);
    clt->clt_bev = NULL;
abort:
    if (errstr == NULL)
        errstr = strerror(errno);
    server_abort_http(clt, code, errstr);
    return (-1);
}
示例#2
0
文件: server_file.c 项目: Nshk/httpd
int
server_file_access(struct httpd *env, struct client *clt,
                   char *path, size_t len)
{
    struct http_descriptor	*desc = clt->clt_descreq;
    struct server_config	*srv_conf = clt->clt_srv_conf;
    struct stat		 st;
    struct kv		*r, key;
    char			*newpath;
    int			 ret;

    errno = 0;

    if (access(path, R_OK) == -1) {
        goto fail;
    } else if (stat(path, &st) == -1) {
        goto fail;
    } else if (S_ISDIR(st.st_mode)) {
        /* Deny access if directory indexing is disabled */
        if (srv_conf->flags & SRVFLAG_NO_INDEX) {
            errno = EACCES;
            goto fail;
        }

        if (desc->http_path_alias != NULL) {
            /* Recursion - the index "file" is a directory? */
            errno = EINVAL;
            goto fail;
        }

        /* Redirect to path with trailing "/" */
        if (path[strlen(path) - 1] != '/') {
            if (asprintf(&newpath, "http%s://%s%s/",
                         srv_conf->flags & SRVFLAG_TLS ? "s" : "",
                         desc->http_host, desc->http_path) == -1)
                return (500);
            /* Path alias will be used for the redirection */
            desc->http_path_alias = newpath;

            /* Indicate that the file has been moved */
            return (301);
        }

        /* Append the default index file to the location */
        if (asprintf(&newpath, "%s%s", desc->http_path,
                     srv_conf->index) == -1)
            return (500);
        desc->http_path_alias = newpath;
        if (server_getlocation(clt, newpath) != srv_conf) {
            /* The location has changed */
            return (server_file(env, clt));
        }

        /* Otherwise append the default index file to the path */
        if (strlcat(path, srv_conf->index, len) >= len) {
            errno = EACCES;
            goto fail;
        }

        ret = server_file_access(env, clt, path, len);
        if (ret == 404) {
            /*
             * Index file not found; fail if auto-indexing is
             * not enabled, otherwise return success but
             * indicate directory with S_ISDIR of the previous
             * stat.
             */
            if ((srv_conf->flags & SRVFLAG_AUTO_INDEX) == 0) {
                errno = EACCES;
                goto fail;
            }

            return (server_file_index(env, clt, &st));
        }
        return (ret);
    } else if (!S_ISREG(st.st_mode)) {
        /* Don't follow symlinks and ignore special files */
        errno = EACCES;
        goto fail;
    }

    key.kv_key = "Range";
    r = kv_find(&desc->http_headers, &key);
    if (r != NULL)
        return (server_partial_file_request(env, clt, path, &st,
                                            r->kv_value));
    else
        return (server_file_request(env, clt, path, &st));

fail:
    switch (errno) {
    case ENOENT:
    case ENOTDIR:
        return (404);
    case EACCES:
        return (403);
    default:
        return (500);
    }

    /* NOTREACHED */
}
示例#3
0
文件: server_file.c 项目: reyk/httpd
int
server_partial_file_request(struct httpd *env, struct client *clt, char *path,
    struct stat *st, char *range_str)
{
	struct server_config	*srv_conf = clt->clt_srv_conf;
	struct http_descriptor	*resp = clt->clt_descresp;
	struct http_descriptor	*desc = clt->clt_descreq;
	struct media_type	*media, multipart_media;
	struct range_data	*r = &clt->clt_ranges;
	struct range		*range;
	size_t			 content_length = 0;
	int			 code = 500, fd = -1, i, nranges, ret;
	char			 content_range[64];
	const char		*errstr = NULL;

	/* Ignore range request for methods other than GET */
	if (desc->http_method != HTTP_METHOD_GET)
		return server_file_request(env, clt, path, st);

	if ((nranges = parse_ranges(clt, range_str, st->st_size)) < 1) {
		code = 416;
		(void)snprintf(content_range, sizeof(content_range),
		    "bytes */%lld", st->st_size);
		errstr = content_range;
		goto abort;
	}

	/* Now open the file, should be readable or we have another problem */
	if ((fd = open(path, O_RDONLY)) == -1)
		goto abort;

	media = media_find_config(env, srv_conf, path);
	r->range_media = media;

	if (nranges == 1) {
		range = &r->range[0];
		(void)snprintf(content_range, sizeof(content_range),
		    "bytes %lld-%lld/%lld", range->start, range->end,
		    st->st_size);
		if (kv_add(&resp->http_headers, "Content-Range",
		    content_range) == NULL)
			goto abort;

		range = &r->range[0];
		content_length += range->end - range->start + 1;
	} else {
		/* Add boundary, all parts will be handled by the callback */
		arc4random_buf(&clt->clt_boundary, sizeof(clt->clt_boundary));

		/* Calculate Content-Length of the complete multipart body */
		for (i = 0; i < nranges; i++) {
			range = &r->range[i];

			/* calculate Content-Length of the complete body */
			if ((ret = snprintf(NULL, 0,
			    "\r\n--%llu\r\n"
			    "Content-Type: %s/%s\r\n"
			    "Content-Range: bytes %lld-%lld/%lld\r\n\r\n",
			    clt->clt_boundary,
			    media->media_type, media->media_subtype,
			    range->start, range->end, st->st_size)) < 0)
				goto abort;

			/* Add data length */
			content_length += ret + range->end - range->start + 1;

		}
		if ((ret = snprintf(NULL, 0, "\r\n--%llu--\r\n",
		    clt->clt_boundary)) < 0)
			goto abort;
		content_length += ret;

		/* prepare multipart/byteranges media type */
		(void)strlcpy(multipart_media.media_type, "multipart",
		    sizeof(multipart_media.media_type));
		(void)snprintf(multipart_media.media_subtype,
		    sizeof(multipart_media.media_subtype),
		    "byteranges; boundary=%llu", clt->clt_boundary);
		media = &multipart_media;
	}

	/* Start with first range */
	r->range_toread = TOREAD_HTTP_RANGE;

	ret = server_response_http(clt, 206, media, content_length,
	    MINIMUM(time(NULL), st->st_mtim.tv_sec));
	switch (ret) {
	case -1:
		goto fail;
	case 0:
		/* Connection is already finished */
		close(fd);
		goto done;
	default:
		break;
	}

	clt->clt_fd = fd;
	if (clt->clt_srvbev != NULL)
		bufferevent_free(clt->clt_srvbev);

	clt->clt_srvbev_throttled = 0;
	clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read_httprange,
	    server_write, server_file_error, clt);
	if (clt->clt_srvbev == NULL) {
		errstr = "failed to allocate file buffer event";
		goto fail;
	}

	/* Adjust read watermark to the socket output buffer size */
	bufferevent_setwatermark(clt->clt_srvbev, EV_READ, 0,
	    clt->clt_sndbufsiz);

	bufferevent_settimeout(clt->clt_srvbev,
	    srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
	bufferevent_enable(clt->clt_srvbev, EV_READ);
	bufferevent_disable(clt->clt_bev, EV_READ);

 done:
	server_reset_http(clt);
	return (0);
 fail:
	bufferevent_disable(clt->clt_bev, EV_READ|EV_WRITE);
	bufferevent_free(clt->clt_bev);
	clt->clt_bev = NULL;
 abort:
	if (fd != -1)
		close(fd);
	if (errstr == NULL)
		errstr = strerror(errno);
	server_abort_http(clt, code, errstr);
	return (-1);
}