Ejemplo n.º 1
0
void h2o_socketpool_init_by_address(h2o_socketpool_t *pool, struct sockaddr *sa, socklen_t salen, int is_ssl, size_t capacity)
{
    char host[NI_MAXHOST];
    size_t host_len;

    assert(salen <= sizeof(pool->peer.sockaddr.bytes));

    if ((host_len = h2o_socket_getnumerichost(sa, salen, host)) == SIZE_MAX) {
        if (sa->sa_family != AF_UNIX)
            h2o_fatal("failed to convert a non-unix socket address to a numerical representation");
        /* use the sockaddr_un::sun_path as the SNI indicator (is that the right thing to do?) */
        strcpy(host, ((struct sockaddr_un *)sa)->sun_path);
        host_len = strlen(host);
    }

    common_init(pool, H2O_SOCKETPOOL_TYPE_SOCKADDR, h2o_iovec_init(host, host_len), is_ssl, capacity);
    memcpy(&pool->peer.sockaddr.bytes, sa, salen);
    pool->peer.sockaddr.len = salen;
}
Ejemplo n.º 2
0
Archivo: proxy.c Proyecto: firewood/h2o
static h2o_iovec_t build_request(h2o_req_t *req, int keepalive, int is_websocket_handshake)
{
    h2o_iovec_t buf;
    size_t offset = 0, remote_addr_len = SIZE_MAX;
    char remote_addr[NI_MAXHOST];
    struct sockaddr_storage ss;
    socklen_t sslen;
    h2o_iovec_t cookie_buf = {}, xff_buf = {}, via_buf = {};

    /* for x-f-f */
    if ((sslen = req->conn->callbacks->get_peername(req->conn, (void *)&ss)) != 0)
        remote_addr_len = h2o_socket_getnumerichost((void *)&ss, sslen, remote_addr);

    /* build response */
    buf.len = req->method.len + req->path.len + req->authority.len + 512;
    buf.base = h2o_mem_alloc_pool(&req->pool, buf.len);

#define RESERVE(sz)                                                                                                                \
    do {                                                                                                                           \
        size_t required = offset + sz + 4 /* for "\r\n\r\n" */;                                                                    \
        if (required > buf.len) {                                                                                                  \
            do {                                                                                                                   \
                buf.len *= 2;                                                                                                      \
            } while (required > buf.len);                                                                                          \
            char *newp = h2o_mem_alloc_pool(&req->pool, buf.len);                                                                  \
            memcpy(newp, buf.base, offset);                                                                                        \
            buf.base = newp;                                                                                                       \
        }                                                                                                                          \
    } while (0)
#define APPEND(s, l)                                                                                                               \
    do {                                                                                                                           \
        memcpy(buf.base + offset, (s), (l));                                                                                       \
        offset += (l);                                                                                                             \
    } while (0)
#define APPEND_STRLIT(lit) APPEND((lit), sizeof(lit) - 1)
#define FLATTEN_PREFIXED_VALUE(prefix, value, add_size)                                                                            \
    do {                                                                                                                           \
        RESERVE(sizeof(prefix) - 1 + value.len + 2 + add_size);                                                                    \
        APPEND_STRLIT(prefix);                                                                                                     \
        if (value.len != 0) {                                                                                                      \
            APPEND(value.base, value.len);                                                                                         \
            if (add_size != 0) {                                                                                                   \
                buf.base[offset++] = ',';                                                                                          \
                buf.base[offset++] = ' ';                                                                                          \
            }                                                                                                                      \
        }                                                                                                                          \
    } while (0)

    APPEND(req->method.base, req->method.len);
    buf.base[offset++] = ' ';
    APPEND(req->path.base, req->path.len);
    APPEND_STRLIT(" HTTP/1.1\r\nconnection: ");
    if (is_websocket_handshake) {
        APPEND_STRLIT("upgrade\r\nupgrade: websocket\r\nhost: ");
    } else if (keepalive) {
        APPEND_STRLIT("keep-alive\r\nhost: ");
    } else {
        APPEND_STRLIT("close\r\nhost: ");
    }
    APPEND(req->authority.base, req->authority.len);
    buf.base[offset++] = '\r';
    buf.base[offset++] = '\n';
    assert(offset <= buf.len);
    if (req->entity.base != NULL) {
        RESERVE(sizeof("content-length: 18446744073709551615") - 1);
        offset += sprintf(buf.base + offset, "content-length: %zu\r\n", req->entity.len);
    }
    {
        const h2o_header_t *h, *h_end;
        for (h = req->headers.entries, h_end = h + req->headers.size; h != h_end; ++h) {
            if (h2o_iovec_is_token(h->name)) {
                const h2o_token_t *token = (void *)h->name;
                if (token->proxy_should_drop) {
                    continue;
                } else if (token == H2O_TOKEN_COOKIE) {
                    /* merge the cookie headers; see HTTP/2 8.1.2.5 and HTTP/1 (RFC6265 5.4) */
                    /* FIXME current algorithm is O(n^2) against the number of cookie headers */
                    cookie_buf = build_request_merge_headers(&req->pool, cookie_buf, h->value, ';');
                    continue;
                } else if (token == H2O_TOKEN_VIA) {
                    via_buf = build_request_merge_headers(&req->pool, via_buf, h->value, ',');
                    continue;
                } else if (token == H2O_TOKEN_X_FORWARDED_FOR) {
                    xff_buf = build_request_merge_headers(&req->pool, xff_buf, h->value, ',');
                    continue;
                }
            }
            if (h2o_lcstris(h->name->base, h->name->len, H2O_STRLIT("x-forwarded-proto")))
                continue;
            RESERVE(h->name->len + h->value.len + 2);
            APPEND(h->name->base, h->name->len);
            buf.base[offset++] = ':';
            buf.base[offset++] = ' ';
            APPEND(h->value.base, h->value.len);
            buf.base[offset++] = '\r';
            buf.base[offset++] = '\n';
        }
    }
    if (cookie_buf.len != 0) {
        FLATTEN_PREFIXED_VALUE("cookie: ", cookie_buf, 0);
        buf.base[offset++] = '\r';
        buf.base[offset++] = '\n';
    }
    FLATTEN_PREFIXED_VALUE("x-forwarded-proto: ", req->input.scheme->name, 0);
    buf.base[offset++] = '\r';
    buf.base[offset++] = '\n';
    if (remote_addr_len != SIZE_MAX) {
        FLATTEN_PREFIXED_VALUE("x-forwarded-for: ", xff_buf, remote_addr_len);
        APPEND(remote_addr, remote_addr_len);
    } else {
        FLATTEN_PREFIXED_VALUE("x-forwarded-for: ", xff_buf, 0);
    }
    buf.base[offset++] = '\r';
    buf.base[offset++] = '\n';
    FLATTEN_PREFIXED_VALUE("via: ", via_buf, sizeof("1.1 ") - 1 + req->input.authority.len);
    if (req->version < 0x200) {
        buf.base[offset++] = '1';
        buf.base[offset++] = '.';
        buf.base[offset++] = '0' + (0x100 <= req->version && req->version <= 0x109 ? req->version - 0x100 : 0);
    } else {
        buf.base[offset++] = '2';
    }
    buf.base[offset++] = ' ';
    APPEND(req->input.authority.base, req->input.authority.len);
    APPEND_STRLIT("\r\n\r\n");

#undef RESERVE
#undef APPEND
#undef APPEND_STRLIT
#undef FLATTEN_PREFIXED_VALUE

    /* set the length */
    assert(offset <= buf.len);
    buf.len = offset;

    return buf;
}
Ejemplo n.º 3
0
static h2o_iovec_t build_request(h2o_req_t *req, int keepalive, int is_websocket_handshake, int use_proxy_protocol)
{
    h2o_iovec_t buf;
    size_t offset = 0, remote_addr_len = SIZE_MAX;
    char remote_addr[NI_MAXHOST];
    struct sockaddr_storage ss;
    socklen_t sslen;
    h2o_iovec_t cookie_buf = {NULL}, xff_buf = {NULL}, via_buf = {NULL};
    int preserve_x_forwarded_proto = req->conn->ctx->globalconf->proxy.preserve_x_forwarded_proto;
    int emit_x_forwarded_headers = req->conn->ctx->globalconf->proxy.emit_x_forwarded_headers;
    int emit_via_header = req->conn->ctx->globalconf->proxy.emit_via_header;

    /* for x-f-f */
    if ((sslen = req->conn->callbacks->get_peername(req->conn, (void *)&ss)) != 0)
        remote_addr_len = h2o_socket_getnumerichost((void *)&ss, sslen, remote_addr);

    /* build response */
    buf.len = req->method.len + req->path.len + req->authority.len + 512;
    if (use_proxy_protocol)
        buf.len += H2O_PROXY_HEADER_MAX_LENGTH;
    buf.base = h2o_mem_alloc_pool(&req->pool, buf.len);

#define RESERVE(sz)                                                                                                                \
    do {                                                                                                                           \
        size_t required = offset + sz + 4 /* for "\r\n\r\n" */;                                                                    \
        if (required > buf.len) {                                                                                                  \
            do {                                                                                                                   \
                buf.len *= 2;                                                                                                      \
            } while (required > buf.len);                                                                                          \
            char *newp = h2o_mem_alloc_pool(&req->pool, buf.len);                                                                  \
            memcpy(newp, buf.base, offset);                                                                                        \
            buf.base = newp;                                                                                                       \
        }                                                                                                                          \
    } while (0)
#define APPEND(s, l)                                                                                                               \
    do {                                                                                                                           \
        memcpy(buf.base + offset, (s), (l));                                                                                       \
        offset += (l);                                                                                                             \
    } while (0)
#define APPEND_STRLIT(lit) APPEND((lit), sizeof(lit) - 1)
#define FLATTEN_PREFIXED_VALUE(prefix, value, add_size)                                                                            \
    do {                                                                                                                           \
        RESERVE(sizeof(prefix) - 1 + value.len + 2 + add_size);                                                                    \
        APPEND_STRLIT(prefix);                                                                                                     \
        if (value.len != 0) {                                                                                                      \
            APPEND(value.base, value.len);                                                                                         \
            if (add_size != 0) {                                                                                                   \
                buf.base[offset++] = ',';                                                                                          \
                buf.base[offset++] = ' ';                                                                                          \
            }                                                                                                                      \
        }                                                                                                                          \
    } while (0)

    if (use_proxy_protocol)
        offset += h2o_stringify_proxy_header(req->conn, buf.base + offset);

    APPEND(req->method.base, req->method.len);
    buf.base[offset++] = ' ';
    APPEND(req->path.base, req->path.len);
    APPEND_STRLIT(" HTTP/1.1\r\nconnection: ");
    if (is_websocket_handshake) {
        APPEND_STRLIT("upgrade\r\nupgrade: websocket\r\nhost: ");
    } else if (keepalive) {
        APPEND_STRLIT("keep-alive\r\nhost: ");
    } else {
        APPEND_STRLIT("close\r\nhost: ");
    }
    APPEND(req->authority.base, req->authority.len);
    buf.base[offset++] = '\r';
    buf.base[offset++] = '\n';
    assert(offset <= buf.len);
    if (req->entity.base != NULL || req_requires_content_length(req)) {
        RESERVE(sizeof("content-length: " H2O_UINT64_LONGEST_STR) - 1);
        offset += sprintf(buf.base + offset, "content-length: %zu\r\n", req->entity.len);
    }

    /* rewrite headers if necessary */
    h2o_headers_t req_headers = req->headers;
    if (req->overrides != NULL && req->overrides->headers_cmds != NULL) {
        req_headers.entries = NULL;
        req_headers.size = 0;
        req_headers.capacity = 0;
        h2o_headers_command_t *cmd;
        h2o_vector_reserve(&req->pool, &req_headers, req->headers.capacity);
        memcpy(req_headers.entries, req->headers.entries, sizeof(req->headers.entries[0]) * req->headers.size);
        req_headers.size = req->headers.size;
        for (cmd = req->overrides->headers_cmds; cmd->cmd != H2O_HEADERS_CMD_NULL; ++cmd)
            h2o_rewrite_headers(&req->pool, &req_headers, cmd);
    }

    {
        const h2o_header_t *h, *h_end;
        for (h = req_headers.entries, h_end = h + req_headers.size; h != h_end; ++h) {
            if (h2o_iovec_is_token(h->name)) {
                const h2o_token_t *token = (void *)h->name;
                if (token->proxy_should_drop) {
                    continue;
                } else if (token == H2O_TOKEN_COOKIE) {
                    /* merge the cookie headers; see HTTP/2 8.1.2.5 and HTTP/1 (RFC6265 5.4) */
                    /* FIXME current algorithm is O(n^2) against the number of cookie headers */
                    cookie_buf = build_request_merge_headers(&req->pool, cookie_buf, h->value, ';');
                    continue;
                } else if (token == H2O_TOKEN_VIA) {
                    if (!emit_via_header) {
                        goto AddHeader;
                    }
                    via_buf = build_request_merge_headers(&req->pool, via_buf, h->value, ',');
                    continue;
                } else if (token == H2O_TOKEN_X_FORWARDED_FOR) {
                    if (!emit_x_forwarded_headers) {
                        goto AddHeader;
                    }
                    xff_buf = build_request_merge_headers(&req->pool, xff_buf, h->value, ',');
                    continue;
                }
            }
            if (!preserve_x_forwarded_proto && h2o_lcstris(h->name->base, h->name->len, H2O_STRLIT("x-forwarded-proto")))
                continue;
        AddHeader:
            RESERVE(h->name->len + h->value.len + 2);
            APPEND(h->orig_name ? h->orig_name : h->name->base, h->name->len);
            buf.base[offset++] = ':';
            buf.base[offset++] = ' ';
            APPEND(h->value.base, h->value.len);
            buf.base[offset++] = '\r';
            buf.base[offset++] = '\n';
        }
    }
    if (cookie_buf.len != 0) {
        FLATTEN_PREFIXED_VALUE("cookie: ", cookie_buf, 0);
        buf.base[offset++] = '\r';
        buf.base[offset++] = '\n';
    }
    if (emit_x_forwarded_headers) {
        if (!preserve_x_forwarded_proto) {
            FLATTEN_PREFIXED_VALUE("x-forwarded-proto: ", req->input.scheme->name, 0);
            buf.base[offset++] = '\r';
            buf.base[offset++] = '\n';
        }
        if (remote_addr_len != SIZE_MAX) {
            FLATTEN_PREFIXED_VALUE("x-forwarded-for: ", xff_buf, remote_addr_len);
            APPEND(remote_addr, remote_addr_len);
        } else {
            FLATTEN_PREFIXED_VALUE("x-forwarded-for: ", xff_buf, 0);
        }
        buf.base[offset++] = '\r';
        buf.base[offset++] = '\n';
    }
    if (emit_via_header) {
        FLATTEN_PREFIXED_VALUE("via: ", via_buf, sizeof("1.1 ") - 1 + req->input.authority.len);
        if (req->version < 0x200) {
            buf.base[offset++] = '1';
            buf.base[offset++] = '.';
            buf.base[offset++] = '0' + (0x100 <= req->version && req->version <= 0x109 ? req->version - 0x100 : 0);
        } else {
            buf.base[offset++] = '2';
        }
        buf.base[offset++] = ' ';
        APPEND(req->input.authority.base, req->input.authority.len);
        buf.base[offset++] = '\r';
        buf.base[offset++] = '\n';
    }
    APPEND_STRLIT("\r\n");

#undef RESERVE
#undef APPEND
#undef APPEND_STRLIT
#undef FLATTEN_PREFIXED_VALUE

    /* set the length */
    assert(offset <= buf.len);
    buf.len = offset;

    return buf;
}
Ejemplo n.º 4
0
static void log_access(h2o_logger_t *_self, h2o_req_t *req)
{
    struct st_h2o_access_logger_t *self = (struct st_h2o_access_logger_t *)_self;
    h2o_access_log_filehandle_t *fh = self->fh;
    char *line, *pos, *line_end;
    size_t element_index;

    /* note: LOG_ALLOCA_SIZE should be much greater than NI_MAXHOST to avoid unnecessary reallocations */
    line = alloca(LOG_ALLOCA_SIZE);
    pos = line;
    line_end = line + LOG_ALLOCA_SIZE;

    for (element_index = 0; element_index != fh->num_elements; ++element_index) {
        struct log_element_t *element = fh->elements + element_index;

/* reserve capacity + suffix.len */
#define RESERVE(capacity)                                                                                                          \
    do {                                                                                                                           \
        if ((capacity) + element->suffix.len > line_end - pos) {                                                                   \
            size_t off = pos - line;                                                                                               \
            line = expand_line_buf(line, line_end - line, off + (capacity) + element->suffix.len);                                 \
            pos = line + off;                                                                                                      \
        }                                                                                                                          \
    } while (0)

        switch (element->type) {
        case ELEMENT_TYPE_EMPTY:
            RESERVE(0);
            break;
        case ELEMENT_TYPE_BYTES_SENT: /* %b */
            RESERVE(sizeof("18446744073709551615") - 1);
            pos += sprintf(pos, "%llu", (unsigned long long)req->bytes_sent);
            break;
        case ELEMENT_TYPE_PROTOCOL: /* %H */
            RESERVE(sizeof("HTTP/1.1"));
            pos += h2o_stringify_protocol_version(pos, req->version);
            break;
        case ELEMENT_TYPE_REMOTE_ADDR: /* %h */ {
            struct sockaddr_storage ss;
            socklen_t sslen;
            if ((sslen = req->conn->get_peername(req->conn, (void *)&ss)) != 0) {
                RESERVE(NI_MAXHOST);
                size_t l = h2o_socket_getnumerichost((void *)&ss, sslen, pos);
                if (l != SIZE_MAX)
                    pos += l;
                else
                    *pos++ = '-';
            } else {
                RESERVE(1);
                *pos++ = '-';
            }
        } break;
        case ELEMENT_TYPE_METHOD: /* %m */
            RESERVE(req->input.method.len * 4);
            pos = append_unsafe_string(pos, req->input.method.base, req->input.method.len);
            break;
        case ELEMENT_TYPE_QUERY: /* %q */
            if (req->input.query_at != SIZE_MAX) {
                size_t len = req->input.path.len - req->input.query_at;
                RESERVE(len * 4);
                pos = append_unsafe_string(pos, req->input.path.base + req->input.query_at, len);
            }
            break;
        case ELEMENT_TYPE_REQUEST_LINE: /* %r */
            RESERVE((req->input.method.len + req->input.path.len) * 4 + sizeof("  HTTP/1.1"));
            pos = append_unsafe_string(pos, req->input.method.base, req->input.method.len);
            *pos++ = ' ';
            pos = append_unsafe_string(pos, req->input.path.base, req->input.path.len);
            *pos++ = ' ';
            pos += h2o_stringify_protocol_version(pos, req->version);
            break;
        case ELEMENT_TYPE_STATUS: /* %s */
            RESERVE(sizeof("2147483647") - 1);
            pos += sprintf(pos, "%d", req->res.status);
            break;
        case ELEMENT_TYPE_TIMESTAMP: /* %t */
            RESERVE(H2O_TIMESTR_LOG_LEN + 2);
            *pos++ = '[';
            pos = append_safe_string(pos, req->processed_at.str->log, H2O_TIMESTR_LOG_LEN);
            *pos++ = ']';
            break;
        case ELEMENT_TYPE_URL_PATH: /* %U */
        {
            size_t path_len = req->input.query_at == SIZE_MAX ? req->input.path.len : req->input.query_at;
            RESERVE(req->input.scheme->name.len + (sizeof("://") - 1) + (req->input.authority.len + path_len) * 4);
            pos = append_safe_string(pos, req->input.scheme->name.base, req->input.scheme->name.len);
            pos = append_safe_string(pos, H2O_STRLIT("://"));
            pos = append_unsafe_string(pos, req->input.authority.base, req->input.authority.len);
            pos = append_unsafe_string(pos, req->input.path.base, path_len);
        } break;
        case ELEMENT_TYPE_AUTHORITY: /* %V */
            RESERVE(req->input.authority.len * 4);
            pos = append_unsafe_string(pos, req->input.authority.base, req->input.authority.len);
            break;
        case ELEMENT_TYPE_HOSTCONF: /* %v */
            RESERVE(req->hostconf->authority.hostport.len * 4);
            pos = append_unsafe_string(pos, req->hostconf->authority.hostport.base, req->hostconf->authority.hostport.len);
            break;

        case ELEMENT_TYPE_LOGNAME:     /* %l */
        case ELEMENT_TYPE_REMOTE_USER: /* %u */
            RESERVE(1);
            *pos++ = '-';
            break;

#define EMIT_HEADER(headers, _index)                                                                                               \
    do {                                                                                                                           \
        ssize_t index = (_index);                                                                                                  \
        if (index != -1) {                                                                                                         \
            const h2o_header_t *header = (headers)->entries + index;                                                               \
            RESERVE(header->value.len * 4);                                                                                        \
            pos = append_unsafe_string(pos, header->value.base, header->value.len);                                                \
        } else {                                                                                                                   \
            RESERVE(1);                                                                                                            \
            *pos++ = '-';                                                                                                          \
        }                                                                                                                          \
    } while (0)
        case ELEMENT_TYPE_IN_HEADER_TOKEN:
            EMIT_HEADER(&req->headers, h2o_find_header(&req->headers, element->data.header_token, SIZE_MAX));
            break;
        case ELEMENT_TYPE_IN_HEADER_STRING:
            EMIT_HEADER(&req->headers, h2o_find_header_by_str(&req->headers, element->data.header_string.base,
                                                              element->data.header_string.len, SIZE_MAX));
            break;
        case ELEMENT_TYPE_OUT_HEADER_TOKEN:
            EMIT_HEADER(&req->res.headers, h2o_find_header(&req->res.headers, element->data.header_token, SIZE_MAX));
            break;
        case ELEMENT_TYPE_OUT_HEADER_STRING:
            EMIT_HEADER(&req->res.headers, h2o_find_header_by_str(&req->res.headers, element->data.header_string.base,
                                                                  element->data.header_string.len, SIZE_MAX));
            break;
#undef EMIT_HEADER

        default:
            assert(!"unknown type");
            break;
        }

#undef RESERVE

        pos = append_safe_string(pos, element->suffix.base, element->suffix.len);
    }

    write(fh->fd, line, pos - line);

    if (line_end - line != LOG_ALLOCA_SIZE)
        free(line);
}