Compare commits
22
Commits
fee2fbbabf
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9d52d14d0 | ||
|
|
94e5c04900 | ||
|
|
82f432938a | ||
|
|
694f182c33 | ||
|
|
d42d1aa241 | ||
|
|
602334da65 | ||
|
|
b65b2d1663 | ||
|
|
45064635e8 | ||
|
|
5a9ef13268 | ||
|
|
f35d500e17 | ||
|
|
ae58265dda | ||
|
|
90c231613f | ||
|
|
da24305046 | ||
|
|
57def53924 | ||
|
|
aea1e5e5d6 | ||
|
|
86951937fd | ||
|
|
542db9bf40 | ||
|
|
0f1559a356 | ||
|
|
ba676aaec9 | ||
|
|
671ee2f9ee | ||
|
|
15aa168406 | ||
|
|
c10ab73fe3 |
@@ -0,0 +1,15 @@
|
||||
---
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
For NGINX troubleshooting/technical help, please visit our community forum instead of asking your questions here. We will politely redirect these types of questions to the forum.
|
||||
- type: textarea
|
||||
id: general
|
||||
attributes:
|
||||
label: What would you like to discuss?
|
||||
description: Please provide as much context as possible. Remember that only general discussions related to the NGINX codebase will be addressed on GitHub. For anything else, please visit our [community forum](https://community.nginx.org/).
|
||||
value: |
|
||||
I would like to discuss...
|
||||
validations:
|
||||
required: true
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
For NGINX troubleshooting/technical help, please visit our community forum instead of asking your questions here. We will politely redirect these types of questions to the forum.
|
||||
- type: textarea
|
||||
id: ideas
|
||||
attributes:
|
||||
label: What idea would you like to discuss?
|
||||
description: Please provide as much context as possible. Remember that only ideas related to the NGINX codebase will be addressed on GitHub. For anything else, please visit our [community forum](https://community.nginx.org/).
|
||||
value: |
|
||||
I have an idea for...
|
||||
validations:
|
||||
required: true
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
For NGINX troubleshooting/technical help, please visit our community forum instead of asking your questions here. We will politely redirect these types of questions to the forum.
|
||||
- type: textarea
|
||||
id: q-a
|
||||
attributes:
|
||||
label: What question do you have?
|
||||
description: Please provide as much context as possible. Remember that only questions related to the NGINX codebase will be addressed on GitHub. For anything else, please visit our [community forum](https://community.nginx.org/).
|
||||
value: |
|
||||
I would like to know...
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ""
|
||||
labels: "bug"
|
||||
---
|
||||
|
||||
### Environment
|
||||
|
||||
Include the result of the following commands:
|
||||
- `nginx -V`
|
||||
- `uname -a`
|
||||
|
||||
### Description
|
||||
|
||||
Describe the bug in full detail including expected and actual behavior.
|
||||
Specify conditions that caused it. Provide the relevant part of nginx
|
||||
configuration and debug log.
|
||||
|
||||
- [ ] The bug is reproducible with the latest version of nginx
|
||||
- [ ] The nginx configuration is minimized to the smallest possible
|
||||
to reproduce the issue and doesn't contain third-party modules
|
||||
|
||||
#### nginx configuration
|
||||
|
||||
```
|
||||
# Your nginx configuration here
|
||||
```
|
||||
or share the configuration in [gist](https://gist.github.com/).
|
||||
|
||||
#### nginx debug log
|
||||
|
||||
It is advised to enable
|
||||
[debug logging](http://nginx.org/en/docs/debugging_log.html).
|
||||
```
|
||||
# Your nginx debug log here
|
||||
```
|
||||
or share the debug log in [gist](https://gist.github.com/).
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
name: 🐛 Bug report
|
||||
description: Create a report to help us improve
|
||||
labels: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
|
||||
Before you continue filling out this report, please take a moment to check that your bug has not been [already reported on GitHub][issue search], is reproducible with the latest version of nginx, and does not involve any third-party modules 🙌
|
||||
|
||||
Remember to redact any sensitive information such as authentication credentials and/or license keys!
|
||||
|
||||
**Note:** If you are seeking community support, please start a new topic in the [NGINX Community forum][forum]. If you wish to discuss the codebase, please start a new thread via [GitHub discussions][discussions].
|
||||
|
||||
[issue search]: https://github.com/nginx/nginx/search?q=is%3Aissue&type=issues
|
||||
[discussions]: https://github.com/nginx/nginx/discussions
|
||||
[forum]: https://community.nginx.org
|
||||
|
||||
- type: textarea
|
||||
id: overview
|
||||
attributes:
|
||||
label: Bug Overview
|
||||
description: A clear and concise overview of the bug.
|
||||
placeholder: When I do "X", "Y" happens instead of "Z".
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
placeholder: When I do "X", I expect "Z" to happen.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: Steps to Reproduce the Bug
|
||||
description: Detail the series of steps required to reproduce the bug.
|
||||
placeholder: When I run "X" using [...], "X" fails with "Y" error message. If I check the terminal outputs and/or logs, I see the following info.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: configuration
|
||||
attributes:
|
||||
label: NGINX Configuration
|
||||
description: Please provide your NGINX configuration. Minimize it to the smallest possible configuration that reproduces the issue.
|
||||
value: |
|
||||
```
|
||||
# Your NGINX configuration
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: version
|
||||
attributes:
|
||||
label: NGINX version and build configuration options
|
||||
description: Please provide details about your NGINX build.
|
||||
value: |
|
||||
The output of `nginx -V`: [...]
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: environment
|
||||
attributes:
|
||||
label: Environment where NGINX is being built and/or deployed
|
||||
description: Please provide details about your environment.
|
||||
value: |
|
||||
- Target deployment platform: [e.g. AWS/GCP/local cluster/etc...]
|
||||
- Target OS: [e.g. RHEL 9/Ubuntu 24.04/etc...]
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: architecture
|
||||
attributes:
|
||||
label: Architecture where NGINX is being built and/or deployed
|
||||
description: Please provide details about your deployment environment.
|
||||
value: |
|
||||
The output of `uname -a`: [...]
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: NGINX Debug Log
|
||||
description: Please provide your NGINX debug log. See this [doc](http://nginx.org/en/docs/debugging_log.html) for details on how to enable it.
|
||||
value: |
|
||||
```
|
||||
# Your NGINX debug log
|
||||
```
|
||||
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other context about the problem here.
|
||||
placeholder: Feel free to add any other context/information/screenshots/etc... that you think might be relevant to this issue in here.
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Talk to the NGINX community!
|
||||
url: https://community.nginx.org
|
||||
about: A community forum for NGINX users, developers, and contributors
|
||||
- name: 📝 Code of Conduct
|
||||
url: https://www.contributor-covenant.org/version/2/1/code_of_conduct
|
||||
about: NGINX follows the Contributor Covenant Code of Conduct to ensure a safe and inclusive community
|
||||
- name: 💼 For commercial & enterprise users
|
||||
url: https://www.f5.com/products/nginx
|
||||
about: F5 offers a wide range of NGINX products for commercial & enterprise users
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a feature for nginx
|
||||
title: ""
|
||||
labels: "feature"
|
||||
---
|
||||
|
||||
### Describe the feature you'd like to add to nginx
|
||||
|
||||
A clear and concise description of the feature.
|
||||
|
||||
### Describe the problem this feature solves
|
||||
|
||||
A clear and concise description of the problem.
|
||||
|
||||
### Additional context
|
||||
|
||||
Add any other context about the feature request here.
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: ✨ Feature request
|
||||
description: Suggest an idea for this project
|
||||
labels: enhancement
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request!
|
||||
|
||||
Before you continue filling out this request, please take a moment to check that your feature has not been [already requested on GitHub][issue search] 🙌
|
||||
|
||||
**Note:** If you are seeking community support, please start a new topic in the [NGINX Community forum][forum]. If you wish to discuss the codebase, please start a new thread via [GitHub discussions][discussions].
|
||||
|
||||
[issue search]: https://github.com/nginx/nginx/search?q=is%3Aissue&type=issues
|
||||
[discussions]: https://github.com/nginx/nginx/discussions
|
||||
[forum]: https://community.nginx.org
|
||||
|
||||
- type: textarea
|
||||
id: overview
|
||||
attributes:
|
||||
label: Feature Overview
|
||||
description: A clear and concise description of what the feature request is.
|
||||
placeholder: I would like this project to be able to do "X".
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives Considered
|
||||
description: Detail any potential alternative solutions/workarounds you've used or considered.
|
||||
placeholder: I have done/might be able to do "X" in this project by doing "Y".
|
||||
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other context about the problem here.
|
||||
placeholder: Feel free to add any other context/information/screenshots/etc... that you think might be relevant to this feature request here.
|
||||
@@ -1,10 +1,10 @@
|
||||
### Proposed changes
|
||||
|
||||
Describe the use case and detail of the change.
|
||||
Describe the use case and detail of the change. If this PR addresses an issue on GitHub, make sure to include a link to that issue using one of the [supported keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) in this PR's description or commit message.
|
||||
|
||||
If this pull request addresses an issue on GitHub, make sure to reference that
|
||||
issue using one of the
|
||||
[supported keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue).
|
||||
### Checklist
|
||||
|
||||
Before creating a pull request, make sure to comply with
|
||||
[`Contributing Guidelines`](/CONTRIBUTING.md).
|
||||
Before creating a PR, run through this checklist and mark each as complete:
|
||||
|
||||
- [ ] I have read the [contributing guidelines](/CONTRIBUTING.md).
|
||||
- [ ] I have checked that NGINX compiles and runs after adding my changes.
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: F5 CLA
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_target:
|
||||
types: [opened, closed, synchronize]
|
||||
permissions: read-all
|
||||
jobs:
|
||||
f5-cla:
|
||||
name: F5 CLA
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
actions: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
steps:
|
||||
- name: Run F5 Contributor License Agreement (CLA) assistant
|
||||
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have hereby read the F5 CLA and agree to its terms') || github.event_name == 'pull_request_target'
|
||||
uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1
|
||||
with:
|
||||
# Path to the CLA document.
|
||||
path-to-document: https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md
|
||||
# Custom CLA messages.
|
||||
custom-notsigned-prcomment: '🎉 Thank you for your contribution! It appears you have not yet signed the [F5 Contributor License Agreement (CLA)](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md), which is required for your changes to be incorporated into an F5 Open Source Software (OSS) project. Please kindly read the [F5 CLA](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md) and reply on a new comment with the following text to agree:'
|
||||
custom-pr-sign-comment: 'I have hereby read the F5 CLA and agree to its terms'
|
||||
custom-allsigned-prcomment: '✅ All required contributors have signed the F5 CLA for this PR. Thank you!'
|
||||
# Remote repository storing CLA signatures.
|
||||
remote-organization-name: f5
|
||||
remote-repository-name: f5-cla-data
|
||||
# Branch where CLA signatures are stored.
|
||||
branch: main
|
||||
path-to-signatures: signatures/signatures.json
|
||||
# Comma separated list of usernames for maintainers or any other individuals who should not be prompted for a CLA.
|
||||
# NOTE: You will want to edit the usernames to suit your project needs.
|
||||
allowlist: bot*
|
||||
# Do not lock PRs after a merge.
|
||||
lock-pullrequest-aftermerge: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
PERSONAL_ACCESS_TOKEN: ${{ secrets.F5_CLA_TOKEN }}
|
||||
+24
-11
@@ -5,34 +5,34 @@ We really appreciate that you are considering contributing!
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Ask a Question](#ask-a-question)
|
||||
- [Report a Bug](#report-a-bug)
|
||||
- [Suggest a Feature or Enhancement](#suggest-a-feature-or-enhancement)
|
||||
- [Open a Discussion](#open-a-discussion)
|
||||
- [Submit a Pull Request](#submit-a-pull-request)
|
||||
- [Issue Lifecycle](#issue-lifecycle)
|
||||
|
||||
## Ask a Question
|
||||
|
||||
To ask a question, open an issue on GitHub with the label `question`.
|
||||
|
||||
## Report a Bug
|
||||
|
||||
To report a bug, open an issue on GitHub with the label `bug` using the
|
||||
available bug report issue template. Before reporting a bug, make sure the
|
||||
issue has not already been reported.
|
||||
available [bug report issue form](/.github/ISSUE_TEMPLATE/bug_report.yml).
|
||||
Please ensure the bug has not already been reported. **If the bug is a
|
||||
potential security vulnerability, please report it using our
|
||||
[security policy](/SECURITY.md).**
|
||||
|
||||
## Suggest a Feature or Enhancement
|
||||
|
||||
To suggest a feature or enhancement, open an issue on GitHub with the label
|
||||
`feature` or `enhancement` using the available feature request issue template.
|
||||
To suggest a feature or enhancement, please create an issue on GitHub with the
|
||||
label `enhancement` using the available
|
||||
[feature request issue form](/.github/ISSUE_TEMPLATE/feature_request.yml).
|
||||
Please ensure the feature or enhancement has not already been suggested.
|
||||
|
||||
## Open a Discussion
|
||||
|
||||
If you want to engage in a conversation with the community and maintainers,
|
||||
we encourage you to use
|
||||
[GitHub Discussions](https://github.com/nginx/nginx/discussions).
|
||||
[GitHub Discussions](https://github.com/nginx/nginx/discussions) to discuss
|
||||
the NGINX codebase or the [NGINX Community forum](https://community.nginx.org)
|
||||
to chat anything else NGINX (including troubleshooting).
|
||||
|
||||
## Submit a Pull Request
|
||||
|
||||
@@ -89,7 +89,20 @@ git clone https://github.com/nginx/nginx-tests.git
|
||||
```
|
||||
|
||||
- Submitting a change implies granting project a permission to use it under the
|
||||
[BSD-2-Clause license](https://github.com/nginx/nginx/blob/master/LICENSE)
|
||||
[BSD-2-Clause license](/LICENSE)
|
||||
|
||||
### F5 Contributor License Agreement (CLA)
|
||||
|
||||
F5 requires all contributors to agree to the terms of the F5 CLA
|
||||
(available [here](https://github.com/f5/f5-cla/blob/main/docs/f5_cla.md))
|
||||
before any of their changes can be incorporated into an F5 Open Source
|
||||
repository (even contributions to the F5 CLA itself!).
|
||||
|
||||
If you have not yet agreed to the F5 CLA terms and submit a PR to this
|
||||
repository, a bot will prompt you to view and agree to the F5 CLA.
|
||||
You will have to agree to the F5 CLA terms through a comment in the PR
|
||||
before any of your changes can be merged. Your agreement signature
|
||||
will be safely stored by F5 and no longer be required in future PRs.
|
||||
|
||||
## Issue Lifecycle
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Copyright (C) 2002-2021 Igor Sysoev
|
||||
Copyright (C) 2011-2024 Nginx, Inc.
|
||||
Copyright (C) 2011-2025 Nginx, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/9335b488-ffcc-4157-8364-2370a0b70ad0">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://github.com/user-attachments/assets/3a7eeb08-1133-47f5-859c-fad4f5a6a013">
|
||||
<img alt="NGINX Banner">
|
||||
</picture>
|
||||
|
||||
NGINX (pronounced "engine x" or "en-jin-eks") is the world's most popular Web Server, high performance Load Balancer, Reverse Proxy, API Gateway and Content Cache.
|
||||
|
||||
NGINX is free and open source software, distributed under the terms of a simplified [2-clause BSD-like license](LICENSE).
|
||||
|
||||
Enterprise distributions, commercial support and training are available from [F5, Inc](https://www.f5.com/products/nginx).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The goal of this README is to provide a basic, structured introduction to NGINX for novice users. Please refer to the [full NGINX documentation](https://nginx.org/en/docs/) for detailed information on [installing](https://nginx.org/en/docs/install.html), [building](https://nginx.org/en/docs/configure.html), [configuring](https://nginx.org/en/docs/dirindex.html), [debugging](https://nginx.org/en/docs/debugging_log.html), and more. These documentation pages also contain a more detailed [Beginners Guide](https://nginx.org/en/docs/beginners_guide.html), How-Tos, [Development guide](https://nginx.org/en/docs/dev/development_guide.html), and a complete module and [directive reference](https://nginx.org/en/docs/dirindex.html).
|
||||
|
||||
# Table of contents
|
||||
- [How it works](#how-it-works)
|
||||
- [Modules](#modules)
|
||||
- [Configurations](#configurations)
|
||||
- [Runtime](#runtime)
|
||||
- [Downloading and installing](#downloading-and-installing)
|
||||
- [Stable and Mainline binaries](#stable-and-mainline-binaries)
|
||||
- [Linux binary installation process](#linux-binary-installation-process)
|
||||
- [FreeBSD installation process](#freebsd-installation-process)
|
||||
- [Windows executables](#windows-executables)
|
||||
- [Dynamic modules](#dynamic-modules)
|
||||
- [Getting started with NGINX](#getting-started-with-nginx)
|
||||
- [Installing SSL certificates and enabling TLS encryption](#installing-ssl-certificates-and-enabling-tls-encryption)
|
||||
- [Load Balancing](#load-balancing)
|
||||
- [Rate limiting](#rate-limiting)
|
||||
- [Content caching](#content-caching)
|
||||
- [Building from source](#building-from-source)
|
||||
- [Installing dependencies](#installing-dependencies)
|
||||
- [Cloning the NGINX GitHub repository](#cloning-the-nginx-github-repository)
|
||||
- [Configuring the build](#configuring-the-build)
|
||||
- [Compiling](#compiling)
|
||||
- [Location of binary and installation](#location-of-binary-and-installation)
|
||||
- [Running and testing the installed binary](#running-and-testing-the-installed-binary)
|
||||
- [Asking questions and reporting issues](#asking-questions-and-reporting-issues)
|
||||
- [Contributing code](#contributing-code)
|
||||
- [Additional help and resources](#additional-help-and-resources)
|
||||
- [Changelog](#changelog)
|
||||
- [License](#license)
|
||||
|
||||
# How it works
|
||||
NGINX is installed software with binary packages available for all major operating systems and Linux distributions. See [Tested OS and Platforms](https://nginx.org/en/#tested_os_and_platforms) for a full list of compatible systems.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> While nearly all popular Linux-based operating systems are distributed with a community version of nginx, we highly advise installation and usage of official [packages](https://nginx.org/en/linux_packages.html) or sources from this repository. Doing so ensures that you're using the most recent release or source code, including the latest feature-set, fixes and security patches.
|
||||
|
||||
## Modules
|
||||
NGINX is comprised of individual modules, each extending core functionality by providing additional, configurable features. See "Modules reference" at the bottom of [nginx documentation](https://nginx.org/en/docs/) for a complete list of official modules.
|
||||
|
||||
NGINX modules can be built and distributed as static or dynamic modules. Static modules are defined at build-time, compiled, and distributed in the resulting binaries. See [Dynamic Modules](#dynamic-modules) for more information on how they work, as well as, how to obtain, install, and configure them.
|
||||
|
||||
> [!TIP]
|
||||
> You can issue the following command to see which static modules your NGINX binaries were built with:
|
||||
```bash
|
||||
nginx -V
|
||||
```
|
||||
> See [Configuring the build](#configuring-the-build) for information on how to include specific Static modules into your nginx build.
|
||||
|
||||
|
||||
## Configurations
|
||||
NGINX is highly flexible and configurable. Provisioning the software is achieved via text-based config file(s) accepting parameters called "[Directives](https://nginx.org/en/docs/dirindex.html)". See [Configuration File's Structure](https://nginx.org/en/docs/beginners_guide.html#conf_structure) for a comprehensive description of how NGINX configuration files work.
|
||||
|
||||
> [!NOTE]
|
||||
> The set of directives available to your distribution of NGINX is dependent on which [modules](#modules) have been made available to it.
|
||||
|
||||
## Runtime
|
||||
Rather than running in a single, monolithic process, NGINX is architected to scale beyond Operating System process limitations by operating as a collection of processes. They include:
|
||||
- A "master" process that maintains worker processes, as well as, reads and evaluates configuration files.
|
||||
- One or more "worker" processes that process data (eg. HTTP requests).
|
||||
|
||||
The number of [worker processes](https://nginx.org/en/docs/ngx_core_module.html#worker_processes) is defined in the configuration file and may be fixed for a given configuration or automatically adjusted to the number of available CPU cores. In most cases, the latter option optimally balances load across available system resources, as NGINX is designed to efficiently distribute work across all worker processes.
|
||||
|
||||
> [!TIP]
|
||||
> Processes synchronize data through shared memory. For this reason, many NGINX directives require the allocation of shared memory zones. As an example, when configuring [rate limiting](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req), connecting clients may need to be tracked in a [common memory zone](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_zone) so all worker processes can know how many times a particular client has accessed the server in a span of time.
|
||||
|
||||
# Downloading and installing
|
||||
Follow these steps to download and install precompiled NGINX binaries. You may also choose to [build NGINX locally from source code](#building-from-source).
|
||||
|
||||
## Stable and Mainline binaries
|
||||
NGINX binaries are built and distributed in two versions: stable and mainline. Stable binaries are built from stable branches and only contain critical fixes backported from the mainline version. Mainline binaries are built from the [master branch](https://github.com/nginx/nginx/tree/master) and contain the latest features and bugfixes. You'll need to [decide which is appropriate for your purposes](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/#choosing-between-a-stable-or-a-mainline-version).
|
||||
|
||||
## Linux binary installation process
|
||||
The NGINX binary installation process takes advantage of package managers native to specific Linux distributions. For this reason, first-time installations involve adding the official NGINX package repository to your system's package manager. Follow [these steps](https://nginx.org/en/linux_packages.html) to download, verify, and install NGINX binaries using the package manager appropriate for your Linux distribution.
|
||||
|
||||
### Upgrades
|
||||
Future upgrades to the latest version can be managed using the same package manager without the need to manually download and verify binaries.
|
||||
|
||||
## FreeBSD installation process
|
||||
For more information on installing NGINX on FreeBSD system, visit https://nginx.org/en/docs/install.html
|
||||
|
||||
## Windows executables
|
||||
Windows executables for mainline and stable releases can be found on the main [NGINX download page](https://nginx.org/en/download.html). Note that the current implementation of NGINX for Windows is at the Proof-of-Concept stage and should only be used for development and testing purposes. For additional information, please see [nginx for Windows](https://nginx.org/en/docs/windows.html).
|
||||
|
||||
## Dynamic modules
|
||||
NGINX version 1.9.11 added support for [Dynamic Modules](https://nginx.org/en/docs/ngx_core_module.html#load_module). Unlike Static modules, dynamically built modules can be downloaded, installed, and configured after the core NGINX binaries have been built. [Official dynamic module binaries](https://nginx.org/en/linux_packages.html#dynmodules) are available from the same package repository as the core NGINX binaries described in previous steps.
|
||||
|
||||
> [!TIP]
|
||||
> [NGINX JavaScript (njs)](https://github.com/nginx/njs), is a popular NGINX dynamic module that enables the extension of core NGINX functionality using familiar JavaScript syntax.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If desired, dynamic modules can also be built statically into NGINX at compile time.
|
||||
|
||||
# Getting started with NGINX
|
||||
For a gentle introduction to NGINX basics, please see our [Beginner’s Guide](https://nginx.org/en/docs/beginners_guide.html).
|
||||
|
||||
## Installing SSL certificates and enabling TLS encryption
|
||||
See [Configuring HTTPS servers](https://nginx.org/en/docs/http/configuring_https_servers.html) for a quick guide on how to enable secure traffic to your NGINX installation.
|
||||
|
||||
## Load Balancing
|
||||
For a quick start guide on configuring NGINX as a Load Balancer, please see [Using nginx as HTTP load balancer](https://nginx.org/en/docs/http/load_balancing.html).
|
||||
|
||||
## Rate limiting
|
||||
See our [Rate Limiting with NGINX](https://blog.nginx.org/blog/rate-limiting-nginx) blog post for an overview of core concepts for provisioning NGINX as an API Gateway.
|
||||
|
||||
## Content caching
|
||||
See [A Guide to Caching with NGINX and NGINX Plus](https://blog.nginx.org/blog/nginx-caching-guide) blog post for an overview of how to use NGINX as a content cache (e.g. edge server of a content delivery network).
|
||||
|
||||
# Building from source
|
||||
The following steps can be used to build NGINX from source code available in this repository.
|
||||
|
||||
## Installing dependencies
|
||||
Most Linux distributions will require several dependencies to be installed in order to build NGINX. The following instructions are specific to the `apt` package manager, widely available on most Ubuntu/Debian distributions and their derivatives.
|
||||
|
||||
> [!TIP]
|
||||
> It is always a good idea to update your package repository lists prior to installing new packages.
|
||||
> ```bash
|
||||
> sudo apt update
|
||||
> ```
|
||||
|
||||
### Installing compiler and make utility
|
||||
Use the following command to install the GNU C compiler and Make utility.
|
||||
|
||||
```bash
|
||||
sudo apt install gcc make
|
||||
```
|
||||
|
||||
### Installing dependency libraries
|
||||
|
||||
```bash
|
||||
sudo apt install libpcre3-dev zlib1g-dev
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> This is the minimal set of dependency libraries needed to build NGINX with rewriting and gzip capabilities. Other dependencies may be required if you choose to build NGINX with additional modules. Monitor the output of the `configure` command discussed in the following sections for information on which modules may be missing. For example, if you plan to use SSL certificates to encrypt traffic with TLS, you'll need to install the OpenSSL library. To do so, issue the following command.
|
||||
|
||||
>```bash
|
||||
>sudo apt install libssl-dev
|
||||
|
||||
## Cloning the NGINX GitHub repository
|
||||
Using your preferred method, clone the NGINX repository into your development directory. See [Cloning a GitHub Repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) for additional help.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/nginx/nginx.git
|
||||
```
|
||||
|
||||
## Configuring the build
|
||||
Prior to building NGINX, you must run the `configure` script with [appropriate flags](https://nginx.org/en/docs/configure.html). This will generate a Makefile in your NGINX source root directory that can then be used to compile NGINX with [options specified during configuration](https://nginx.org/en/docs/configure.html).
|
||||
|
||||
From the NGINX source code repository's root directory:
|
||||
|
||||
```bash
|
||||
auto/configure
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Configuring the build without any flags will compile NGINX with the default set of options. Please refer to https://nginx.org/en/docs/configure.html for a full list of available build configuration options.
|
||||
|
||||
## Compiling
|
||||
The `configure` script will generate a `Makefile` in the NGINX source root directory upon successful execution. To compile NGINX into a binary, issue the following command from that same directory:
|
||||
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
## Location of binary and installation
|
||||
After successful compilation, a binary will be generated at `<NGINX_SRC_ROOT_DIR>/objs/nginx`. To install this binary, issue the following command from the source root directory:
|
||||
|
||||
```bash
|
||||
sudo make install
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The binary will be installed into the `/usr/local/nginx/` directory.
|
||||
|
||||
## Running and testing the installed binary
|
||||
To run the installed binary, issue the following command:
|
||||
|
||||
```bash
|
||||
sudo /usr/local/nginx/sbin/nginx
|
||||
```
|
||||
|
||||
You may test NGINX operation using `curl`.
|
||||
|
||||
```bash
|
||||
curl localhost
|
||||
```
|
||||
|
||||
The output of which should start with:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
```
|
||||
|
||||
# Asking questions and reporting issues
|
||||
We encourage you to engage with us.
|
||||
- [NGINX GitHub Discussions](https://github.com/nginx/nginx/discussions), is the go-to place to start asking questions and sharing your thoughts.
|
||||
- Our [GitHub Issues](https://github.com/nginx/nginx/issues) page offers space to submit and discuss specific issues, report bugs, and suggest enhancements.
|
||||
|
||||
# Contributing code
|
||||
Please see the [Contributing](CONTRIBUTING.md) guide for information on how to contribute code.
|
||||
|
||||
# Additional help and resources
|
||||
- See the [NGINX Community Blog](https://blog.nginx.org/) for more tips, tricks and HOW-TOs related to NGINX and related projects.
|
||||
- Access [nginx.org](https://nginx.org/), your go-to source for all documentation, information and software related to the NGINX suite of projects.
|
||||
|
||||
# Changelog
|
||||
See our [changelog](https://nginx.org/en/CHANGES) to keep track of updates.
|
||||
|
||||
# License
|
||||
[2-clause BSD-like license](LICENSE)
|
||||
|
||||
---
|
||||
Additional documentation available at: https://nginx.org/en/docs
|
||||
+88
-5
@@ -1,8 +1,8 @@
|
||||
# Security Policy
|
||||
|
||||
## Latest Versions
|
||||
|
||||
We advise users to run the most recent mainline or stable release of nginx.
|
||||
This document provides an overview of security concerns related to nginx
|
||||
deployments, focusing on confidentiality, integrity, availability, and the
|
||||
implications of configurations and misconfigurations.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
@@ -10,11 +10,94 @@ Please report any vulnerabilities via one of the following methods
|
||||
(in order of preference):
|
||||
|
||||
1. [Report a vulnerability](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability)
|
||||
within this repository. We are using the Github workflow that allows us to
|
||||
manage vulnerabilities in a private manner and to interact with reporters
|
||||
within this repository. We are using the GitHub workflow that allows us to
|
||||
manage vulnerabilities in a private manner and interact with reporters
|
||||
securely.
|
||||
|
||||
2. [Report directly to F5](https://www.f5.com/services/support/report-a-vulnerability).
|
||||
|
||||
3. Report via email to security-alert@nginx.org.
|
||||
This method will be deprecated in the future.
|
||||
|
||||
### Vulnerability Disclosure and Fix Process
|
||||
|
||||
The nginx team expects that all suspected vulnerabilities be reported
|
||||
privately via the
|
||||
[Reporting a Vulnerability](SECURITY.md#reporting-a-vulnerability) guidelines.
|
||||
If a publicly released vulnerability is reported, we
|
||||
may request to handle it according to the private disclosure process.
|
||||
If the reporter agrees, we will follow the private disclosure process.
|
||||
|
||||
Security fixes will be applied to all supported stable releases, as well
|
||||
as the mainline version, as applicable. We recommend using the most recent
|
||||
mainline or stable release of nginx. Fixes are created and tested by the core
|
||||
team using a GitHub private fork for security. If necessary, the reporter
|
||||
may be invited to contribute to the fork and assist with the solution.
|
||||
|
||||
The nginx team is committed to responsible information disclosure with
|
||||
sufficient detail, such as the CVSS score and vector. Privately disclosed
|
||||
vulnerabilities are embargoed by default until the fix is released.
|
||||
Communications and fixes remain private until made public. As nginx is
|
||||
supported by F5, we generally follow the
|
||||
[F5 security vulnerability response policy](https://my.f5.com/manage/s/article/K4602).
|
||||
|
||||
### Vulnerability Disclosure and Fix Service Level Objectives
|
||||
|
||||
- We will acknowledge all vulnerability reports within 1 to 3 days.
|
||||
- Fixes will be developed and released within 90 days from the date of
|
||||
disclosure. If an extension is needed, we will work with the disclosing person.
|
||||
- Publicly disclosed (i.e., Zero-Day vulnerabilities) will be addressed ASAP.
|
||||
|
||||
## Confidentiality, Integrity, and Availability
|
||||
|
||||
### Confidentiality and Integrity
|
||||
|
||||
Vulnerabilities compromising data confidentiality or integrity are considered
|
||||
the highest priority. Any issue leading to unauthorized data access, leaks, or
|
||||
manipulation will trigger the security release process.
|
||||
|
||||
### Availability
|
||||
|
||||
Availability issues must meet the following criteria to trigger the security
|
||||
release process:
|
||||
- Is present in a standard module included with nginx.
|
||||
- Arises from traffic that the module is designed to handle.
|
||||
- Resource exhaustion issues are not mitigated by existing timeout, rate
|
||||
limiting, or buffer size configurations, or applying changes is impractical.
|
||||
- Results in highly asymmetric, extreme resource consumption.
|
||||
|
||||
Availability issues excluded from the security release process:
|
||||
- Local file content or upstream response content resulting only in worker
|
||||
process termination.
|
||||
- Issues with experimental features which result only in availability impact.
|
||||
|
||||
## Trusted Configurations and Misconfigurations
|
||||
|
||||
In nginx, configuration files, modules, certificate/key pairs, nginx JavaScript,
|
||||
and local file content are considered trusted sources. Issues arising from
|
||||
loading or execution of these trusted components are not considered
|
||||
vulnerabilities. Operators are responsible for securing and maintaining the
|
||||
integrity of these sources. Misconfigurations can create vulnerabilities, and
|
||||
operators should implement configurations according to best practices, review
|
||||
them regularly, and apply security updates.
|
||||
|
||||
## Data Plane vs. Control Plane
|
||||
|
||||
The data plane handles traffic through nginx, directly interacting with user
|
||||
data. nginx inherently trusts the content and instructions from upstream
|
||||
servers. The control plane governs configuration, management, and orchestration.
|
||||
Misconfigurations or vulnerabilities in the control plane can cause improper
|
||||
behavior in the data plane.
|
||||
|
||||
## Modules Under Scope
|
||||
|
||||
The policy applies to all nginx modules included in this repository. Security
|
||||
considerations and attack vectors for each module will be identified, with
|
||||
recommended configurations to mitigate risks.
|
||||
|
||||
## Debug Logging and Core Files
|
||||
|
||||
Debug logs and core files produced by nginx may contain un-sanitized data,
|
||||
including sensitive information like client requests, server configurations,
|
||||
and private key material. These artifacts must be handled carefully to avoid
|
||||
exposing confidential data.
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# Support
|
||||
|
||||
## Ask a Question
|
||||
|
||||
We use GitHub issues for tracking bugs and feature requests
|
||||
related to this project.
|
||||
|
||||
If you don't know how something in the codebase works, are curious if NGINX
|
||||
is capable of achieving your desired functionality or want to discuss the
|
||||
implementation of an existing or in development feature, please start a
|
||||
GitHub discussion!
|
||||
|
||||
## NGINX Specific Questions and/or Issues
|
||||
|
||||
This project isn't the right place to get support for NGINX and/or NGINX
|
||||
troubleshooting questions, but the following resources are available below.
|
||||
Thanks for your understanding!
|
||||
|
||||
### Community Forum
|
||||
|
||||
We have a [community forum](https://community.nginx.org/)!
|
||||
If you have any NGINX specific questions and/or issues,
|
||||
try checking out the [`NGINX category`](https://community.nginx.org/c/projects/nginx/23).
|
||||
For general discussions around anything tangentially NGINX related,
|
||||
check out the [`General Discussion category`](https://community.nginx.org/c/general-discussion/34).
|
||||
Both fellow community members and NGINXers might be able to help you! :)
|
||||
|
||||
### Documentation
|
||||
|
||||
For a comprehensive list of all NGINX directives, check out <https://nginx.org>.
|
||||
|
||||
For a comprehensive list of administration and deployment guides for all
|
||||
NGINX products, check out <https://docs.nginx.com>.
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see the [contributing guide](/CONTRIBUTING.md) for guidelines
|
||||
on how to best contribute to this project.
|
||||
|
||||
## Commercial Support
|
||||
|
||||
Commercial support for this project is available.
|
||||
Please get in touch with [F5 sales](https://www.f5.com/products/get-f5/)
|
||||
or check your contract details for more information!
|
||||
|
||||
## Community Support
|
||||
|
||||
Community support is offered on a best effort basis through any of our active communities.
|
||||
@@ -5,15 +5,6 @@
|
||||
# clang
|
||||
|
||||
|
||||
NGX_CLANG_VER=`$CC -v 2>&1 | grep 'version' 2>&1 \
|
||||
| sed -n -e 's/^.*clang version \(.*\)/\1/p' \
|
||||
-e 's/^.*LLVM version \(.*\)/\1/p'`
|
||||
|
||||
echo " + clang version: $NGX_CLANG_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"clang $NGX_CLANG_VER\"" . auto/define
|
||||
|
||||
|
||||
CC_TEST_FLAGS="-pipe"
|
||||
|
||||
|
||||
|
||||
+2
-10
@@ -8,14 +8,6 @@
|
||||
# 4.0.0, 4.0.1, 4.1.0
|
||||
|
||||
|
||||
NGX_GCC_VER=`$CC -v 2>&1 | grep 'gcc version' 2>&1 \
|
||||
| sed -e 's/^.* version \(.*\)/\1/'`
|
||||
|
||||
echo " + gcc version: $NGX_GCC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"gcc $NGX_GCC_VER\"" . auto/define
|
||||
|
||||
|
||||
# Solaris 7's /usr/ccs/bin/as does not support "-pipe"
|
||||
|
||||
CC_TEST_FLAGS="-pipe"
|
||||
@@ -112,7 +104,7 @@ esac
|
||||
|
||||
CC_AUX_FLAGS="$CC_AUX_FLAGS $CPU_OPT"
|
||||
|
||||
case "$NGX_GCC_VER" in
|
||||
case "$NGX_CC_VER" in
|
||||
2.7*)
|
||||
# batch build
|
||||
CPU_OPT=
|
||||
@@ -145,7 +137,7 @@ CFLAGS="$CFLAGS -Wall -Wpointer-arith"
|
||||
#CFLAGS="$CFLAGS -Winline"
|
||||
#CFLAGS="$CFLAGS -Wmissing-prototypes"
|
||||
|
||||
case "$NGX_GCC_VER" in
|
||||
case "$NGX_CC_VER" in
|
||||
2.*)
|
||||
# we have a lot of the unused function arguments
|
||||
CFLAGS="$CFLAGS -Wno-unused"
|
||||
|
||||
+1
-9
@@ -5,14 +5,6 @@
|
||||
|
||||
# Intel C++ compiler 7.1, 8.0, 8.1, 9.0, 11.1
|
||||
|
||||
NGX_ICC_VER=`$CC -V 2>&1 | grep 'Version' 2>&1 \
|
||||
| sed -e 's/^.* Version \([^ ]*\) *Build.*$/\1/'`
|
||||
|
||||
echo " + icc version: $NGX_ICC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"Intel C Compiler $NGX_ICC_VER\"" . auto/define
|
||||
|
||||
|
||||
# optimizations
|
||||
|
||||
CFLAGS="$CFLAGS -O"
|
||||
@@ -83,7 +75,7 @@ CFLAGS="$CFLAGS -wd1418"
|
||||
# external declaration in primary source file
|
||||
CFLAGS="$CFLAGS -wd1419"
|
||||
|
||||
case "$NGX_ICC_VER" in
|
||||
case "$NGX_CC_VER" in
|
||||
9.*)
|
||||
# "cc" clobber ignored, warnings for Linux's htonl()/htons()
|
||||
CFLAGS="$CFLAGS -wd1469"
|
||||
|
||||
+6
-10
@@ -11,20 +11,16 @@
|
||||
# MSVC 2015 (14.0) cl 19.00
|
||||
|
||||
|
||||
NGX_MSVC_VER=`$NGX_WINE $CC 2>&1 | grep 'C/C++.* [0-9][0-9]*\.[0-9]' 2>&1 \
|
||||
| sed -e 's/^.* \([0-9][0-9]*\.[0-9].*\)/\1/'`
|
||||
|
||||
echo " + cl version: $NGX_MSVC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"cl $NGX_MSVC_VER\"" . auto/define
|
||||
|
||||
|
||||
ngx_msvc_ver=`echo $NGX_MSVC_VER | sed -e 's/^\([0-9]*\).*/\1/'`
|
||||
ngx_msvc_ver=`echo $NGX_CC_VER | sed -e 's/^\([0-9]*\).*/\1/'`
|
||||
|
||||
|
||||
# detect x64 builds
|
||||
|
||||
case "$NGX_MSVC_VER" in
|
||||
case "$NGX_CC_VER" in
|
||||
|
||||
*ARM64)
|
||||
NGX_MACHINE=arm64
|
||||
;;
|
||||
|
||||
*x64)
|
||||
NGX_MACHINE=amd64
|
||||
|
||||
@@ -28,6 +28,13 @@ if [ "$CC" = cl ]; then
|
||||
NGX_CC_NAME=msvc
|
||||
echo " + using Microsoft Visual C++ compiler"
|
||||
|
||||
NGX_CC_VER=`$NGX_WINE $CC 2>&1 \
|
||||
| grep 'C/C++.* [0-9][0-9]*\.[0-9]' 2>&1 \
|
||||
| sed -e 's/^.* \([0-9][0-9]*\.[0-9].*\)/\1/'`
|
||||
echo " + cl version: $NGX_CC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"cl $NGX_CC_VER\"" . auto/define
|
||||
|
||||
elif [ "$CC" = wcl386 ]; then
|
||||
NGX_CC_NAME=owc
|
||||
echo " + using Open Watcom C compiler"
|
||||
@@ -40,22 +47,48 @@ elif `$CC -V 2>&1 | grep '^Intel(R) C' >/dev/null 2>&1`; then
|
||||
NGX_CC_NAME=icc
|
||||
echo " + using Intel C++ compiler"
|
||||
|
||||
NGX_CC_VER=`$CC -V 2>&1 \
|
||||
| sed -n -e 's/^.* Version \([^ ]*\) *Build.*$/\1/p'`
|
||||
echo " + icc version: $NGX_CC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"Intel C Compiler $NGX_CC_VER\"" . auto/define
|
||||
|
||||
elif `$CC -v 2>&1 | grep 'gcc version' >/dev/null 2>&1`; then
|
||||
NGX_CC_NAME=gcc
|
||||
echo " + using GNU C compiler"
|
||||
|
||||
NGX_CC_VER=`$CC -v 2>&1 | sed -n -e 's/^.*gcc version \(.*\)/\1/p'`
|
||||
echo " + gcc version: $NGX_CC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"gcc $NGX_CC_VER\"" . auto/define
|
||||
|
||||
elif `$CC -v 2>&1 | grep 'clang version' >/dev/null 2>&1`; then
|
||||
NGX_CC_NAME=clang
|
||||
echo " + using Clang C compiler"
|
||||
|
||||
NGX_CC_VER=`$CC -v 2>&1 | sed -n -e 's/^.*clang version \(.*\)/\1/p'`
|
||||
echo " + clang version: $NGX_CC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"clang $NGX_CC_VER\"" . auto/define
|
||||
|
||||
elif `$CC -v 2>&1 | grep 'LLVM version' >/dev/null 2>&1`; then
|
||||
NGX_CC_NAME=clang
|
||||
echo " + using Clang C compiler"
|
||||
|
||||
NGX_CC_VER=`$CC -v 2>&1 | sed -n -e 's/^.*LLVM version \(.*\)/\1/p'`
|
||||
echo " + clang version: $NGX_CC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"clang $NGX_CC_VER\"" . auto/define
|
||||
|
||||
elif `$CC -V 2>&1 | grep 'Sun C' >/dev/null 2>&1`; then
|
||||
NGX_CC_NAME=sunc
|
||||
echo " + using Sun C compiler"
|
||||
|
||||
NGX_CC_VER=`$CC -V 2>&1 | sed -n -e 's/^.* Sun C \(.*\)/\1/p'`
|
||||
echo " + Sun C version: $NGX_CC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"Sun C $NGX_CC_VER\"" . auto/define
|
||||
|
||||
elif `$CC -V 2>&1 | grep '^Compaq C' >/dev/null 2>&1`; then
|
||||
NGX_CC_NAME=ccc
|
||||
echo " + using Compaq C compiler"
|
||||
|
||||
@@ -13,13 +13,6 @@
|
||||
# Sun C 5.13 SunOS_i386 2014/10/20 Oracle Solaris Studio 12.4
|
||||
# Sun C 5.14 SunOS_i386 2016/05/31 Oracle Developer Studio 12.5
|
||||
|
||||
NGX_SUNC_VER=`$CC -V 2>&1 | grep 'Sun C' 2>&1 \
|
||||
| sed -e 's/^.* Sun C \(.*\)/\1/'`
|
||||
|
||||
echo " + Sun C version: $NGX_SUNC_VER"
|
||||
|
||||
have=NGX_COMPILER value="\"Sun C $NGX_SUNC_VER\"" . auto/define
|
||||
|
||||
|
||||
cat << END > $NGX_AUTOTEST.c
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ if [ $NGX_LIBATOMIC != YES ]; then
|
||||
|
||||
have=NGX_HAVE_LIBATOMIC . auto/have
|
||||
CORE_INCS="$CORE_INCS $NGX_LIBATOMIC/src"
|
||||
LINK_DEPS="$LINK_DEPS $NGX_LIBATOMIC/src/libatomic_ops.a"
|
||||
CORE_LIBS="$CORE_LIBS $NGX_LIBATOMIC/src/libatomic_ops.a"
|
||||
LINK_DEPS="$LINK_DEPS $NGX_LIBATOMIC/build/lib/libatomic_ops.a"
|
||||
CORE_LIBS="$CORE_LIBS $NGX_LIBATOMIC/build/lib/libatomic_ops.a"
|
||||
|
||||
else
|
||||
|
||||
|
||||
@@ -3,14 +3,19 @@
|
||||
# Copyright (C) Nginx, Inc.
|
||||
|
||||
|
||||
case $NGX_LIBATOMIC in
|
||||
/*) ngx_prefix="$NGX_LIBATOMIC/build" ;;
|
||||
*) ngx_prefix="$PWD/$NGX_LIBATOMIC/build" ;;
|
||||
esac
|
||||
|
||||
cat << END >> $NGX_MAKEFILE
|
||||
|
||||
$NGX_LIBATOMIC/src/libatomic_ops.a: $NGX_LIBATOMIC/Makefile
|
||||
cd $NGX_LIBATOMIC && \$(MAKE)
|
||||
$NGX_LIBATOMIC/build/lib/libatomic_ops.a: $NGX_LIBATOMIC/Makefile
|
||||
cd $NGX_LIBATOMIC && \$(MAKE) && \$(MAKE) install
|
||||
|
||||
$NGX_LIBATOMIC/Makefile: $NGX_MAKEFILE
|
||||
cd $NGX_LIBATOMIC \\
|
||||
&& if [ -f Makefile ]; then \$(MAKE) distclean; fi \\
|
||||
&& ./configure
|
||||
&& ./configure --prefix=$ngx_prefix
|
||||
|
||||
END
|
||||
@@ -12,7 +12,6 @@ if [ $OPENSSL != NONE ]; then
|
||||
|
||||
if [ $USE_OPENSSL_QUIC = YES ]; then
|
||||
have=NGX_QUIC . auto/have
|
||||
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
|
||||
fi
|
||||
|
||||
case "$CC" in
|
||||
@@ -148,14 +147,18 @@ else
|
||||
|
||||
if [ $USE_OPENSSL_QUIC = YES ]; then
|
||||
|
||||
ngx_feature="OpenSSL QUIC support"
|
||||
ngx_feature="OpenSSL QUIC API"
|
||||
ngx_feature_name="NGX_QUIC"
|
||||
ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
|
||||
ngx_feature_test="SSL_set_quic_tls_cbs(NULL, NULL, NULL)"
|
||||
. auto/feature
|
||||
|
||||
if [ $ngx_found = no ]; then
|
||||
have=NGX_QUIC_OPENSSL_COMPAT . auto/have
|
||||
ngx_feature="BoringSSL-like QUIC API"
|
||||
ngx_feature_test="SSL_set_quic_method(NULL, NULL)"
|
||||
. auto/feature
|
||||
fi
|
||||
|
||||
if [ $ngx_found = no ]; then
|
||||
ngx_feature="OpenSSL QUIC compatibility"
|
||||
ngx_feature_test="SSL_CTX_add_custom_ext(NULL, 0, 0,
|
||||
NULL, NULL, NULL, NULL, NULL)"
|
||||
|
||||
@@ -13,6 +13,10 @@ case "$CC" in
|
||||
OPENSSL_TARGET=VC-WIN64A
|
||||
;;
|
||||
|
||||
arm64)
|
||||
OPENSSL_TARGET=VC-WIN64-ARM
|
||||
;;
|
||||
|
||||
*)
|
||||
OPENSSL_TARGET=VC-WIN32
|
||||
;;
|
||||
|
||||
+4
-1
@@ -36,7 +36,10 @@ if [ $PCRE_LIBRARY = PCRE2 ]; then
|
||||
pcre2_valid_utf.c \
|
||||
pcre2_xclass.c"
|
||||
|
||||
ngx_pcre_test="pcre2_convert.c \
|
||||
ngx_pcre_test="pcre2_chkdint.c \
|
||||
pcre2_compile_cgroup.c \
|
||||
pcre2_compile_class.c \
|
||||
pcre2_convert.c \
|
||||
pcre2_extuni.c \
|
||||
pcre2_find_bracket.c \
|
||||
pcre2_script_run.c \
|
||||
|
||||
+14
-2
@@ -733,7 +733,7 @@ if [ $HTTP = YES ]; then
|
||||
|
||||
ngx_module_name=ngx_http_proxy_module
|
||||
ngx_module_incs=
|
||||
ngx_module_deps=
|
||||
ngx_module_deps=src/http/modules/ngx_http_proxy_module.h
|
||||
ngx_module_srcs=src/http/modules/ngx_http_proxy_module.c
|
||||
ngx_module_libs=
|
||||
ngx_module_link=$HTTP_PROXY
|
||||
@@ -785,6 +785,17 @@ if [ $HTTP = YES ]; then
|
||||
. auto/module
|
||||
fi
|
||||
|
||||
if [ $HTTP_PROXY = YES -a $HTTP_V2 = YES ]; then
|
||||
ngx_module_name=ngx_http_proxy_v2_module
|
||||
ngx_module_incs=
|
||||
ngx_module_deps=
|
||||
ngx_module_srcs=src/http/modules/ngx_http_proxy_v2_module.c
|
||||
ngx_module_libs=
|
||||
ngx_module_link=$HTTP_V2
|
||||
|
||||
. auto/module
|
||||
fi
|
||||
|
||||
if [ $HTTP_PERL != NO ]; then
|
||||
ngx_module_name=ngx_http_perl_module
|
||||
ngx_module_incs=src/http/modules/perl
|
||||
@@ -1311,10 +1322,11 @@ fi
|
||||
|
||||
if [ $USE_OPENSSL = YES ]; then
|
||||
ngx_module_type=CORE
|
||||
ngx_module_name=ngx_openssl_module
|
||||
ngx_module_name="ngx_openssl_module ngx_openssl_cache_module"
|
||||
ngx_module_incs=
|
||||
ngx_module_deps=src/event/ngx_event_openssl.h
|
||||
ngx_module_srcs="src/event/ngx_event_openssl.c
|
||||
src/event/ngx_event_openssl_cache.c
|
||||
src/event/ngx_event_openssl_stapling.c"
|
||||
ngx_module_libs=
|
||||
ngx_module_link=YES
|
||||
|
||||
@@ -118,3 +118,19 @@ ngx_feature_libs=
|
||||
ngx_feature_test="int32_t lock = 0;
|
||||
if (!OSAtomicCompareAndSwap32Barrier(0, 1, &lock)) return 1"
|
||||
. auto/feature
|
||||
|
||||
|
||||
ngx_feature="TCP_KEEPALIVE"
|
||||
ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs="#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>"
|
||||
ngx_feature_path=
|
||||
ngx_feature_libs=
|
||||
ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPALIVE, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
|
||||
. auto/feature
|
||||
|
||||
NGX_KEEPALIVE_CHECKED=YES
|
||||
@@ -129,28 +129,8 @@ if test -z "$NGX_KQUEUE_CHECKED"; then
|
||||
fi
|
||||
|
||||
|
||||
if [ "$NGX_SYSTEM" = "NetBSD" ]; then
|
||||
|
||||
# NetBSD 2.0 incompatibly defines kevent.udata as "intptr_t"
|
||||
|
||||
cat << END >> $NGX_AUTO_CONFIG_H
|
||||
|
||||
#define NGX_KQUEUE_UDATA_T
|
||||
|
||||
END
|
||||
|
||||
else
|
||||
cat << END >> $NGX_AUTO_CONFIG_H
|
||||
|
||||
#define NGX_KQUEUE_UDATA_T (void *)
|
||||
|
||||
END
|
||||
|
||||
fi
|
||||
|
||||
|
||||
ngx_feature="crypt()"
|
||||
ngx_feature_name=
|
||||
ngx_feature_name="NGX_HAVE_CRYPT"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs=
|
||||
ngx_feature_path=
|
||||
@@ -162,7 +142,7 @@ ngx_feature_test="crypt(\"test\", \"salt\");"
|
||||
if [ $ngx_found = no ]; then
|
||||
|
||||
ngx_feature="crypt() in libcrypt"
|
||||
ngx_feature_name=
|
||||
ngx_feature_name="NGX_HAVE_CRYPT"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs=
|
||||
ngx_feature_path=
|
||||
@@ -508,18 +488,20 @@ ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_DEFER_ACCEPT, NULL, 0)"
|
||||
. auto/feature
|
||||
|
||||
|
||||
ngx_feature="TCP_KEEPIDLE"
|
||||
ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs="#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>"
|
||||
ngx_feature_path=
|
||||
ngx_feature_libs=
|
||||
ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
|
||||
. auto/feature
|
||||
if test -z "$NGX_KEEPALIVE_CHECKED"; then
|
||||
ngx_feature="TCP_KEEPIDLE"
|
||||
ngx_feature_name="NGX_HAVE_KEEPALIVE_TUNABLE"
|
||||
ngx_feature_run=no
|
||||
ngx_feature_incs="#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>"
|
||||
ngx_feature_path=
|
||||
ngx_feature_libs=
|
||||
ngx_feature_test="setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);
|
||||
setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)"
|
||||
. auto/feature
|
||||
fi
|
||||
|
||||
|
||||
ngx_feature="TCP_FASTOPEN"
|
||||
|
||||
@@ -36,8 +36,6 @@ http {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
#charset koi8-r;
|
||||
|
||||
#access_log logs/host.access.log main;
|
||||
|
||||
location / {
|
||||
|
||||
+758
-2
@@ -5,6 +5,762 @@
|
||||
<change_log title="nginx">
|
||||
|
||||
|
||||
<changes ver="1.29.4" date="2025-12-09">
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
модуль ngx_http_proxy_module поддерживает HTTP/2.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the ngx_http_proxy_module supports HTTP/2.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
поддержка расширения TLS Encrypted ClientHello
|
||||
при использовании ветки разработки ECH OpenSSL;
|
||||
директива ssl_ech_file.<br/>
|
||||
Спасибо Stephen Farrell.
|
||||
</para>
|
||||
<para lang="en">
|
||||
Encrypted ClientHello TLS extension support
|
||||
when using OpenSSL ECH feature branch;
|
||||
the "ssl_ech_file" directive.<br/>
|
||||
Thanks to Stephen Farrell.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
валидация хоста и порта в строке запроса,
|
||||
в заголовке "Host" и псевдо-заголовке ":authority"
|
||||
изменена на соответствующую RFC 3986.
|
||||
</para>
|
||||
<para lang="en">
|
||||
validation of host and port in the request line,
|
||||
"Host" header field, and ":authority" pseudo-header field
|
||||
has been changed to follow RFC 3986.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
теперь одиночный символ LF, используемый для перевода строки
|
||||
в chunked-теле запроса или ответа, считается ошибкой.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now a single LF used as a line terminator
|
||||
in a chunked request or response body is considered an error.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
при использовании HTTP/3 с OpenSSL 3.5.1 и новее
|
||||
в рабочем процессе мог произойти segmentation fault;
|
||||
ошибка появилась в 1.29.1.<br/>
|
||||
Спасибо Jan Svojanovsky.
|
||||
</para>
|
||||
<para lang="en">
|
||||
when using HTTP/3 with OpenSSL 3.5.1 or newer
|
||||
a segmentation fault might occur in a worker process;
|
||||
the bug had appeared in 1.29.1.<br/>
|
||||
Thanks to Jan Svojanovsky.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
при совместном использовании директив try_files и proxy_pass с URI
|
||||
в рабочем процессе мог произойти segmentation fault.
|
||||
</para>
|
||||
<para lang="en">
|
||||
a segmentation fault might occur in a worker process
|
||||
if the "try_files" directive and "proxy_pass" with a URI were used.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.29.3" date="2025-10-28">
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директивы add_header_inherit и add_trailer_inherit.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "add_header_inherit" and "add_trailer_inherit" directives.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
переменные $request_port и $is_request_port.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the $request_port and $is_request_port variables.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
переменные $ssl_sigalg и $ssl_client_sigalg.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the $ssl_sigalg and $ssl_client_sigalg variables.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
параметр volatile директивы geo.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "volatile" parameter of the "geo" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
теперь сжатие сертификатов доступно с BoringSSL.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now certificate compression is available with BoringSSL.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
теперь сжатие сертификатов запрещено при включенном OCSP stapling.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now certificate compression is disabled with OCSP stapling.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.29.2" date="2025-10-07">
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
теперь nginx можно собрать с AWS-LC.<br/>
|
||||
Спасибо Samuel Chiang.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now nginx can be built with AWS-LC.<br/>
|
||||
Thanks Samuel Chiang.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
теперь директива ssl_protocols работает
|
||||
в виртуальном сервере, отличном от сервера по умолчанию,
|
||||
при использовании OpenSSL 1.1.1 и новее.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now the "ssl_protocols" directive works
|
||||
in a virtual server different from the default server
|
||||
when using OpenSSL 1.1.1 or newer.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
при использовании TLSv1.3 с OpenSSL и клиентских сертификатов
|
||||
SSL handshake всегда завершался ошибкой
|
||||
при восстановлении сессии с другим значением SNI;
|
||||
ошибка появилась в 1.27.4.
|
||||
</para>
|
||||
<para lang="en">
|
||||
SSL handshake always failed
|
||||
when using TLSv1.3 with OpenSSL and client certificates
|
||||
and resuming a session with a different SNI value;
|
||||
the bug had appeared in 1.27.4.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
при использовании QUIC и директивы ssl_reject_handshake
|
||||
в логах могли появляться сообщения
|
||||
"ignoring stale global SSL error";
|
||||
ошибка появилась в 1.29.0.<br/>
|
||||
Спасибо Владимиру Хомутову.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "ignoring stale global SSL error"
|
||||
alerts might appear in logs
|
||||
when using QUIC and the "ssl_reject_handshake" directive;
|
||||
the bug had appeared in 1.29.0.<br/>
|
||||
Thanks to Vladimir Homutov.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в обработке delta-seconds
|
||||
в строке "Cache-Control" в заголовке ответа бэкенда.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in delta-seconds processing
|
||||
in the "Cache-Control" backend response header line.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
команда XCLIENT не использовала кодировку xtext.<br/>
|
||||
Спасибо Igor Morgenstern из Aisle Research.
|
||||
</para>
|
||||
<para lang="en">
|
||||
an XCLIENT command didn't use the xtext encoding.<br/>
|
||||
Thanks to Igor Morgenstern of Aisle Research.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в кешировании SSL-сертификатов во время переконфигурации.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in SSL certificate caching during reconfiguration.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.29.1" date="2025-08-13">
|
||||
|
||||
<change type="security">
|
||||
<para lang="ru">
|
||||
обработка специально созданного логина/пароля при использовании
|
||||
метода аутентификации "none" в модуле ngx_mail_smtp_module
|
||||
могла приводить к отправке серверу аутентификации
|
||||
части содержимого памяти рабочего процесса (CVE-2025-53859).
|
||||
</para>
|
||||
<para lang="en">
|
||||
processing of a specially crafted login/password when using
|
||||
the "none" authentication method in the ngx_mail_smtp_module
|
||||
might cause worker process memory disclosure
|
||||
to the authentication server (CVE-2025-53859).
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
теперь сжатие сертификатов в протоколе TLSv1.3 по умолчанию запрещено.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now TLSv1.3 certificate compression is disabled by default.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директива ssl_certificate_compression.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "ssl_certificate_compression" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
поддержка 0-RTT в QUIC при использовании OpenSSL 3.5.1 и новее.
|
||||
</para>
|
||||
<para lang="en">
|
||||
support for 0-RTT in QUIC when using OpenSSL 3.5.1 or newer.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
при использовании HTTP/2 и директивы early_hints
|
||||
ответ 103 мог буферизироваться.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the 103 response might be buffered
|
||||
when using HTTP/2 and the "early_hints" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в обработке заголовков запроса "Host" и ":authority"
|
||||
с одинаковыми значениями при использовании HTTP/2;
|
||||
ошибка появилась в 1.17.9.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in handling "Host" and ":authority" header lines
|
||||
with equal values when using HTTP/2;
|
||||
the bug had appeared in 1.17.9.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в обработке заголовка запроса "Host" с портом
|
||||
при использовании HTTP/3.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in handling "Host" header lines with a port
|
||||
when using HTTP/3.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не собирался под NetBSD 10.0.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not be built on NetBSD 10.0.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в работе параметра none директивы smtp_auth.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in the "none" parameter of the "smtp_auth" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.29.0" date="2025-06-24">
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
поддержка ответа с кодом 103 от proxy- и gRPC-бэкендов;
|
||||
директива early_hints.
|
||||
</para>
|
||||
<para lang="en">
|
||||
support for response code 103 from proxy and gRPC backends;
|
||||
the "early_hints" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
возможность загрузки секретных ключей с аппаратных устройств
|
||||
с помощью OpenSSL provider.
|
||||
</para>
|
||||
<para lang="en">
|
||||
loading of secret keys from hardware tokens
|
||||
with OpenSSL provider.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
поддержка параметра so_keepalive директивы listen на macOS.
|
||||
</para>
|
||||
<para lang="en">
|
||||
support for the "so_keepalive" parameter of the "listen" directive on macOS.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
уровень логгирования ошибок SSL в QUIC handshake
|
||||
изменён с уровня error на crit для критических ошибок
|
||||
и на info для всех остальных;
|
||||
уровень логгирования неподдерживаемых транспортных параметров QUIC
|
||||
понижен с уровня info до debug.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the logging level of SSL errors in a QUIC handshake
|
||||
has been changed from "error" to "crit" for critical errors,
|
||||
and to "info" for the rest;
|
||||
the logging level of unsupported QUIC transport parameters
|
||||
has been lowered from "info" to "debug".
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
бинарная версия nginx/Windows теперь использует для сборки Windows SDK 10.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the native nginx/Windows binary release is now built using Windows SDK 10.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не собирался gcc 15,
|
||||
если использовались модули ngx_http_v2_module и ngx_http_v3_module.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not be built by gcc 15
|
||||
if ngx_http_v2_module or ngx_http_v3_module modules were used.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx мог не собираться gcc 14 и новее с оптимизацией -O3 -flto,
|
||||
если использовался модуль ngx_http_v3_module.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx might not be built by gcc 14 or newer with -O3 -flto optimization
|
||||
if ngx_http_v3_module was used.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change>
|
||||
<para lang="ru">
|
||||
Исправления и улучшения в HTTP/3.
|
||||
</para>
|
||||
<para lang="en">
|
||||
Bugfixes and improvements in HTTP/3.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.27.5" date="2025-04-16">
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
контроль перегрузки CUBIC в соединениях QUIC.
|
||||
</para>
|
||||
<para lang="en">
|
||||
CUBIC congestion control in QUIC connections.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
ограничение на максимальный размер кешируемых в разделяемой памяти
|
||||
SSL-сессий поднято до 8192.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the maximum size limit for SSL sessions cached in shared memory
|
||||
has been raised to 8192.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в директивах grpc_ssl_password_file, proxy_ssl_password_file и
|
||||
uwsgi_ssl_password_file
|
||||
при загрузке SSL-сертификатов и зашифрованных ключей из переменных;
|
||||
ошибка появилась в 1.23.1.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in the "grpc_ssl_password_file", "proxy_ssl_password_file", and
|
||||
"uwsgi_ssl_password_file" directives
|
||||
when loading SSL certificates and encrypted keys from variables;
|
||||
the bug had appeared in 1.23.1.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в переменных $ssl_curve и $ssl_curves
|
||||
при использовании подключаемых кривых в OpenSSL.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in the $ssl_curve and $ssl_curves variables
|
||||
when using pluggable curves in OpenSSL.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не собирался с musl libc.<br/>
|
||||
Спасибо Piotr Sikora.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not be built with musl libc.<br/>
|
||||
Thanks to Piotr Sikora.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change>
|
||||
<para lang="ru">
|
||||
Улучшения производительности и исправления в HTTP/3.
|
||||
</para>
|
||||
<para lang="en">
|
||||
Performance improvements and bugfixes in HTTP/3.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.27.4" date="2025-02-05">
|
||||
|
||||
<change type="security">
|
||||
<para lang="ru">
|
||||
недостаточная проверка в обработке виртуальных серверов
|
||||
при использовании SNI в TLSv1.3 позволяла повторно использовать
|
||||
SSL-сессию в контексте другого виртуального сервера,
|
||||
чтобы обойти проверку клиентских SSL-сертификатов (CVE-2025-23419).
|
||||
</para>
|
||||
<para lang="en">
|
||||
insufficient check in virtual servers handling with TLSv1.3 SNI
|
||||
allowed to reuse SSL sessions in a different virtual server,
|
||||
to bypass client SSL certificates verification (CVE-2025-23419).
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директивы ssl_object_cache_inheritable, ssl_certificate_cache,
|
||||
proxy_ssl_certificate_cache, grpc_ssl_certificate_cache
|
||||
и uwsgi_ssl_certificate_cache.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "ssl_object_cache_inheritable", "ssl_certificate_cache",
|
||||
"proxy_ssl_certificate_cache", "grpc_ssl_certificate_cache",
|
||||
and "uwsgi_ssl_certificate_cache" directives.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директива keepalive_min_timeout.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "keepalive_min_timeout" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="workaround">
|
||||
<para lang="ru">
|
||||
при использовании zlib-ng
|
||||
в логах появлялись сообщения "gzip filter failed to use preallocated memory".
|
||||
</para>
|
||||
<para lang="en">
|
||||
"gzip filter failed to use preallocated memory" alerts appeared in logs
|
||||
when using zlib-ng.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не мог собрать библиотеку libatomic из исходных текстов,
|
||||
если использовался параметр --with-libatomic=DIR.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not build libatomic library using the library sources
|
||||
if the --with-libatomic=DIR option was used.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
могла происходить ошибка установления соединения
|
||||
при использовании 0-RTT в QUIC;
|
||||
ошибка появилась в 1.27.1.
|
||||
</para>
|
||||
<para lang="en">
|
||||
QUIC connection might not be established when using 0-RTT;
|
||||
the bug had appeared in 1.27.1.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
теперь nginx игнорирует пакеты согласования версий QUIC от клиентов.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx now ignores QUIC version negotiation packets from clients.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
nginx не собирался на Solaris 10 и более ранних
|
||||
с модулем ngx_http_v3_module.
|
||||
</para>
|
||||
<para lang="en">
|
||||
nginx could not be built on Solaris 10 and earlier
|
||||
with the ngx_http_v3_module.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change>
|
||||
<para lang="ru">
|
||||
Исправления в HTTP/3.
|
||||
</para>
|
||||
<para lang="en">
|
||||
Bugfixes in HTTP/3.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.27.3" date="2024-11-26">
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директива server в блоке upstream поддерживает
|
||||
параметр resolve.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "server" directive in the "upstream" block supports
|
||||
the "resolve" parameter.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директивы resolver и resolver_timeout в блоке upstream.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "resolver" and "resolver_timeout" directives in the "upstream" block.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
поддержка SmarterMail-специфичного режима
|
||||
IMAP LOGIN с нетегированным ответом CAPABILITY
|
||||
в почтовом прокси-сервере.
|
||||
</para>
|
||||
<para lang="en">
|
||||
SmarterMail specific mode support
|
||||
for IMAP LOGIN with untagged CAPABILITY response
|
||||
in the mail proxy module.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
теперь протоколы TLSv1 и TLSv1.1 по умолчанию запрещены.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now TLSv1 and TLSv1.1 protocols are disabled by default.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
IPv6-адрес в квадратных скобках без порта теперь можно указывать
|
||||
в директивах proxy_bind, fastcgi_bind, grpc_bind, memcached_bind,
|
||||
scgi_bind и uwsgi_bind,
|
||||
а также как адрес клиента в модуле ngx_http_realip_module.
|
||||
</para>
|
||||
<para lang="en">
|
||||
an IPv6 address in square brackets and no port can be specified
|
||||
in the "proxy_bind", "fastcgi_bind", "grpc_bind", "memcached_bind",
|
||||
"scgi_bind", and "uwsgi_bind" directives,
|
||||
and as client address in ngx_http_realip_module.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в модуле ngx_http_mp4_module.<br/>
|
||||
Спасибо Nils Bars.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in the ngx_http_mp4_module.<br/>
|
||||
Thanks to Nils Bars.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
параметр so_keepalive директивы listen
|
||||
мог работать некорректно на DragonFly BSD.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "so_keepalive" parameter of the "listen" directive
|
||||
might be handled incorrectly on DragonFly BSD.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="bugfix">
|
||||
<para lang="ru">
|
||||
в директиве proxy_store.
|
||||
</para>
|
||||
<para lang="en">
|
||||
in the "proxy_store" directive.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.27.2" date="2024-10-02">
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
SSL-сертификаты, секретные ключи и списки CRL теперь кешируются
|
||||
на старте или во время переконфигурации.
|
||||
</para>
|
||||
<para lang="en">
|
||||
SSL certificates, secret keys, and CRLs are now cached
|
||||
on start or during reconfiguration.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
проверка клиентских сертификатов с помощью OCSP в модуле stream.
|
||||
</para>
|
||||
<para lang="en">
|
||||
client certificate validation with OCSP in the stream module.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
поддержка OCSP stapling в модуле stream.
|
||||
</para>
|
||||
<para lang="en">
|
||||
OCSP stapling support in the stream module.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директива proxy_pass_trailers в модуле ngx_http_proxy_module.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "proxy_pass_trailers" directive in the ngx_http_proxy_module.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="feature">
|
||||
<para lang="ru">
|
||||
директива ssl_client_certificate теперь поддерживает сертификаты
|
||||
с дополнительными данными.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the "ssl_client_certificate" directive now supports certificates
|
||||
with auxiliary information.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
<change type="change">
|
||||
<para lang="ru">
|
||||
теперь наличие директивы ssl_client_certificate не обязательно
|
||||
для проверки клиентских SSL-сертификатов.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now the "ssl_client_certificate" directive is not required
|
||||
for client SSL certificates verification.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
</changes>
|
||||
|
||||
|
||||
<changes ver="1.27.1" date="2024-08-14">
|
||||
|
||||
<change type="security">
|
||||
@@ -8210,7 +8966,7 @@ Thanks to Piotr Sikora.
|
||||
Спасибо Piotr Sikora.
|
||||
</para>
|
||||
<para lang="en">
|
||||
now nginx can be build with BoringSSL and LibreSSL.<br/>
|
||||
now nginx can be built with BoringSSL and LibreSSL.<br/>
|
||||
Thanks to Piotr Sikora.
|
||||
</para>
|
||||
</change>
|
||||
@@ -25493,7 +26249,7 @@ the ECONNABORTED error log level was changed to "error" from "crit".
|
||||
модуль ngx_http_perl_module не собирался без модуля ngx_http_ssi_filter_module.
|
||||
</para>
|
||||
<para lang="en">
|
||||
the ngx_http_perl_module could not be build without
|
||||
the ngx_http_perl_module could not be built without
|
||||
the ngx_http_ssi_filter_module.
|
||||
</para>
|
||||
</change>
|
||||
|
||||
+1
-1
Submodule lib/boringssl updated: e724ef0208...e056f59c7d
Submodule lib/headers-more-nginx-module updated: f8f80997f1...e76a51306b
+1
-1
Submodule lib/naxsi updated: a0c7694b09...f52861ebe5
Submodule lib/nginx-http-flv-module updated: b26f7f38fc...b89c1acc64
+1
-1
Submodule lib/nginx-module-vts updated: 724b34d7f1...37d319fd39
+1
-1
Submodule lib/ngx_cache_purge updated: a84b0f3f08...a0e7ef4085
+1
-1
Submodule lib/ngx_devel_kit updated: 91e30eb050...bd44d16302
+1
-1
Submodule lib/ngx_dynamic_etag updated: 39a1801a59...58dd26102e
+1
-1
Submodule lib/pcre updated: 1415565c21...91606be5e8
+1
-1
Submodule lib/zlib-ng updated: d330931dd9...bbd2c9025c
+1
-1
Submodule lib/zstd-nginx-module updated: f4ba115e0b...057a7d339a
+9
-5
@@ -6,9 +6,9 @@ TEMP = tmp
|
||||
|
||||
CC = cl
|
||||
OBJS = objs.msvc8
|
||||
OPENSSL = openssl-3.0.14
|
||||
OPENSSL = openssl-3.5.4
|
||||
ZLIB = zlib-1.3.1
|
||||
PCRE = pcre2-10.39
|
||||
PCRE = pcre2-10.46
|
||||
|
||||
|
||||
release: export
|
||||
@@ -76,7 +76,8 @@ win32:
|
||||
--with-stream_realip_module \
|
||||
--with-stream_ssl_preread_module \
|
||||
--with-openssl=$(OBJS)/lib/$(OPENSSL) \
|
||||
--with-openssl-opt="no-asm no-tests -D_WIN32_WINNT=0x0501" \
|
||||
--with-openssl-opt="no-asm no-tests no-makedepend \
|
||||
-D_WIN32_WINNT=0x0501" \
|
||||
--with-http_ssl_module \
|
||||
--with-mail_ssl_module \
|
||||
--with-stream_ssl_module
|
||||
@@ -92,7 +93,10 @@ zip: export
|
||||
sed -i '' -e "s/$$/`printf '\r'`/" $(TEMP)/$(NGINX)/conf/*
|
||||
|
||||
mv $(TEMP)/$(NGINX)/LICENSE $(TEMP)/$(NGINX)/docs.new
|
||||
mv $(TEMP)/$(NGINX)/README $(TEMP)/$(NGINX)/docs.new
|
||||
mv $(TEMP)/$(NGINX)/README.md $(TEMP)/$(NGINX)/docs.new
|
||||
mv $(TEMP)/$(NGINX)/CODE_OF_CONDUCT.md $(TEMP)/$(NGINX)/docs.new
|
||||
mv $(TEMP)/$(NGINX)/CONTRIBUTING.md $(TEMP)/$(NGINX)/docs.new
|
||||
mv $(TEMP)/$(NGINX)/SECURITY.md $(TEMP)/$(NGINX)/docs.new
|
||||
mv $(TEMP)/$(NGINX)/docs/html $(TEMP)/$(NGINX)
|
||||
|
||||
rm -r $(TEMP)/$(NGINX)/docs
|
||||
@@ -106,7 +110,7 @@ zip: export
|
||||
cp -p $(OBJS)/lib/$(OPENSSL)/LICENSE.txt \
|
||||
$(TEMP)/$(NGINX)/docs/OpenSSL.LICENSE
|
||||
|
||||
cp -p $(OBJS)/lib/$(PCRE)/LICENCE \
|
||||
cp -p $(OBJS)/lib/$(PCRE)/LICENCE.md \
|
||||
$(TEMP)/$(NGINX)/docs/PCRE.LICENCE
|
||||
|
||||
sed -ne '/^ (C) 1995-20/,/^ jloup@gzip\.org/p' \
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ make -f misc/GNUmakefile release
|
||||
|
||||
the required tools:
|
||||
*) xsltproc to build CHANGES,
|
||||
*) xslscript.pl ( http://hg.nginx.org/xslscript ) to build XSLTs
|
||||
*) xslscript.pl ( https://github.com/nginx/xslscript ) to build XSLTs
|
||||
from XSLScript sources.
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@
|
||||
#define _NGINX_H_INCLUDED_
|
||||
|
||||
|
||||
#define nginx_version 1027002
|
||||
#define NGINX_VERSION "1.27.2"
|
||||
#define nginx_version 1029005
|
||||
#define NGINX_VERSION "1.29.5"
|
||||
#define NGINX_VER "nginx/" NGINX_VERSION " by Hakase"
|
||||
|
||||
#ifndef NGINX_SERVER
|
||||
|
||||
@@ -94,7 +94,7 @@ typedef intptr_t ngx_flag_t;
|
||||
|
||||
|
||||
#ifndef NGX_ALIGNMENT
|
||||
#define NGX_ALIGNMENT sizeof(unsigned long) /* platform word */
|
||||
#define NGX_ALIGNMENT sizeof(uintptr_t) /* platform word */
|
||||
#endif
|
||||
|
||||
#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
|
||||
|
||||
@@ -765,6 +765,8 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
|
||||
|
||||
#if (NGX_HAVE_KEEPALIVE_TUNABLE)
|
||||
|
||||
#if !(NGX_DARWIN)
|
||||
|
||||
if (ls[i].keepidle) {
|
||||
value = ls[i].keepidle;
|
||||
|
||||
@@ -782,6 +784,8 @@ ngx_configure_listening_sockets(ngx_cycle_t *cycle)
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (ls[i].keepintvl) {
|
||||
value = ls[i].keepintvl;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ typedef struct ngx_event_aio_s ngx_event_aio_t;
|
||||
typedef struct ngx_connection_s ngx_connection_t;
|
||||
typedef struct ngx_thread_task_s ngx_thread_task_t;
|
||||
typedef struct ngx_ssl_s ngx_ssl_t;
|
||||
typedef struct ngx_ssl_cache_s ngx_ssl_cache_t;
|
||||
typedef struct ngx_proxy_protocol_s ngx_proxy_protocol_t;
|
||||
typedef struct ngx_quic_stream_s ngx_quic_stream_t;
|
||||
typedef struct ngx_ssl_connection_s ngx_ssl_connection_t;
|
||||
|
||||
+10
-4
@@ -38,7 +38,7 @@ static ngx_connection_t dumb;
|
||||
ngx_cycle_t *
|
||||
ngx_init_cycle(ngx_cycle_t *old_cycle)
|
||||
{
|
||||
void *rv;
|
||||
void *rv, *data;
|
||||
char **senv;
|
||||
ngx_uint_t i, n;
|
||||
ngx_log_t *log;
|
||||
@@ -438,6 +438,8 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
|
||||
opart = &old_cycle->shared_memory.part;
|
||||
oshm_zone = opart->elts;
|
||||
|
||||
data = NULL;
|
||||
|
||||
for (n = 0; /* void */ ; n++) {
|
||||
|
||||
if (n >= opart->nelts) {
|
||||
@@ -461,9 +463,13 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (shm_zone[i].tag == oshm_zone[n].tag && shm_zone[i].noreuse) {
|
||||
data = oshm_zone[n].data;
|
||||
break;
|
||||
}
|
||||
|
||||
if (shm_zone[i].tag == oshm_zone[n].tag
|
||||
&& shm_zone[i].shm.size == oshm_zone[n].shm.size
|
||||
&& !shm_zone[i].noreuse)
|
||||
&& shm_zone[i].shm.size == oshm_zone[n].shm.size)
|
||||
{
|
||||
shm_zone[i].shm.addr = oshm_zone[n].shm.addr;
|
||||
#if (NGX_WIN32)
|
||||
@@ -490,7 +496,7 @@ ngx_init_cycle(ngx_cycle_t *old_cycle)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {
|
||||
if (shm_zone[i].init(&shm_zone[i], data) != NGX_OK) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -639,7 +639,11 @@ ngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text,
|
||||
|
||||
p = ngx_strlchr(text, last, ']');
|
||||
|
||||
if (p == NULL || p == last - 1 || *++p != ':') {
|
||||
if (p == last - 1) {
|
||||
return ngx_parse_addr(pool, addr, text + 1, len - 2);
|
||||
}
|
||||
|
||||
if (p == NULL || *++p != ':') {
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
+29
-3
@@ -1494,8 +1494,9 @@ ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)
|
||||
uintptr_t
|
||||
ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
|
||||
{
|
||||
ngx_uint_t n;
|
||||
u_char prefix;
|
||||
uint32_t *escape;
|
||||
ngx_uint_t n;
|
||||
static u_char hex[] = "0123456789ABCDEF";
|
||||
|
||||
/*
|
||||
@@ -1633,11 +1634,36 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
|
||||
|
||||
/* mail_auth is the same as memcached */
|
||||
|
||||
/* " ", "+", "=", not allowed */
|
||||
|
||||
static uint32_t mail_xtext[] = {
|
||||
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
||||
|
||||
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
|
||||
0x20000801, /* 0010 0000 0000 0000 0000 1000 0000 0001 */
|
||||
|
||||
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
|
||||
0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
|
||||
|
||||
/* ~}| {zyx wvut srqp onml kjih gfed cba` */
|
||||
0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
|
||||
|
||||
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
||||
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
||||
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
||||
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
|
||||
};
|
||||
|
||||
static uint32_t *map[] =
|
||||
{ uri, args, uri_component, html, refresh, memcached, memcached };
|
||||
{ uri, args, uri_component, html, refresh, memcached, memcached,
|
||||
mail_xtext };
|
||||
|
||||
static u_char map_char[] =
|
||||
{ '%', '%', '%', '%', '%', '%', '%', '+' };
|
||||
|
||||
|
||||
escape = map[type];
|
||||
prefix = map_char[type];
|
||||
|
||||
if (dst == NULL) {
|
||||
|
||||
@@ -1658,7 +1684,7 @@ ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
|
||||
|
||||
while (size) {
|
||||
if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
|
||||
*dst++ = '%';
|
||||
*dst++ = prefix;
|
||||
*dst++ = hex[*src >> 4];
|
||||
*dst++ = hex[*src & 0xf];
|
||||
src++;
|
||||
|
||||
@@ -203,6 +203,7 @@ u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);
|
||||
#define NGX_ESCAPE_REFRESH 4
|
||||
#define NGX_ESCAPE_MEMCACHED 5
|
||||
#define NGX_ESCAPE_MAIL_AUTH 6
|
||||
#define NGX_ESCAPE_MAIL_XTEXT 7
|
||||
|
||||
#define NGX_UNESCAPE_URI 1
|
||||
#define NGX_UNESCAPE_REDIRECT 2
|
||||
|
||||
@@ -207,7 +207,7 @@ ngx_thread_pool_exit_handler(void *data, ngx_log_t *log)
|
||||
|
||||
*lock = 0;
|
||||
|
||||
pthread_exit(0);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,15 @@
|
||||
#include <ngx_event.h>
|
||||
|
||||
|
||||
/* NetBSD up to 10.0 incompatibly defines kevent.udata as "intptr_t" */
|
||||
|
||||
#if (__NetBSD__ && __NetBSD_Version__ < 1000000000)
|
||||
#define NGX_KQUEUE_UDATA_T
|
||||
#else
|
||||
#define NGX_KQUEUE_UDATA_T (void *)
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_uint_t changes;
|
||||
ngx_uint_t events;
|
||||
@@ -191,7 +200,7 @@ ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer)
|
||||
kev.flags = EV_ADD|EV_ENABLE;
|
||||
kev.fflags = 0;
|
||||
kev.data = timer;
|
||||
kev.udata = 0;
|
||||
kev.udata = NGX_KQUEUE_UDATA_T (uintptr_t) 0;
|
||||
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 0;
|
||||
@@ -237,7 +246,7 @@ ngx_kqueue_notify_init(ngx_log_t *log)
|
||||
notify_kev.data = 0;
|
||||
notify_kev.flags = EV_ADD|EV_CLEAR;
|
||||
notify_kev.fflags = 0;
|
||||
notify_kev.udata = 0;
|
||||
notify_kev.udata = NGX_KQUEUE_UDATA_T (uintptr_t) 0;
|
||||
|
||||
if (kevent(ngx_kqueue, ¬ify_kev, 1, NULL, 0, NULL) == -1) {
|
||||
ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
|
||||
|
||||
@@ -203,6 +203,23 @@ ngx_event_accept(ngx_event_t *ev)
|
||||
}
|
||||
}
|
||||
|
||||
#if (NGX_HAVE_KEEPALIVE_TUNABLE && NGX_DARWIN)
|
||||
|
||||
/* Darwin doesn't inherit TCP_KEEPALIVE from a listening socket */
|
||||
|
||||
if (ls->keepidle) {
|
||||
if (setsockopt(s, IPPROTO_TCP, TCP_KEEPALIVE,
|
||||
(const void *) &ls->keepidle, sizeof(int))
|
||||
== -1)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
|
||||
"setsockopt(TCP_KEEPALIVE, %d) failed, ignored",
|
||||
ls->keepidle);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
*log = ls->log;
|
||||
|
||||
c->recv = ngx_recv;
|
||||
|
||||
+912
-374
@@ -9,6 +9,10 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
|
||||
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define NGX_SSL_PASSWORD_BUFFER_SIZE 4096
|
||||
|
||||
@@ -18,15 +22,19 @@ typedef struct {
|
||||
} ngx_openssl_conf_t;
|
||||
|
||||
|
||||
static X509 *ngx_ssl_load_certificate(ngx_pool_t *pool, char **err,
|
||||
ngx_str_t *cert, STACK_OF(X509) **chain);
|
||||
static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
|
||||
ngx_str_t *key, ngx_array_t *passwords);
|
||||
static int ngx_ssl_password_callback(char *buf, int size, int rwflag,
|
||||
void *userdata);
|
||||
static ngx_inline ngx_int_t ngx_ssl_cert_already_in_hash(void);
|
||||
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
|
||||
static int ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn,
|
||||
CBB *out, const uint8_t *in, size_t in_len);
|
||||
static void *ngx_ssl_cert_compression_alloc(void *opaque, u_int items,
|
||||
u_int size);
|
||||
static void ngx_ssl_cert_compression_free(void *opaque, void *address);
|
||||
#endif
|
||||
static int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
|
||||
static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
|
||||
int ret);
|
||||
static int ngx_ssl_cmp_x509_name(const X509_NAME *const *a,
|
||||
const X509_NAME *const *b);
|
||||
static void ngx_ssl_passwords_cleanup(void *data);
|
||||
static int ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn,
|
||||
ngx_ssl_session_t *sess);
|
||||
@@ -48,8 +56,6 @@ static ssize_t ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file,
|
||||
size_t size);
|
||||
static void ngx_ssl_read_handler(ngx_event_t *rev);
|
||||
static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
|
||||
static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
|
||||
ngx_err_t err, char *text);
|
||||
static void ngx_ssl_clear_error(ngx_log_t *log);
|
||||
|
||||
static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,
|
||||
@@ -131,10 +137,13 @@ int ngx_ssl_server_conf_index;
|
||||
int ngx_ssl_session_cache_index;
|
||||
int ngx_ssl_ticket_keys_index;
|
||||
int ngx_ssl_ocsp_index;
|
||||
int ngx_ssl_certificate_index;
|
||||
int ngx_ssl_next_certificate_index;
|
||||
int ngx_ssl_index;
|
||||
int ngx_ssl_certificate_name_index;
|
||||
int ngx_ssl_stapling_index;
|
||||
int ngx_ssl_certificate_comp_index;
|
||||
int ngx_ssl_client_hello_arg_index;
|
||||
|
||||
|
||||
u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE];
|
||||
|
||||
|
||||
ngx_int_t
|
||||
@@ -258,21 +267,14 @@ ngx_ssl_init(ngx_log_t *log)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
|
||||
NULL);
|
||||
if (ngx_ssl_certificate_index == -1) {
|
||||
ngx_ssl_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (ngx_ssl_index == -1) {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
|
||||
"SSL_CTX_get_ex_new_index() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_ssl_next_certificate_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
|
||||
NULL);
|
||||
if (ngx_ssl_next_certificate_index == -1) {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_ssl_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
|
||||
NULL);
|
||||
|
||||
@@ -281,13 +283,21 @@ ngx_ssl_init(ngx_log_t *log)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL);
|
||||
|
||||
if (ngx_ssl_stapling_index == -1) {
|
||||
ngx_ssl_certificate_comp_index = X509_get_ex_new_index(0, NULL, NULL, NULL,
|
||||
NULL);
|
||||
if (ngx_ssl_certificate_comp_index == -1) {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_ssl_client_hello_arg_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,
|
||||
NULL, NULL);
|
||||
if (ngx_ssl_client_hello_arg_index == -1) {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, log, 0,
|
||||
"SSL_CTX_get_ex_new_index() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
@@ -308,12 +318,15 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, NULL) == 0) {
|
||||
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_index, ssl) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_set_ex_data() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_rbtree_init(&ssl->staple_rbtree, &ssl->staple_sentinel,
|
||||
ngx_rbtree_insert_value);
|
||||
|
||||
ssl->buffer_size = NGX_SSL_BUFSIZE;
|
||||
|
||||
/* client side options */
|
||||
@@ -402,6 +415,11 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
|
||||
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
|
||||
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION);
|
||||
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_RX_CERTIFICATE_COMPRESSION);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_OP_NO_ANTI_REPLAY
|
||||
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_ANTI_REPLAY);
|
||||
#endif
|
||||
@@ -458,12 +476,20 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
ngx_str_t *key, ngx_array_t *passwords)
|
||||
{
|
||||
char *err;
|
||||
X509 *x509;
|
||||
X509 *x509, **elm;
|
||||
u_long n;
|
||||
EVP_PKEY *pkey;
|
||||
ngx_uint_t mask;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain);
|
||||
if (x509 == NULL) {
|
||||
mask = 0;
|
||||
elm = NULL;
|
||||
|
||||
retry:
|
||||
|
||||
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CERT | mask,
|
||||
&err, cert, NULL);
|
||||
if (chain == NULL) {
|
||||
if (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"cannot load certificate \"%s\": %s",
|
||||
@@ -473,6 +499,8 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
x509 = sk_X509_shift(chain);
|
||||
|
||||
if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_use_certificate(\"%s\") failed", cert->data);
|
||||
@@ -490,29 +518,34 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index,
|
||||
SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index))
|
||||
== 0)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
if (ssl->certs.elts == NULL) {
|
||||
if (ngx_array_init(&ssl->certs, cf->pool, 1, sizeof(X509 *))
|
||||
!= NGX_OK)
|
||||
{
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_set_ex_data() failed");
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
if (elm == NULL) {
|
||||
elm = ngx_array_push(&ssl->certs);
|
||||
if (elm == NULL) {
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
X509_free(*elm);
|
||||
}
|
||||
|
||||
*elm = x509;
|
||||
|
||||
/*
|
||||
* Note that x509 is not freed here, but will be instead freed in
|
||||
* ngx_ssl_cleanup_ctx(). This is because we need to preserve all
|
||||
* certificates to be able to iterate all of them through exdata
|
||||
* (ngx_ssl_certificate_index, ngx_ssl_next_certificate_index),
|
||||
* certificates to be able to iterate all of them through ssl->certs,
|
||||
* while OpenSSL can free a certificate if it is replaced with another
|
||||
* certificate of the same type.
|
||||
*/
|
||||
@@ -527,11 +560,21 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
}
|
||||
|
||||
#else
|
||||
{
|
||||
int n;
|
||||
|
||||
/* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */
|
||||
|
||||
#ifdef SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS
|
||||
/* OpenSSL 1.0.1+ */
|
||||
SSL_CTX_clear_extra_chain_certs(ssl->ctx);
|
||||
#else
|
||||
|
||||
if (ssl->ctx->extra_certs) {
|
||||
sk_X509_pop_free(ssl->ctx->extra_certs, X509_free);
|
||||
ssl->ctx->extra_certs = NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
n = sk_X509_num(chain);
|
||||
|
||||
while (n--) {
|
||||
@@ -547,10 +590,11 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
}
|
||||
|
||||
sk_X509_free(chain);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords);
|
||||
pkey = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_PKEY | mask,
|
||||
&err, key, passwords);
|
||||
if (pkey == NULL) {
|
||||
if (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
@@ -562,9 +606,23 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
/* there can be mismatched pairs on uneven cache update */
|
||||
|
||||
n = ERR_peek_last_error();
|
||||
|
||||
if (ERR_GET_LIB(n) == ERR_LIB_X509
|
||||
&& ERR_GET_REASON(n) == X509_R_KEY_VALUES_MISMATCH
|
||||
&& mask == 0)
|
||||
{
|
||||
ERR_clear_error();
|
||||
mask = NGX_SSL_CACHE_INVALIDATE;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_use_PrivateKey(\"%s\") failed", key->data);
|
||||
EVP_PKEY_free(pkey);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
@@ -576,15 +634,24 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords)
|
||||
ngx_str_t *cert, ngx_str_t *key, ngx_ssl_cache_t *cache,
|
||||
ngx_array_t *passwords)
|
||||
{
|
||||
char *err;
|
||||
X509 *x509;
|
||||
u_long n;
|
||||
EVP_PKEY *pkey;
|
||||
ngx_uint_t mask;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
x509 = ngx_ssl_load_certificate(pool, &err, cert, &chain);
|
||||
if (x509 == NULL) {
|
||||
mask = 0;
|
||||
|
||||
retry:
|
||||
|
||||
chain = ngx_ssl_cache_connection_fetch(cache, pool,
|
||||
NGX_SSL_CACHE_CERT | mask,
|
||||
&err, cert, NULL);
|
||||
if (chain == NULL) {
|
||||
if (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
|
||||
"cannot load certificate \"%s\": %s",
|
||||
@@ -594,6 +661,8 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
x509 = sk_X509_shift(chain);
|
||||
|
||||
if (SSL_use_certificate(c->ssl->connection, x509) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
|
||||
"SSL_use_certificate(\"%s\") failed", cert->data);
|
||||
@@ -621,7 +690,9 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
|
||||
#endif
|
||||
|
||||
pkey = ngx_ssl_load_certificate_key(pool, &err, key, passwords);
|
||||
pkey = ngx_ssl_cache_connection_fetch(cache, pool,
|
||||
NGX_SSL_CACHE_PKEY | mask,
|
||||
&err, key, passwords);
|
||||
if (pkey == NULL) {
|
||||
if (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
|
||||
@@ -633,9 +704,23 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
}
|
||||
|
||||
if (SSL_use_PrivateKey(c->ssl->connection, pkey) == 0) {
|
||||
EVP_PKEY_free(pkey);
|
||||
|
||||
/* there can be mismatched pairs on uneven cache update */
|
||||
|
||||
n = ERR_peek_last_error();
|
||||
|
||||
if (ERR_GET_LIB(n) == ERR_LIB_X509
|
||||
&& ERR_GET_REASON(n) == X509_R_KEY_VALUES_MISMATCH
|
||||
&& mask == 0)
|
||||
{
|
||||
ERR_clear_error();
|
||||
mask = NGX_SSL_CACHE_INVALIDATE;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
|
||||
"SSL_use_PrivateKey(\"%s\") failed", key->data);
|
||||
EVP_PKEY_free(pkey);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
@@ -645,241 +730,197 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
}
|
||||
|
||||
|
||||
static X509 *
|
||||
ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, ngx_str_t *cert,
|
||||
STACK_OF(X509) **chain)
|
||||
ngx_int_t
|
||||
ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_uint_t enable)
|
||||
{
|
||||
BIO *bio;
|
||||
X509 *x509, *temp;
|
||||
u_long n;
|
||||
|
||||
if (ngx_strncmp(cert->data, "data:", sizeof("data:") - 1) == 0) {
|
||||
|
||||
bio = BIO_new_mem_buf(cert->data + sizeof("data:") - 1,
|
||||
cert->len - (sizeof("data:") - 1));
|
||||
if (bio == NULL) {
|
||||
*err = "BIO_new_mem_buf() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, cert)
|
||||
!= NGX_OK)
|
||||
{
|
||||
*err = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bio = BIO_new_file((char *) cert->data, "r");
|
||||
if (bio == NULL) {
|
||||
*err = "BIO_new_file() failed";
|
||||
return NULL;
|
||||
}
|
||||
if (!enable) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
/* certificate itself */
|
||||
#ifdef SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
|
||||
|
||||
x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
|
||||
if (x509 == NULL) {
|
||||
*err = "PEM_read_bio_X509_AUX() failed";
|
||||
BIO_free(bio);
|
||||
return NULL;
|
||||
if (SSL_CTX_compress_certs(ssl->ctx, 0) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_WARN, ssl->log, 0,
|
||||
"SSL_CTX_compress_certs() failed, ignored");
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
/* rest of the chain */
|
||||
SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION);
|
||||
|
||||
*chain = sk_X509_new_null();
|
||||
if (*chain == NULL) {
|
||||
*err = "sk_X509_new_null() failed";
|
||||
BIO_free(bio);
|
||||
X509_free(x509);
|
||||
return NULL;
|
||||
#elif (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
|
||||
|
||||
if (SSL_CTX_add_cert_compression_alg(ssl->ctx, TLSEXT_cert_compression_zlib,
|
||||
ngx_ssl_cert_compression_callback,
|
||||
NULL)
|
||||
== 0)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_add_cert_compression_alg() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
temp = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
if (temp == NULL) {
|
||||
n = ERR_peek_last_error();
|
||||
|
||||
if (ERR_GET_LIB(n) == ERR_LIB_PEM
|
||||
&& ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
|
||||
{
|
||||
/* end of file */
|
||||
ERR_clear_error();
|
||||
break;
|
||||
}
|
||||
|
||||
/* some real error */
|
||||
|
||||
*err = "PEM_read_bio_X509() failed";
|
||||
BIO_free(bio);
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(*chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sk_X509_push(*chain, temp) == 0) {
|
||||
*err = "sk_X509_push() failed";
|
||||
BIO_free(bio);
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(*chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
return x509;
|
||||
}
|
||||
|
||||
|
||||
static EVP_PKEY *
|
||||
ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
|
||||
ngx_str_t *key, ngx_array_t *passwords)
|
||||
{
|
||||
BIO *bio;
|
||||
EVP_PKEY *pkey;
|
||||
ngx_str_t *pwd;
|
||||
ngx_uint_t tries;
|
||||
pem_password_cb *cb;
|
||||
|
||||
if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) {
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
|
||||
u_char *p, *last;
|
||||
ENGINE *engine;
|
||||
|
||||
p = key->data + sizeof("engine:") - 1;
|
||||
last = (u_char *) ngx_strchr(p, ':');
|
||||
|
||||
if (last == NULL) {
|
||||
*err = "invalid syntax";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*last = '\0';
|
||||
|
||||
engine = ENGINE_by_id((char *) p);
|
||||
|
||||
*last++ = ':';
|
||||
|
||||
if (engine == NULL) {
|
||||
*err = "ENGINE_by_id() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);
|
||||
|
||||
if (pkey == NULL) {
|
||||
*err = "ENGINE_load_private_key() failed";
|
||||
ENGINE_free(engine);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ENGINE_free(engine);
|
||||
|
||||
return pkey;
|
||||
|
||||
#else
|
||||
|
||||
*err = "loading \"engine:...\" certificate keys is not supported";
|
||||
return NULL;
|
||||
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
|
||||
"\"ssl_certificate_compression\" is not supported "
|
||||
"on this platform, ignored");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ngx_strncmp(key->data, "data:", sizeof("data:") - 1) == 0) {
|
||||
|
||||
bio = BIO_new_mem_buf(key->data + sizeof("data:") - 1,
|
||||
key->len - (sizeof("data:") - 1));
|
||||
if (bio == NULL) {
|
||||
*err = "BIO_new_mem_buf() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, key)
|
||||
!= NGX_OK)
|
||||
{
|
||||
*err = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bio = BIO_new_file((char *) key->data, "r");
|
||||
if (bio == NULL) {
|
||||
*err = "BIO_new_file() failed";
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (passwords) {
|
||||
tries = passwords->nelts;
|
||||
pwd = passwords->elts;
|
||||
cb = ngx_ssl_password_callback;
|
||||
|
||||
} else {
|
||||
tries = 1;
|
||||
pwd = NULL;
|
||||
cb = NULL;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
|
||||
if (pkey != NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tries-- > 1) {
|
||||
ERR_clear_error();
|
||||
(void) BIO_reset(bio);
|
||||
pwd++;
|
||||
continue;
|
||||
}
|
||||
|
||||
*err = "PEM_read_bio_PrivateKey() failed";
|
||||
BIO_free(bio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
return pkey;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_ZLIB && defined TLSEXT_cert_compression_zlib)
|
||||
|
||||
static int
|
||||
ngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata)
|
||||
ngx_ssl_cert_compression_callback(ngx_ssl_conn_t *ssl_conn, CBB *out,
|
||||
const uint8_t *in, size_t in_len)
|
||||
{
|
||||
ngx_str_t *pwd = userdata;
|
||||
int rc;
|
||||
X509 *cert;
|
||||
u_char *p;
|
||||
z_stream zstream;
|
||||
ngx_str_t *comp, tmp;
|
||||
ngx_pool_t *pool;
|
||||
ngx_connection_t *c;
|
||||
|
||||
if (rwflag) {
|
||||
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
||||
"ngx_ssl_password_callback() is called for encryption");
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
{
|
||||
SSL_CTX *ssl_ctx;
|
||||
ngx_ssl_t *ssl;
|
||||
|
||||
/* BoringSSL doesn't have certificate slots, we take the last set */
|
||||
|
||||
ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
|
||||
ssl = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_index);
|
||||
cert = ((X509 **) ssl->certs.elts)[ssl->certs.nelts - 1];
|
||||
}
|
||||
#else
|
||||
|
||||
/*
|
||||
* AWS-LC saves leaf certificate in SSL to associate with SSL_CTX,
|
||||
* see https://github.com/aws/aws-lc/commit/e1ba2b3e5
|
||||
*/
|
||||
|
||||
cert = SSL_get_certificate(ssl_conn);
|
||||
|
||||
#endif
|
||||
|
||||
comp = X509_get_ex_data(cert, ngx_ssl_certificate_comp_index);
|
||||
|
||||
if (comp != NULL) {
|
||||
return CBB_add_bytes(out, comp->data, comp->len);
|
||||
}
|
||||
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
|
||||
pool = ngx_create_pool(256, c->log);
|
||||
if (pool == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pwd == NULL) {
|
||||
return 0;
|
||||
pool->log = c->log;
|
||||
|
||||
ngx_memzero(&zstream, sizeof(z_stream));
|
||||
|
||||
zstream.zalloc = ngx_ssl_cert_compression_alloc;
|
||||
zstream.zfree = ngx_ssl_cert_compression_free;
|
||||
zstream.opaque = pool;
|
||||
|
||||
rc = deflateInit(&zstream, Z_DEFAULT_COMPRESSION);
|
||||
|
||||
if (rc != Z_OK) {
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateInit() failed: %d", rc);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (pwd->len > (size_t) size) {
|
||||
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
|
||||
"password is truncated to %d bytes", size);
|
||||
} else {
|
||||
size = pwd->len;
|
||||
tmp.len = deflateBound(&zstream, in_len);
|
||||
tmp.data = ngx_palloc(pool, tmp.len);
|
||||
if (tmp.data == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ngx_memcpy(buf, pwd->data, size);
|
||||
zstream.next_in = (u_char *) in;
|
||||
zstream.avail_in = in_len;
|
||||
zstream.next_out = tmp.data;
|
||||
zstream.avail_out = tmp.len;
|
||||
|
||||
return size;
|
||||
rc = deflate(&zstream, Z_FINISH);
|
||||
|
||||
if (rc != Z_STREAM_END) {
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"deflate(Z_FINISH) failed: %d", rc);
|
||||
goto error;
|
||||
}
|
||||
|
||||
tmp.len -= zstream.avail_out;
|
||||
|
||||
rc = deflateEnd(&zstream);
|
||||
|
||||
if (rc != Z_OK) {
|
||||
ngx_log_error(NGX_LOG_ALERT, c->log, 0, "deflateEnd() failed: %d", rc);
|
||||
goto error;
|
||||
}
|
||||
|
||||
p = ngx_alloc(sizeof(ngx_str_t) + tmp.len, c->log);
|
||||
if (p == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
comp = (ngx_str_t *) p;
|
||||
|
||||
comp->len = tmp.len;
|
||||
comp->data = p + sizeof(ngx_str_t);
|
||||
|
||||
ngx_memcpy(comp->data, tmp.data, tmp.len);
|
||||
|
||||
if (X509_set_ex_data(cert, ngx_ssl_certificate_comp_index, p) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "X509_set_ex_data() failed");
|
||||
ngx_free(p);
|
||||
}
|
||||
|
||||
rc = CBB_add_bytes(out, tmp.data, tmp.len);
|
||||
|
||||
ngx_destroy_pool(pool);
|
||||
|
||||
return rc;
|
||||
|
||||
error:
|
||||
|
||||
ngx_destroy_pool(pool);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_ssl_cert_compression_alloc(void *opaque, u_int items, u_int size)
|
||||
{
|
||||
ngx_pool_t *pool = opaque;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pool->log, 0,
|
||||
"cert compression alloc: n:%ud s:%ud", items, size);
|
||||
|
||||
return ngx_palloc(pool, items * size);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_cert_compression_free(void *opaque, void *address)
|
||||
{
|
||||
#if 0
|
||||
ngx_pool_t *pool = opaque;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pool->log, 0,
|
||||
"cert compression free: %p", address);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
|
||||
ngx_uint_t prefer_server_ciphers)
|
||||
@@ -903,6 +944,12 @@ ngx_int_t
|
||||
ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
ngx_int_t depth)
|
||||
{
|
||||
int n, i;
|
||||
char *err;
|
||||
X509 *x509;
|
||||
X509_NAME *name;
|
||||
X509_STORE *store;
|
||||
STACK_OF(X509) *chain;
|
||||
STACK_OF(X509_NAME) *list;
|
||||
|
||||
SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_ssl_verify_callback);
|
||||
@@ -913,88 +960,8 @@ ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
|
||||
== 0)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_load_verify_locations(\"%s\") failed",
|
||||
cert->data);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* SSL_CTX_load_verify_locations() may leave errors in the error queue
|
||||
* while returning success
|
||||
*/
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
list = SSL_load_client_CA_file((char *) cert->data);
|
||||
|
||||
list = sk_X509_NAME_new(ngx_ssl_cmp_x509_name);
|
||||
if (list == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_load_client_CA_file(\"%s\") failed", cert->data);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
SSL_CTX_set_client_CA_list(ssl->ctx, list);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
ngx_int_t depth)
|
||||
{
|
||||
SSL_CTX_set_verify(ssl->ctx, SSL_CTX_get_verify_mode(ssl->ctx),
|
||||
ngx_ssl_verify_callback);
|
||||
|
||||
SSL_CTX_set_verify_depth(ssl->ctx, depth);
|
||||
|
||||
if (cert->len == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
|
||||
== 0)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_load_verify_locations(\"%s\") failed",
|
||||
cert->data);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* SSL_CTX_load_verify_locations() may leave errors in the error queue
|
||||
* while returning success
|
||||
*/
|
||||
|
||||
ERR_clear_error();
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
|
||||
{
|
||||
X509_STORE *store;
|
||||
X509_LOOKUP *lookup;
|
||||
|
||||
if (crl->len == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
@@ -1006,22 +973,190 @@ ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
|
||||
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CA, &err, cert, NULL);
|
||||
if (chain == NULL) {
|
||||
if (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"cannot load certificate \"%s\": %s",
|
||||
cert->data, err);
|
||||
}
|
||||
|
||||
if (lookup == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_STORE_add_lookup() failed");
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM)
|
||||
== 0)
|
||||
{
|
||||
n = sk_X509_num(chain);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
x509 = sk_X509_value(chain, i);
|
||||
|
||||
if (X509_STORE_add_cert(store, x509) != 1) {
|
||||
|
||||
if (ngx_ssl_cert_already_in_hash()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_STORE_add_cert(\"%s\") failed", cert->data);
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
name = X509_get_subject_name(x509);
|
||||
if (name == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_get_subject_name(\"%s\") failed", cert->data);
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
name = X509_NAME_dup(name);
|
||||
if (name == NULL) {
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
if (sk_X509_NAME_find(list, NULL, name) > 0) {
|
||||
#else
|
||||
if (sk_X509_NAME_find(list, name) >= 0) {
|
||||
#endif
|
||||
X509_NAME_free(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sk_X509_NAME_push(list, name) == 0) {
|
||||
sk_X509_NAME_pop_free(list, X509_NAME_free);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
X509_NAME_free(name);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
|
||||
SSL_CTX_set_client_CA_list(ssl->ctx, list);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
|
||||
ngx_int_t depth)
|
||||
{
|
||||
int i, n;
|
||||
char *err;
|
||||
X509 *x509;
|
||||
X509_STORE *store;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
SSL_CTX_set_verify(ssl->ctx, SSL_CTX_get_verify_mode(ssl->ctx),
|
||||
ngx_ssl_verify_callback);
|
||||
|
||||
SSL_CTX_set_verify_depth(ssl->ctx, depth);
|
||||
|
||||
if (cert->len == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
store = SSL_CTX_get_cert_store(ssl->ctx);
|
||||
|
||||
if (store == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_LOOKUP_load_file(\"%s\") failed", crl->data);
|
||||
"SSL_CTX_get_cert_store() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CA, &err, cert, NULL);
|
||||
if (chain == NULL) {
|
||||
if (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"cannot load certificate \"%s\": %s",
|
||||
cert->data, err);
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
n = sk_X509_num(chain);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
x509 = sk_X509_value(chain, i);
|
||||
|
||||
if (X509_STORE_add_cert(store, x509) != 1) {
|
||||
|
||||
if (ngx_ssl_cert_already_in_hash()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_STORE_add_cert(\"%s\") failed", cert->data);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
|
||||
{
|
||||
int n, i;
|
||||
char *err;
|
||||
X509_CRL *x509;
|
||||
X509_STORE *store;
|
||||
STACK_OF(X509_CRL) *chain;
|
||||
|
||||
if (crl->len == 0) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
store = SSL_CTX_get_cert_store(ssl->ctx);
|
||||
|
||||
if (store == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_get_cert_store() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
chain = ngx_ssl_cache_fetch(cf, NGX_SSL_CACHE_CRL, &err, crl, NULL);
|
||||
if (chain == NULL) {
|
||||
if (err != NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"cannot load CRL \"%s\": %s", crl->data, err);
|
||||
}
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
n = sk_X509_CRL_num(chain);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
x509 = sk_X509_CRL_value(chain, i);
|
||||
|
||||
if (X509_STORE_add_crl(store, x509) != 1) {
|
||||
|
||||
if (ngx_ssl_cert_already_in_hash()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_STORE_add_crl(\"%s\") failed", crl->data);
|
||||
sk_X509_CRL_pop_free(chain, X509_CRL_free);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
sk_X509_CRL_pop_free(chain, X509_CRL_free);
|
||||
|
||||
X509_STORE_set_flags(store,
|
||||
X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
|
||||
|
||||
@@ -1029,6 +1164,32 @@ ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
|
||||
}
|
||||
|
||||
|
||||
static ngx_inline ngx_int_t
|
||||
ngx_ssl_cert_already_in_hash(void)
|
||||
{
|
||||
#if !(OPENSSL_VERSION_NUMBER >= 0x1010009fL \
|
||||
|| LIBRESSL_VERSION_NUMBER >= 0x3050000fL)
|
||||
u_long error;
|
||||
|
||||
/*
|
||||
* OpenSSL prior to 1.1.0i doesn't ignore duplicate certificate entries,
|
||||
* see https://github.com/openssl/openssl/commit/c0452248
|
||||
*/
|
||||
|
||||
error = ERR_peek_last_error();
|
||||
|
||||
if (ERR_GET_LIB(error) == ERR_LIB_X509
|
||||
&& ERR_GET_REASON(error) == X509_R_CERT_ALREADY_IN_HASH_TABLE)
|
||||
{
|
||||
ERR_clear_error();
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
|
||||
{
|
||||
@@ -1195,6 +1356,13 @@ ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_ssl_cmp_x509_name(const X509_NAME *const *a, const X509_NAME *const *b)
|
||||
{
|
||||
return (X509_NAME_cmp(*a, *b));
|
||||
}
|
||||
|
||||
|
||||
ngx_array_t *
|
||||
ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)
|
||||
{
|
||||
@@ -1407,6 +1575,8 @@ ngx_ssl_passwords_cleanup(void *data)
|
||||
ngx_int_t
|
||||
ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
|
||||
{
|
||||
#ifndef OPENSSL_NO_DH
|
||||
|
||||
BIO *bio;
|
||||
|
||||
if (file->len == 0) {
|
||||
@@ -1465,8 +1635,8 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
|
||||
|
||||
if (SSL_CTX_set0_tmp_dh_pkey(ssl->ctx, dh) != 1) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_set0_tmp_dh_pkey(\%s\") failed", file->data);
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x3000001fL)
|
||||
"SSL_CTX_set0_tmp_dh_pkey(\"%s\") failed", file->data);
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x30000010L)
|
||||
EVP_PKEY_free(dh);
|
||||
#endif
|
||||
BIO_free(bio);
|
||||
@@ -1477,10 +1647,111 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_ech_files(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *filenames)
|
||||
{
|
||||
#ifdef SSL_OP_ECH_GREASE
|
||||
int numkeys;
|
||||
BIO *in;
|
||||
ngx_int_t rc;
|
||||
ngx_str_t *filename;
|
||||
ngx_uint_t i;
|
||||
OSSL_ECHSTORE *es;
|
||||
|
||||
if (filenames == NULL) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
es = OSSL_ECHSTORE_new(NULL, NULL);
|
||||
if (es == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "OSSL_ECHSTORE_new() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
rc = NGX_ERROR;
|
||||
filename = filenames->elts;
|
||||
|
||||
for (i = 0; i < filenames->nelts; i++) {
|
||||
|
||||
if (ngx_conf_full_name(cf->cycle, &filename[i], 1) != NGX_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
in = BIO_new_file((char *) filename[i].data, "r");
|
||||
if (in == NULL) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"BIO_new_file(\"%s\") failed", filename[i].data);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only set the ECHConfigList from the first file read to use
|
||||
* in ECH retry-configs.
|
||||
*
|
||||
* That allows many sensible key rotation schemes so that the
|
||||
* values sent in ECH retry-configs are smaller and current.
|
||||
* For example, if the first file name has the current ECH
|
||||
* private key, and a second one has the previously used key
|
||||
* that some clients may still use due to DNS caching.
|
||||
*/
|
||||
|
||||
if (OSSL_ECHSTORE_read_pem(es, in, i ? OSSL_ECH_NO_RETRY
|
||||
: OSSL_ECH_FOR_RETRY)
|
||||
!= 1)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"OSSL_ECHSTORE_read_pem(%s) failed",
|
||||
filename[i].data);
|
||||
BIO_free(in);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
BIO_free(in);
|
||||
}
|
||||
|
||||
/*
|
||||
* load the ECH store after checking there's at least one ECH
|
||||
* private key in there (the PEM file spec allows zero or one
|
||||
* private key per file)
|
||||
*/
|
||||
|
||||
if (OSSL_ECHSTORE_num_keys(es, &numkeys) != 1) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"OSSL_ECHSTORE_num_keys(%s) failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (numkeys > 0 && SSL_CTX_set1_echstore(ssl->ctx, es) != 1) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_set1_echstore() failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = NGX_OK;
|
||||
|
||||
cleanup:
|
||||
|
||||
OSSL_ECHSTORE_free(es);
|
||||
return rc;
|
||||
|
||||
#else
|
||||
if (filenames != NULL) {
|
||||
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
|
||||
"\"ssl_ech_file\" is not supported on this platform, "
|
||||
"ignored");
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)
|
||||
{
|
||||
@@ -1700,6 +1971,131 @@ ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_set_client_hello_callback(ngx_ssl_t *ssl, ngx_ssl_client_hello_arg *cb)
|
||||
{
|
||||
#ifdef SSL_CLIENT_HELLO_SUCCESS
|
||||
|
||||
SSL_CTX_set_client_hello_cb(ssl->ctx, ngx_ssl_client_hello_callback, NULL);
|
||||
|
||||
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_client_hello_arg_index, cb) == 0)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_set_ex_data() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#elif defined OPENSSL_IS_BORINGSSL
|
||||
|
||||
SSL_CTX_set_select_certificate_cb(ssl->ctx, ngx_ssl_select_certificate);
|
||||
|
||||
if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_client_hello_arg_index, cb) == 0)
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"SSL_CTX_set_ex_data() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifdef SSL_CLIENT_HELLO_SUCCESS
|
||||
|
||||
int
|
||||
ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)
|
||||
{
|
||||
u_char *p;
|
||||
size_t len;
|
||||
ngx_int_t rc;
|
||||
ngx_str_t host;
|
||||
ngx_connection_t *c;
|
||||
ngx_ssl_client_hello_arg *cb;
|
||||
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
cb = SSL_CTX_get_ex_data(c->ssl->session_ctx,
|
||||
ngx_ssl_client_hello_arg_index);
|
||||
|
||||
if (SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name,
|
||||
(const unsigned char **) &p, &len)
|
||||
== 0)
|
||||
{
|
||||
ngx_str_null(&host);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 6066 mandates non-zero HostName length, we follow OpenSSL.
|
||||
* No more than one ServerName is expected.
|
||||
*/
|
||||
|
||||
if (len < 5
|
||||
|| (size_t) (p[0] << 8) + p[1] + 2 != len
|
||||
|| p[2] != TLSEXT_NAMETYPE_host_name
|
||||
|| (size_t) (p[3] << 8) + p[4] + 2 + 3 != len)
|
||||
{
|
||||
*ad = SSL_AD_DECODE_ERROR;
|
||||
return SSL_CLIENT_HELLO_ERROR;
|
||||
}
|
||||
|
||||
len -= 5;
|
||||
p += 5;
|
||||
|
||||
if (len > TLSEXT_MAXLEN_host_name || ngx_strlchr(p, p + len, '\0')) {
|
||||
*ad = SSL_AD_UNRECOGNIZED_NAME;
|
||||
return SSL_CLIENT_HELLO_ERROR;
|
||||
}
|
||||
|
||||
host.len = len;
|
||||
host.data = p;
|
||||
|
||||
done:
|
||||
|
||||
rc = cb->servername(ssl_conn, ad, &host);
|
||||
|
||||
if (rc == SSL_TLSEXT_ERR_ALERT_FATAL) {
|
||||
return SSL_CLIENT_HELLO_ERROR;
|
||||
}
|
||||
|
||||
return SSL_CLIENT_HELLO_SUCCESS;
|
||||
}
|
||||
|
||||
#elif defined OPENSSL_IS_BORINGSSL
|
||||
|
||||
enum ssl_select_cert_result_t ngx_ssl_select_certificate(
|
||||
const SSL_CLIENT_HELLO *client_hello)
|
||||
{
|
||||
int ad;
|
||||
ngx_int_t rc;
|
||||
ngx_ssl_conn_t *ssl_conn;
|
||||
ngx_connection_t *c;
|
||||
ngx_ssl_client_hello_arg *cb;
|
||||
|
||||
ssl_conn = client_hello->ssl;
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
cb = SSL_CTX_get_ex_data(c->ssl->session_ctx,
|
||||
ngx_ssl_client_hello_arg_index);
|
||||
|
||||
/*
|
||||
* BoringSSL sends a hardcoded "handshake_failure" alert on errors,
|
||||
* we use it to map SSL_AD_INTERNAL_ERROR. To preserve other alert
|
||||
* values, error handling is postponed to the servername callback.
|
||||
*/
|
||||
|
||||
rc = cb->servername(ssl_conn, &ad, NULL);
|
||||
|
||||
if (rc == SSL_TLSEXT_ERR_ALERT_FATAL && ad == SSL_AD_INTERNAL_ERROR) {
|
||||
return ssl_select_cert_error;
|
||||
}
|
||||
|
||||
return ssl_select_cert_success;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
|
||||
{
|
||||
@@ -3428,7 +3824,7 @@ ngx_ssl_shutdown_handler(ngx_event_t *ev)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
void
|
||||
ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
|
||||
char *text)
|
||||
{
|
||||
@@ -3859,10 +4255,9 @@ ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
|
||||
cert;
|
||||
cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
|
||||
{
|
||||
for (k = 0; k < ssl->certs.nelts; k++) {
|
||||
cert = ((X509 **) ssl->certs.elts)[k];
|
||||
|
||||
if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
|
||||
"X509_digest() failed");
|
||||
@@ -3876,9 +4271,7 @@ ngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL
|
||||
&& certificates != NULL)
|
||||
{
|
||||
if (ssl->certs.nelts == 0 && certificates != NULL) {
|
||||
/*
|
||||
* If certificates are loaded dynamically, we use certificate
|
||||
* names as specified in the configuration (with variables).
|
||||
@@ -4026,7 +4419,6 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_ssl_sess_id_t *sess_id;
|
||||
ngx_ssl_session_cache_t *cache;
|
||||
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
|
||||
|
||||
#ifdef TLS1_3_VERSION
|
||||
|
||||
@@ -4053,7 +4445,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = buf;
|
||||
p = ngx_ssl_session_buffer;
|
||||
i2d_SSL_SESSION(sess, &p);
|
||||
|
||||
session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);
|
||||
@@ -4117,7 +4509,7 @@ ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
|
||||
|
||||
#endif
|
||||
|
||||
ngx_memcpy(sess_id->session, buf, len);
|
||||
ngx_memcpy(sess_id->session, ngx_ssl_session_buffer, len);
|
||||
ngx_memcpy(sess_id->id, session_id, session_id_length);
|
||||
|
||||
hash = ngx_crc32_short(session_id, session_id_length);
|
||||
@@ -4171,12 +4563,11 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
|
||||
const u_char *p;
|
||||
ngx_shm_zone_t *shm_zone;
|
||||
ngx_slab_pool_t *shpool;
|
||||
ngx_connection_t *c;
|
||||
ngx_rbtree_node_t *node, *sentinel;
|
||||
ngx_ssl_session_t *sess;
|
||||
ngx_ssl_sess_id_t *sess_id;
|
||||
ngx_ssl_session_cache_t *cache;
|
||||
u_char buf[NGX_SSL_MAX_SESSION_SIZE];
|
||||
ngx_connection_t *c;
|
||||
|
||||
hash = ngx_crc32_short((u_char *) (uintptr_t) id, (size_t) len);
|
||||
*copy = 0;
|
||||
@@ -4224,11 +4615,11 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
|
||||
if (sess_id->expire > ngx_time()) {
|
||||
slen = sess_id->len;
|
||||
|
||||
ngx_memcpy(buf, sess_id->session, slen);
|
||||
ngx_memcpy(ngx_ssl_session_buffer, sess_id->session, slen);
|
||||
|
||||
ngx_shmtx_unlock(&shpool->mutex);
|
||||
|
||||
p = buf;
|
||||
p = ngx_ssl_session_buffer;
|
||||
sess = d2i_SSL_SESSION(NULL, &p, slen);
|
||||
|
||||
return sess;
|
||||
@@ -4890,14 +5281,21 @@ ngx_ssl_cleanup_ctx(void *data)
|
||||
{
|
||||
ngx_ssl_t *ssl = data;
|
||||
|
||||
X509 *cert, *next;
|
||||
X509 *cert;
|
||||
u_char *p;
|
||||
ngx_uint_t i;
|
||||
|
||||
cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
|
||||
for (i = 0; i < ssl->certs.nelts; i++) {
|
||||
cert = ((X509 **) ssl->certs.elts)[i];
|
||||
|
||||
p = X509_get_ex_data(cert, ngx_ssl_certificate_comp_index);
|
||||
|
||||
if (p) {
|
||||
ngx_free(p);
|
||||
X509_set_ex_data(cert, ngx_ssl_certificate_comp_index, NULL);
|
||||
}
|
||||
|
||||
while (cert) {
|
||||
next = X509_get_ex_data(cert, ngx_ssl_next_certificate_index);
|
||||
X509_free(cert);
|
||||
cert = next;
|
||||
}
|
||||
|
||||
SSL_CTX_free(ssl->ctx);
|
||||
@@ -5178,7 +5576,8 @@ ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
#ifdef SSL_get_negotiated_group
|
||||
|
||||
int nid;
|
||||
int nid;
|
||||
const char *name;
|
||||
|
||||
nid = SSL_get_negotiated_group(c->ssl->connection);
|
||||
|
||||
@@ -5190,14 +5589,20 @@ ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
s->len = sizeof("0x0000") - 1;
|
||||
name = SSL_group_to_name(c->ssl->connection, nid);
|
||||
|
||||
s->len = name ? ngx_strlen(name) : sizeof("0x0000") - 1;
|
||||
s->data = ngx_pnalloc(pool, s->len);
|
||||
if (s->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_sprintf(s->data, "0x%04xd", nid & 0xffff);
|
||||
if (name) {
|
||||
ngx_memcpy(s->data, name, s->len);
|
||||
|
||||
} else {
|
||||
ngx_sprintf(s->data, "0x%04xd", nid & 0xffff);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -5217,6 +5622,7 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
int *curves, n, i, nid;
|
||||
u_char *p;
|
||||
size_t len;
|
||||
const char *name;
|
||||
|
||||
n = SSL_get1_curves(c->ssl->connection, NULL);
|
||||
|
||||
@@ -5237,7 +5643,9 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
nid = curves[i];
|
||||
|
||||
if (nid & TLSEXT_nid_unknown) {
|
||||
len += sizeof("0x0000") - 1;
|
||||
name = SSL_group_to_name(c->ssl->connection, nid);
|
||||
|
||||
len += name ? ngx_strlen(name) : sizeof("0x0000") - 1;
|
||||
|
||||
} else {
|
||||
len += ngx_strlen(OBJ_nid2sn(nid));
|
||||
@@ -5257,7 +5665,10 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
nid = curves[i];
|
||||
|
||||
if (nid & TLSEXT_nid_unknown) {
|
||||
p = ngx_sprintf(p, "0x%04xd", nid & 0xffff);
|
||||
name = SSL_group_to_name(c->ssl->connection, nid);
|
||||
|
||||
p = name ? ngx_cpymem(p, name, ngx_strlen(name))
|
||||
: ngx_sprintf(p, "0x%04xd", nid & 0xffff);
|
||||
|
||||
} else {
|
||||
p = ngx_sprintf(p, "%s", OBJ_nid2sn(nid));
|
||||
@@ -5280,6 +5691,32 @@ ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_get_sigalg(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
#ifdef SSL_get0_signature_name
|
||||
|
||||
const char *name;
|
||||
|
||||
if (SSL_get0_signature_name(c->ssl->connection, &name)) {
|
||||
s->len = ngx_strlen(name);
|
||||
s->data = ngx_pnalloc(pool, s->len);
|
||||
if (s->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(s->data, name, s->len);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
s->len = 0;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
@@ -5409,6 +5846,81 @@ ngx_ssl_get_alpn_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_get_ech_status(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
#ifdef SSL_OP_ECH_GREASE
|
||||
int echrv;
|
||||
char *inner_sni, *outer_sni;
|
||||
|
||||
inner_sni = NULL;
|
||||
outer_sni = NULL;
|
||||
|
||||
echrv = SSL_ech_get1_status(c->ssl->connection, &inner_sni, &outer_sni);
|
||||
|
||||
switch (echrv) {
|
||||
case SSL_ECH_STATUS_NOT_TRIED:
|
||||
ngx_str_set(s, "NOT_TRIED");
|
||||
break;
|
||||
case SSL_ECH_STATUS_SUCCESS:
|
||||
ngx_str_set(s, "SUCCESS");
|
||||
break;
|
||||
case SSL_ECH_STATUS_GREASE:
|
||||
ngx_str_set(s, "GREASE");
|
||||
break;
|
||||
case SSL_ECH_STATUS_BACKEND:
|
||||
ngx_str_set(s, "BACKEND");
|
||||
break;
|
||||
default:
|
||||
ngx_str_set(s, "FAILED");
|
||||
break;
|
||||
}
|
||||
|
||||
OPENSSL_free(inner_sni);
|
||||
OPENSSL_free(outer_sni);
|
||||
#else
|
||||
s->len = 0;
|
||||
#endif
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_get_ech_outer_server_name(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s)
|
||||
{
|
||||
#if defined(SSL_OP_ECH_GREASE)
|
||||
int echrv;
|
||||
char *inner_sni, *outer_sni;
|
||||
|
||||
inner_sni = NULL;
|
||||
outer_sni = NULL;
|
||||
|
||||
echrv = SSL_ech_get1_status(c->ssl->connection, &inner_sni, &outer_sni);
|
||||
|
||||
if (echrv == SSL_ECH_STATUS_SUCCESS && outer_sni) {
|
||||
s->len = ngx_strlen(outer_sni);
|
||||
|
||||
s->data = ngx_pnalloc(pool, s->len);
|
||||
if (s->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(s->data, outer_sni, s->len);
|
||||
|
||||
} else {
|
||||
s->len = 0;
|
||||
}
|
||||
|
||||
OPENSSL_free(inner_sni);
|
||||
OPENSSL_free(outer_sni);
|
||||
#else
|
||||
s->len = 0;
|
||||
#endif
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
@@ -6025,6 +6537,32 @@ ngx_ssl_parse_time(
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_ssl_get_client_sigalg(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
|
||||
{
|
||||
#ifdef SSL_get0_peer_signature_name
|
||||
|
||||
const char *name;
|
||||
|
||||
if (SSL_get0_peer_signature_name(c->ssl->connection, &name)) {
|
||||
s->len = ngx_strlen(name);
|
||||
s->data = ngx_pnalloc(pool, s->len);
|
||||
if (s->data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(s->data, name, s->len);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
s->len = 0;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_openssl_create_conf(ngx_cycle_t *cycle)
|
||||
{
|
||||
|
||||
@@ -19,19 +19,13 @@
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/crypto.h>
|
||||
#ifndef OPENSSL_NO_DH
|
||||
#include <openssl/dh.h>
|
||||
#endif
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
#include <openssl/engine.h>
|
||||
#endif
|
||||
#include <openssl/evp.h>
|
||||
#if (NGX_QUIC)
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#include <openssl/hkdf.h>
|
||||
#include <openssl/chacha.h>
|
||||
#else
|
||||
#include <openssl/kdf.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <openssl/hmac.h>
|
||||
#ifndef OPENSSL_NO_OCSP
|
||||
#include <openssl/ocsp.h>
|
||||
@@ -83,7 +77,23 @@
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t;
|
||||
#ifdef OPENSSL_NO_DEPRECATED_3_4
|
||||
#define SSL_SESSION_get_time(s) SSL_SESSION_get_time_ex(s)
|
||||
#define SSL_SESSION_set_time(s, t) SSL_SESSION_set_time_ex(s, t)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef OPENSSL_NO_DEPRECATED_3_0
|
||||
#define EVP_CIPHER_CTX_cipher(c) EVP_CIPHER_CTX_get0_cipher(c)
|
||||
#endif
|
||||
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
|
||||
#define SSL_group_to_name(s, nid) NULL
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct ngx_ssl_ocsp_s ngx_ssl_ocsp_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
@@ -98,6 +108,12 @@ struct ngx_ssl_s {
|
||||
SSL_CTX *ctx;
|
||||
ngx_log_t *log;
|
||||
size_t buffer_size;
|
||||
|
||||
ngx_array_t certs;
|
||||
|
||||
ngx_rbtree_t staple_rbtree;
|
||||
ngx_rbtree_node_t staple_sentinel;
|
||||
|
||||
ngx_ssl_dyn_rec_t dyn_rec;
|
||||
};
|
||||
|
||||
@@ -137,6 +153,7 @@ struct ngx_ssl_connection_s {
|
||||
unsigned in_ocsp:1;
|
||||
unsigned early_preread:1;
|
||||
unsigned write_blocked:1;
|
||||
unsigned sni_accepted:1;
|
||||
|
||||
ngx_ssl_dyn_rec_t dyn_rec;
|
||||
ngx_msec_t dyn_rec_last_write;
|
||||
@@ -187,6 +204,13 @@ typedef struct {
|
||||
} ngx_ssl_session_cache_t;
|
||||
|
||||
|
||||
typedef int (*ngx_ssl_servername_pt)(ngx_ssl_conn_t *, int *, void *);
|
||||
|
||||
typedef struct {
|
||||
ngx_ssl_servername_pt servername;
|
||||
} ngx_ssl_client_hello_arg;
|
||||
|
||||
|
||||
#define NGX_SSL_SSLv2 0x0002
|
||||
#define NGX_SSL_SSLv3 0x0004
|
||||
#define NGX_SSL_TLSv1 0x0008
|
||||
@@ -195,12 +219,27 @@ typedef struct {
|
||||
#define NGX_SSL_TLSv1_3 0x0040
|
||||
|
||||
|
||||
#if (defined SSL_OP_NO_TLSv1_2 || defined SSL_OP_NO_TLSv1_3)
|
||||
#define NGX_SSL_DEFAULT_PROTOCOLS (NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3)
|
||||
#else
|
||||
#define NGX_SSL_DEFAULT_PROTOCOLS (NGX_SSL_TLSv1|NGX_SSL_TLSv1_1)
|
||||
#endif
|
||||
|
||||
|
||||
#define NGX_SSL_BUFFER 1
|
||||
#define NGX_SSL_CLIENT 2
|
||||
|
||||
#define NGX_SSL_BUFSIZE 16384
|
||||
|
||||
|
||||
#define NGX_SSL_CACHE_CERT 0
|
||||
#define NGX_SSL_CACHE_PKEY 1
|
||||
#define NGX_SSL_CACHE_CRL 2
|
||||
#define NGX_SSL_CACHE_CA 3
|
||||
|
||||
#define NGX_SSL_CACHE_INVALIDATE 0x80000000
|
||||
|
||||
|
||||
ngx_int_t ngx_ssl_init(ngx_log_t *log);
|
||||
ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);
|
||||
|
||||
@@ -209,7 +248,10 @@ ngx_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
|
||||
ngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
|
||||
ngx_str_t *cert, ngx_str_t *key, ngx_ssl_cache_t *cache,
|
||||
ngx_array_t *passwords);
|
||||
ngx_int_t ngx_ssl_certificate_compression(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_uint_t enable);
|
||||
|
||||
ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
|
||||
ngx_uint_t prefer_server_ciphers);
|
||||
@@ -232,10 +274,19 @@ ngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s);
|
||||
void ngx_ssl_ocsp_cleanup(ngx_connection_t *c);
|
||||
ngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data);
|
||||
|
||||
ngx_ssl_cache_t *ngx_ssl_cache_init(ngx_pool_t *pool, ngx_uint_t max,
|
||||
time_t valid, time_t inactive);
|
||||
void *ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
|
||||
ngx_str_t *path, void *data);
|
||||
void *ngx_ssl_cache_connection_fetch(ngx_ssl_cache_t *cache, ngx_pool_t *pool,
|
||||
ngx_uint_t index, char **err, ngx_str_t *path, void *data);
|
||||
|
||||
ngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);
|
||||
ngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf,
|
||||
ngx_array_t *passwords);
|
||||
ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
|
||||
ngx_int_t ngx_ssl_ech_files(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_array_t *filename);
|
||||
ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
|
||||
ngx_int_t ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_uint_t enable);
|
||||
@@ -251,6 +302,15 @@ ngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_array_t *paths);
|
||||
ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
|
||||
|
||||
ngx_int_t ngx_ssl_set_client_hello_callback(ngx_ssl_t *ssl,
|
||||
ngx_ssl_client_hello_arg *cb);
|
||||
#ifdef SSL_CLIENT_HELLO_SUCCESS
|
||||
int ngx_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);
|
||||
#elif defined OPENSSL_IS_BORINGSSL
|
||||
enum ssl_select_cert_result_t ngx_ssl_select_certificate(
|
||||
const SSL_CLIENT_HELLO *client_hello);
|
||||
#endif
|
||||
|
||||
ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
|
||||
ngx_uint_t flags);
|
||||
|
||||
@@ -284,6 +344,8 @@ ngx_int_t ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_sigalg(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
@@ -292,6 +354,10 @@ ngx_int_t ngx_ssl_get_early_data(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_ech_status(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_ech_outer_server_name(ngx_connection_t *c,
|
||||
ngx_pool_t *pool, ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_alpn_protocol(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
@@ -320,6 +386,8 @@ ngx_int_t ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
ngx_int_t ngx_ssl_get_client_sigalg(ngx_connection_t *c, ngx_pool_t *pool,
|
||||
ngx_str_t *s);
|
||||
|
||||
|
||||
ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
|
||||
@@ -333,6 +401,8 @@ ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
|
||||
off_t limit);
|
||||
void ngx_ssl_free_buffer(ngx_connection_t *c);
|
||||
ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);
|
||||
void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
|
||||
char *text);
|
||||
void ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
|
||||
char *fmt, ...);
|
||||
void ngx_ssl_cleanup_ctx(void *data);
|
||||
@@ -343,10 +413,13 @@ extern int ngx_ssl_server_conf_index;
|
||||
extern int ngx_ssl_session_cache_index;
|
||||
extern int ngx_ssl_ticket_keys_index;
|
||||
extern int ngx_ssl_ocsp_index;
|
||||
extern int ngx_ssl_certificate_index;
|
||||
extern int ngx_ssl_next_certificate_index;
|
||||
extern int ngx_ssl_index;
|
||||
extern int ngx_ssl_certificate_name_index;
|
||||
extern int ngx_ssl_stapling_index;
|
||||
extern int ngx_ssl_certificate_comp_index;
|
||||
extern int ngx_ssl_client_hello_arg_index;
|
||||
|
||||
|
||||
extern u_char ngx_ssl_session_buffer[NGX_SSL_MAX_SESSION_SIZE];
|
||||
|
||||
|
||||
#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */
|
||||
@@ -0,0 +1,1267 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
|
||||
#ifdef ERR_R_OSSL_STORE_LIB
|
||||
#include <openssl/store.h>
|
||||
#include <openssl/ui.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define NGX_SSL_CACHE_PATH 0
|
||||
#define NGX_SSL_CACHE_DATA 1
|
||||
#define NGX_SSL_CACHE_ENGINE 2
|
||||
#define NGX_SSL_CACHE_STORE 3
|
||||
|
||||
#define NGX_SSL_CACHE_DISABLED (ngx_array_t *) (uintptr_t) -1
|
||||
|
||||
|
||||
#define ngx_ssl_cache_get_conf(cycle) \
|
||||
(ngx_ssl_cache_t *) ngx_get_conf(cycle->conf_ctx, ngx_openssl_cache_module)
|
||||
|
||||
#define ngx_ssl_cache_get_old_conf(cycle) \
|
||||
cycle->old_cycle->conf_ctx ? ngx_ssl_cache_get_conf(cycle->old_cycle) \
|
||||
: NULL
|
||||
|
||||
|
||||
typedef struct {
|
||||
unsigned type:2;
|
||||
unsigned len:30;
|
||||
u_char *data;
|
||||
} ngx_ssl_cache_key_t;
|
||||
|
||||
|
||||
typedef void *(*ngx_ssl_cache_create_pt)(ngx_ssl_cache_key_t *id, char **err,
|
||||
void *data);
|
||||
typedef void (*ngx_ssl_cache_free_pt)(void *data);
|
||||
typedef void *(*ngx_ssl_cache_ref_pt)(char **err, void *data);
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_ssl_cache_create_pt create;
|
||||
ngx_ssl_cache_free_pt free;
|
||||
ngx_ssl_cache_ref_pt ref;
|
||||
} ngx_ssl_cache_type_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_rbtree_node_t node;
|
||||
ngx_queue_t queue;
|
||||
ngx_ssl_cache_key_t id;
|
||||
ngx_ssl_cache_type_t *type;
|
||||
void *value;
|
||||
|
||||
time_t created;
|
||||
time_t accessed;
|
||||
|
||||
time_t mtime;
|
||||
ngx_file_uniq_t uniq;
|
||||
} ngx_ssl_cache_node_t;
|
||||
|
||||
|
||||
struct ngx_ssl_cache_s {
|
||||
ngx_rbtree_t rbtree;
|
||||
ngx_rbtree_node_t sentinel;
|
||||
ngx_queue_t expire_queue;
|
||||
|
||||
ngx_flag_t inheritable;
|
||||
|
||||
ngx_uint_t current;
|
||||
ngx_uint_t max;
|
||||
time_t valid;
|
||||
time_t inactive;
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t *pwd;
|
||||
unsigned encrypted:1;
|
||||
} ngx_ssl_cache_pwd_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index,
|
||||
ngx_str_t *path, ngx_ssl_cache_key_t *id);
|
||||
static ngx_ssl_cache_node_t *ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache,
|
||||
ngx_ssl_cache_type_t *type, ngx_ssl_cache_key_t *id, uint32_t hash);
|
||||
static void ngx_ssl_cache_expire(ngx_ssl_cache_t *cache, ngx_uint_t n,
|
||||
ngx_log_t *log);
|
||||
|
||||
static void *ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err,
|
||||
void *data);
|
||||
static void ngx_ssl_cache_cert_free(void *data);
|
||||
static void *ngx_ssl_cache_cert_ref(char **err, void *data);
|
||||
|
||||
static void *ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err,
|
||||
void *data);
|
||||
static int ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag,
|
||||
void *userdata);
|
||||
static void ngx_ssl_cache_pkey_free(void *data);
|
||||
static void *ngx_ssl_cache_pkey_ref(char **err, void *data);
|
||||
|
||||
static void *ngx_ssl_cache_crl_create(ngx_ssl_cache_key_t *id, char **err,
|
||||
void *data);
|
||||
static void ngx_ssl_cache_crl_free(void *data);
|
||||
static void *ngx_ssl_cache_crl_ref(char **err, void *data);
|
||||
|
||||
static void *ngx_ssl_cache_ca_create(ngx_ssl_cache_key_t *id, char **err,
|
||||
void *data);
|
||||
|
||||
static BIO *ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err);
|
||||
|
||||
static void *ngx_openssl_cache_create_conf(ngx_cycle_t *cycle);
|
||||
static char *ngx_openssl_cache_init_conf(ngx_cycle_t *cycle, void *conf);
|
||||
static void ngx_ssl_cache_cleanup(void *data);
|
||||
static void ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
|
||||
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
|
||||
static void ngx_ssl_cache_node_free(ngx_rbtree_t *rbtree,
|
||||
ngx_ssl_cache_node_t *cn);
|
||||
|
||||
static ngx_int_t ngx_openssl_cache_init_worker(ngx_cycle_t *cycle);
|
||||
|
||||
|
||||
static ngx_command_t ngx_openssl_cache_commands[] = {
|
||||
|
||||
{ ngx_string("ssl_object_cache_inheritable"),
|
||||
NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
0,
|
||||
offsetof(ngx_ssl_cache_t, inheritable),
|
||||
NULL },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
|
||||
static ngx_core_module_t ngx_openssl_cache_module_ctx = {
|
||||
ngx_string("openssl_cache"),
|
||||
ngx_openssl_cache_create_conf,
|
||||
ngx_openssl_cache_init_conf
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_openssl_cache_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_openssl_cache_module_ctx, /* module context */
|
||||
ngx_openssl_cache_commands, /* module directives */
|
||||
NGX_CORE_MODULE, /* module type */
|
||||
NULL, /* init master */
|
||||
NULL, /* init module */
|
||||
ngx_openssl_cache_init_worker, /* init process */
|
||||
NULL, /* init thread */
|
||||
NULL, /* exit thread */
|
||||
NULL, /* exit process */
|
||||
NULL, /* exit master */
|
||||
NGX_MODULE_V1_PADDING
|
||||
};
|
||||
|
||||
|
||||
static ngx_ssl_cache_type_t ngx_ssl_cache_types[] = {
|
||||
|
||||
/* NGX_SSL_CACHE_CERT */
|
||||
{ ngx_ssl_cache_cert_create,
|
||||
ngx_ssl_cache_cert_free,
|
||||
ngx_ssl_cache_cert_ref },
|
||||
|
||||
/* NGX_SSL_CACHE_PKEY */
|
||||
{ ngx_ssl_cache_pkey_create,
|
||||
ngx_ssl_cache_pkey_free,
|
||||
ngx_ssl_cache_pkey_ref },
|
||||
|
||||
/* NGX_SSL_CACHE_CRL */
|
||||
{ ngx_ssl_cache_crl_create,
|
||||
ngx_ssl_cache_crl_free,
|
||||
ngx_ssl_cache_crl_ref },
|
||||
|
||||
/* NGX_SSL_CACHE_CA */
|
||||
{ ngx_ssl_cache_ca_create,
|
||||
ngx_ssl_cache_cert_free,
|
||||
ngx_ssl_cache_cert_ref }
|
||||
};
|
||||
|
||||
|
||||
void *
|
||||
ngx_ssl_cache_fetch(ngx_conf_t *cf, ngx_uint_t index, char **err,
|
||||
ngx_str_t *path, void *data)
|
||||
{
|
||||
void *value;
|
||||
time_t mtime;
|
||||
uint32_t hash;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t invalidate;
|
||||
ngx_file_uniq_t uniq;
|
||||
ngx_file_info_t fi;
|
||||
ngx_ssl_cache_t *cache, *old_cache;
|
||||
ngx_ssl_cache_key_t id;
|
||||
ngx_ssl_cache_type_t *type;
|
||||
ngx_ssl_cache_node_t *cn;
|
||||
|
||||
*err = NULL;
|
||||
|
||||
invalidate = index & NGX_SSL_CACHE_INVALIDATE;
|
||||
index &= ~NGX_SSL_CACHE_INVALIDATE;
|
||||
|
||||
if (ngx_ssl_cache_init_key(cf->pool, index, path, &id) != NGX_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (id.type == NGX_SSL_CACHE_DATA) {
|
||||
invalidate = 0;
|
||||
}
|
||||
|
||||
cache = (ngx_ssl_cache_t *) ngx_get_conf(cf->cycle->conf_ctx,
|
||||
ngx_openssl_cache_module);
|
||||
|
||||
type = &ngx_ssl_cache_types[index];
|
||||
hash = ngx_murmur_hash2(id.data, id.len);
|
||||
|
||||
cn = ngx_ssl_cache_lookup(cache, type, &id, hash);
|
||||
|
||||
if (cn != NULL) {
|
||||
if (!invalidate) {
|
||||
return type->ref(err, cn->value);
|
||||
}
|
||||
|
||||
type->free(cn->value);
|
||||
ngx_rbtree_delete(&cache->rbtree, &cn->node);
|
||||
}
|
||||
|
||||
value = NULL;
|
||||
|
||||
if (id.type == NGX_SSL_CACHE_PATH
|
||||
&& (rc = ngx_file_info(id.data, &fi)) != NGX_FILE_ERROR)
|
||||
{
|
||||
mtime = ngx_file_mtime(&fi);
|
||||
uniq = ngx_file_uniq(&fi);
|
||||
|
||||
} else {
|
||||
rc = NGX_FILE_ERROR;
|
||||
mtime = 0;
|
||||
uniq = 0;
|
||||
}
|
||||
|
||||
/* try to use a reference from the old cycle */
|
||||
|
||||
old_cache = ngx_ssl_cache_get_old_conf(cf->cycle);
|
||||
|
||||
if (old_cache && old_cache->inheritable && !invalidate) {
|
||||
cn = ngx_ssl_cache_lookup(old_cache, type, &id, hash);
|
||||
|
||||
if (cn != NULL) {
|
||||
switch (id.type) {
|
||||
|
||||
case NGX_SSL_CACHE_DATA:
|
||||
value = type->ref(err, cn->value);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (rc != NGX_FILE_ERROR
|
||||
&& uniq == cn->uniq && mtime == cn->mtime)
|
||||
{
|
||||
value = type->ref(err, cn->value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value == NULL) {
|
||||
value = type->create(&id, err, &data);
|
||||
|
||||
if (value == NULL || data == NGX_SSL_CACHE_DISABLED) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
cn = ngx_palloc(cf->pool, sizeof(ngx_ssl_cache_node_t) + id.len + 1);
|
||||
if (cn == NULL) {
|
||||
type->free(value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cn->node.key = hash;
|
||||
cn->id.data = (u_char *)(cn + 1);
|
||||
cn->id.len = id.len;
|
||||
cn->id.type = id.type;
|
||||
cn->type = type;
|
||||
cn->value = value;
|
||||
cn->mtime = mtime;
|
||||
cn->uniq = uniq;
|
||||
|
||||
ngx_cpystrn(cn->id.data, id.data, id.len + 1);
|
||||
|
||||
ngx_queue_init(&cn->queue);
|
||||
|
||||
ngx_rbtree_insert(&cache->rbtree, &cn->node);
|
||||
|
||||
return type->ref(err, cn->value);
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
ngx_ssl_cache_connection_fetch(ngx_ssl_cache_t *cache, ngx_pool_t *pool,
|
||||
ngx_uint_t index, char **err, ngx_str_t *path, void *data)
|
||||
{
|
||||
void *value;
|
||||
time_t now;
|
||||
uint32_t hash;
|
||||
ngx_uint_t invalidate;
|
||||
ngx_file_info_t fi;
|
||||
ngx_ssl_cache_key_t id;
|
||||
ngx_ssl_cache_type_t *type;
|
||||
ngx_ssl_cache_node_t *cn;
|
||||
|
||||
*err = NULL;
|
||||
|
||||
invalidate = index & NGX_SSL_CACHE_INVALIDATE;
|
||||
index &= ~NGX_SSL_CACHE_INVALIDATE;
|
||||
|
||||
if (ngx_ssl_cache_init_key(pool, index, path, &id) != NGX_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
type = &ngx_ssl_cache_types[index];
|
||||
|
||||
if (cache == NULL) {
|
||||
return type->create(&id, err, &data);
|
||||
}
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
hash = ngx_murmur_hash2(id.data, id.len);
|
||||
|
||||
cn = ngx_ssl_cache_lookup(cache, type, &id, hash);
|
||||
|
||||
if (cn != NULL) {
|
||||
ngx_queue_remove(&cn->queue);
|
||||
|
||||
if (id.type == NGX_SSL_CACHE_DATA) {
|
||||
goto found;
|
||||
}
|
||||
|
||||
if (!invalidate && now - cn->created <= cache->valid) {
|
||||
goto found;
|
||||
}
|
||||
|
||||
switch (id.type) {
|
||||
|
||||
case NGX_SSL_CACHE_PATH:
|
||||
|
||||
if (ngx_file_info(id.data, &fi) != NGX_FILE_ERROR) {
|
||||
|
||||
if (!invalidate
|
||||
&& ngx_file_uniq(&fi) == cn->uniq
|
||||
&& ngx_file_mtime(&fi) == cn->mtime)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
cn->mtime = ngx_file_mtime(&fi);
|
||||
cn->uniq = ngx_file_uniq(&fi);
|
||||
|
||||
} else {
|
||||
cn->mtime = 0;
|
||||
cn->uniq = 0;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_CORE, pool->log, 0,
|
||||
"update cached ssl object: %s", cn->id.data);
|
||||
|
||||
type->free(cn->value);
|
||||
|
||||
value = type->create(&id, err, &data);
|
||||
|
||||
if (value == NULL || data == NGX_SSL_CACHE_DISABLED) {
|
||||
ngx_rbtree_delete(&cache->rbtree, &cn->node);
|
||||
|
||||
cache->current--;
|
||||
|
||||
ngx_free(cn);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
cn->value = value;
|
||||
}
|
||||
|
||||
cn->created = now;
|
||||
|
||||
goto found;
|
||||
}
|
||||
|
||||
value = type->create(&id, err, &data);
|
||||
|
||||
if (value == NULL || data == NGX_SSL_CACHE_DISABLED) {
|
||||
return value;
|
||||
}
|
||||
|
||||
cn = ngx_alloc(sizeof(ngx_ssl_cache_node_t) + id.len + 1, pool->log);
|
||||
if (cn == NULL) {
|
||||
type->free(value);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cn->node.key = hash;
|
||||
cn->id.data = (u_char *)(cn + 1);
|
||||
cn->id.len = id.len;
|
||||
cn->id.type = id.type;
|
||||
cn->type = type;
|
||||
cn->value = value;
|
||||
cn->created = now;
|
||||
|
||||
ngx_cpystrn(cn->id.data, id.data, id.len + 1);
|
||||
|
||||
if (id.type == NGX_SSL_CACHE_PATH) {
|
||||
|
||||
if (ngx_file_info(id.data, &fi) != NGX_FILE_ERROR) {
|
||||
cn->mtime = ngx_file_mtime(&fi);
|
||||
cn->uniq = ngx_file_uniq(&fi);
|
||||
|
||||
} else {
|
||||
cn->mtime = 0;
|
||||
cn->uniq = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_ssl_cache_expire(cache, 1, pool->log);
|
||||
|
||||
if (cache->current >= cache->max) {
|
||||
ngx_ssl_cache_expire(cache, 0, pool->log);
|
||||
}
|
||||
|
||||
ngx_rbtree_insert(&cache->rbtree, &cn->node);
|
||||
|
||||
cache->current++;
|
||||
|
||||
found:
|
||||
|
||||
cn->accessed = now;
|
||||
|
||||
ngx_queue_insert_head(&cache->expire_queue, &cn->queue);
|
||||
|
||||
return type->ref(err, cn->value);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_ssl_cache_init_key(ngx_pool_t *pool, ngx_uint_t index, ngx_str_t *path,
|
||||
ngx_ssl_cache_key_t *id)
|
||||
{
|
||||
if (index <= NGX_SSL_CACHE_PKEY
|
||||
&& ngx_strncmp(path->data, "data:", sizeof("data:") - 1) == 0)
|
||||
{
|
||||
id->type = NGX_SSL_CACHE_DATA;
|
||||
|
||||
} else if (index == NGX_SSL_CACHE_PKEY
|
||||
&& ngx_strncmp(path->data, "engine:", sizeof("engine:") - 1) == 0)
|
||||
{
|
||||
id->type = NGX_SSL_CACHE_ENGINE;
|
||||
|
||||
} else if (index == NGX_SSL_CACHE_PKEY
|
||||
&& ngx_strncmp(path->data, "store:", sizeof("store:") - 1) == 0)
|
||||
{
|
||||
id->type = NGX_SSL_CACHE_STORE;
|
||||
|
||||
} else {
|
||||
if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, path)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
id->type = NGX_SSL_CACHE_PATH;
|
||||
}
|
||||
|
||||
id->len = path->len;
|
||||
id->data = path->data;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_ssl_cache_node_t *
|
||||
ngx_ssl_cache_lookup(ngx_ssl_cache_t *cache, ngx_ssl_cache_type_t *type,
|
||||
ngx_ssl_cache_key_t *id, uint32_t hash)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_rbtree_node_t *node, *sentinel;
|
||||
ngx_ssl_cache_node_t *cn;
|
||||
|
||||
node = cache->rbtree.root;
|
||||
sentinel = cache->rbtree.sentinel;
|
||||
|
||||
while (node != sentinel) {
|
||||
|
||||
if (hash < node->key) {
|
||||
node = node->left;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hash > node->key) {
|
||||
node = node->right;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* hash == node->key */
|
||||
|
||||
cn = (ngx_ssl_cache_node_t *) node;
|
||||
|
||||
if (type < cn->type) {
|
||||
node = node->left;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type > cn->type) {
|
||||
node = node->right;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* type == cn->type */
|
||||
|
||||
rc = ngx_memn2cmp(id->data, cn->id.data, id->len, cn->id.len);
|
||||
|
||||
if (rc == 0) {
|
||||
return cn;
|
||||
}
|
||||
|
||||
node = (rc < 0) ? node->left : node->right;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_cache_expire(ngx_ssl_cache_t *cache, ngx_uint_t n,
|
||||
ngx_log_t *log)
|
||||
{
|
||||
time_t now;
|
||||
ngx_queue_t *q;
|
||||
ngx_ssl_cache_node_t *cn;
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
while (n < 3) {
|
||||
|
||||
if (ngx_queue_empty(&cache->expire_queue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
q = ngx_queue_last(&cache->expire_queue);
|
||||
|
||||
cn = ngx_queue_data(q, ngx_ssl_cache_node_t, queue);
|
||||
|
||||
if (n++ != 0 && now - cn->accessed <= cache->inactive) {
|
||||
return;
|
||||
}
|
||||
|
||||
ngx_ssl_cache_node_free(&cache->rbtree, cn);
|
||||
|
||||
cache->current--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_ssl_cache_cert_create(ngx_ssl_cache_key_t *id, char **err, void *data)
|
||||
{
|
||||
BIO *bio;
|
||||
X509 *x509;
|
||||
u_long n;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
chain = sk_X509_new_null();
|
||||
if (chain == NULL) {
|
||||
*err = "sk_X509_new_null() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bio = ngx_ssl_cache_create_bio(id, err);
|
||||
if (bio == NULL) {
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* certificate itself */
|
||||
|
||||
x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
|
||||
if (x509 == NULL) {
|
||||
*err = "PEM_read_bio_X509_AUX() failed";
|
||||
BIO_free(bio);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sk_X509_push(chain, x509) == 0) {
|
||||
*err = "sk_X509_push() failed";
|
||||
BIO_free(bio);
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* rest of the chain */
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
if (x509 == NULL) {
|
||||
n = ERR_peek_last_error();
|
||||
|
||||
if (ERR_GET_LIB(n) == ERR_LIB_PEM
|
||||
&& ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
|
||||
{
|
||||
/* end of file */
|
||||
ERR_clear_error();
|
||||
break;
|
||||
}
|
||||
|
||||
/* some real error */
|
||||
|
||||
*err = "PEM_read_bio_X509() failed";
|
||||
BIO_free(bio);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sk_X509_push(chain, x509) == 0) {
|
||||
*err = "sk_X509_push() failed";
|
||||
BIO_free(bio);
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_cache_cert_free(void *data)
|
||||
{
|
||||
sk_X509_pop_free(data, X509_free);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_ssl_cache_cert_ref(char **err, void *data)
|
||||
{
|
||||
int n, i;
|
||||
X509 *x509;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
chain = sk_X509_dup(data);
|
||||
if (chain == NULL) {
|
||||
*err = "sk_X509_dup() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n = sk_X509_num(chain);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
x509 = sk_X509_value(chain, i);
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
|
||||
X509_up_ref(x509);
|
||||
#else
|
||||
CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509);
|
||||
#endif
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_ssl_cache_pkey_create(ngx_ssl_cache_key_t *id, char **err, void *data)
|
||||
{
|
||||
ngx_array_t **passwords = data;
|
||||
|
||||
BIO *bio;
|
||||
EVP_PKEY *pkey;
|
||||
ngx_uint_t tries;
|
||||
pem_password_cb *cb;
|
||||
ngx_ssl_cache_pwd_t cb_data, *pwd;
|
||||
|
||||
if (id->type == NGX_SSL_CACHE_ENGINE) {
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
|
||||
u_char *p, *last;
|
||||
ENGINE *engine;
|
||||
|
||||
p = id->data + sizeof("engine:") - 1;
|
||||
last = (u_char *) ngx_strchr(p, ':');
|
||||
|
||||
if (last == NULL) {
|
||||
*err = "invalid syntax";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*last = '\0';
|
||||
|
||||
engine = ENGINE_by_id((char *) p);
|
||||
|
||||
*last++ = ':';
|
||||
|
||||
if (engine == NULL) {
|
||||
*err = "ENGINE_by_id() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkey = ENGINE_load_private_key(engine, (char *) last, NULL, NULL);
|
||||
|
||||
if (pkey == NULL) {
|
||||
*err = "ENGINE_load_private_key() failed";
|
||||
ENGINE_free(engine);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ENGINE_free(engine);
|
||||
|
||||
return pkey;
|
||||
|
||||
#else
|
||||
|
||||
*err = "loading \"engine:...\" certificate keys is not supported";
|
||||
return NULL;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
cb_data.encrypted = 0;
|
||||
|
||||
if (*passwords) {
|
||||
cb_data.pwd = (*passwords)->elts;
|
||||
tries = (*passwords)->nelts;
|
||||
pwd = &cb_data;
|
||||
cb = ngx_ssl_cache_pkey_password_callback;
|
||||
|
||||
} else {
|
||||
cb_data.pwd = NULL;
|
||||
tries = 1;
|
||||
pwd = NULL;
|
||||
cb = NULL;
|
||||
}
|
||||
|
||||
if (id->type == NGX_SSL_CACHE_STORE) {
|
||||
|
||||
#ifdef ERR_R_OSSL_STORE_LIB
|
||||
|
||||
u_char *uri;
|
||||
UI_METHOD *method;
|
||||
OSSL_STORE_CTX *store;
|
||||
OSSL_STORE_INFO *info;
|
||||
|
||||
method = (cb != NULL) ? UI_UTIL_wrap_read_pem_callback(cb, 0) : NULL;
|
||||
uri = id->data + sizeof("store:") - 1;
|
||||
|
||||
store = OSSL_STORE_open((char *) uri, method, pwd, NULL, NULL);
|
||||
|
||||
if (store == NULL) {
|
||||
*err = "OSSL_STORE_open() failed";
|
||||
|
||||
if (method != NULL) {
|
||||
UI_destroy_method(method);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkey = NULL;
|
||||
|
||||
while (pkey == NULL && !OSSL_STORE_eof(store)) {
|
||||
info = OSSL_STORE_load(store);
|
||||
|
||||
if (info == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY) {
|
||||
pkey = OSSL_STORE_INFO_get1_PKEY(info);
|
||||
}
|
||||
|
||||
OSSL_STORE_INFO_free(info);
|
||||
}
|
||||
|
||||
OSSL_STORE_close(store);
|
||||
|
||||
if (method != NULL) {
|
||||
UI_destroy_method(method);
|
||||
}
|
||||
|
||||
if (pkey == NULL) {
|
||||
*err = "OSSL_STORE_load() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cb_data.encrypted) {
|
||||
*passwords = NGX_SSL_CACHE_DISABLED;
|
||||
}
|
||||
|
||||
return pkey;
|
||||
|
||||
#else
|
||||
|
||||
*err = "loading \"store:...\" certificate keys is not supported";
|
||||
return NULL;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
bio = ngx_ssl_cache_create_bio(id, err);
|
||||
if (bio == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);
|
||||
if (pkey != NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (tries-- > 1) {
|
||||
ERR_clear_error();
|
||||
(void) BIO_reset(bio);
|
||||
cb_data.pwd++;
|
||||
continue;
|
||||
}
|
||||
|
||||
*err = "PEM_read_bio_PrivateKey() failed";
|
||||
BIO_free(bio);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cb_data.encrypted) {
|
||||
*passwords = NGX_SSL_CACHE_DISABLED;
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_ssl_cache_pkey_password_callback(char *buf, int size, int rwflag,
|
||||
void *userdata)
|
||||
{
|
||||
ngx_ssl_cache_pwd_t *data = userdata;
|
||||
|
||||
ngx_str_t *pwd;
|
||||
|
||||
if (rwflag) {
|
||||
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
||||
"ngx_ssl_cache_pkey_password_callback() is called "
|
||||
"for encryption");
|
||||
return 0;
|
||||
}
|
||||
|
||||
data->encrypted = 1;
|
||||
|
||||
pwd = data->pwd;
|
||||
|
||||
if (pwd == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pwd->len > (size_t) size) {
|
||||
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
|
||||
"password is truncated to %d bytes", size);
|
||||
} else {
|
||||
size = pwd->len;
|
||||
}
|
||||
|
||||
ngx_memcpy(buf, pwd->data, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_cache_pkey_free(void *data)
|
||||
{
|
||||
EVP_PKEY_free(data);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_ssl_cache_pkey_ref(char **err, void *data)
|
||||
{
|
||||
EVP_PKEY *pkey = data;
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
|
||||
EVP_PKEY_up_ref(pkey);
|
||||
#else
|
||||
CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_ssl_cache_crl_create(ngx_ssl_cache_key_t *id, char **err, void *data)
|
||||
{
|
||||
BIO *bio;
|
||||
u_long n;
|
||||
X509_CRL *x509;
|
||||
STACK_OF(X509_CRL) *chain;
|
||||
|
||||
chain = sk_X509_CRL_new_null();
|
||||
if (chain == NULL) {
|
||||
*err = "sk_X509_CRL_new_null() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bio = ngx_ssl_cache_create_bio(id, err);
|
||||
if (bio == NULL) {
|
||||
sk_X509_CRL_pop_free(chain, X509_CRL_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
x509 = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
|
||||
if (x509 == NULL) {
|
||||
n = ERR_peek_last_error();
|
||||
|
||||
if (ERR_GET_LIB(n) == ERR_LIB_PEM
|
||||
&& ERR_GET_REASON(n) == PEM_R_NO_START_LINE
|
||||
&& sk_X509_CRL_num(chain) > 0)
|
||||
{
|
||||
/* end of file */
|
||||
ERR_clear_error();
|
||||
break;
|
||||
}
|
||||
|
||||
/* some real error */
|
||||
|
||||
*err = "PEM_read_bio_X509_CRL() failed";
|
||||
BIO_free(bio);
|
||||
sk_X509_CRL_pop_free(chain, X509_CRL_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sk_X509_CRL_push(chain, x509) == 0) {
|
||||
*err = "sk_X509_CRL_push() failed";
|
||||
BIO_free(bio);
|
||||
X509_CRL_free(x509);
|
||||
sk_X509_CRL_pop_free(chain, X509_CRL_free);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_cache_crl_free(void *data)
|
||||
{
|
||||
sk_X509_CRL_pop_free(data, X509_CRL_free);
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_ssl_cache_crl_ref(char **err, void *data)
|
||||
{
|
||||
int n, i;
|
||||
X509_CRL *x509;
|
||||
STACK_OF(X509_CRL) *chain;
|
||||
|
||||
chain = sk_X509_CRL_dup(data);
|
||||
if (chain == NULL) {
|
||||
*err = "sk_X509_CRL_dup() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n = sk_X509_CRL_num(chain);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
x509 = sk_X509_CRL_value(chain, i);
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
|
||||
X509_CRL_up_ref(x509);
|
||||
#else
|
||||
CRYPTO_add(&x509->references, 1, CRYPTO_LOCK_X509_CRL);
|
||||
#endif
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_ssl_cache_ca_create(ngx_ssl_cache_key_t *id, char **err, void *data)
|
||||
{
|
||||
BIO *bio;
|
||||
X509 *x509;
|
||||
u_long n;
|
||||
STACK_OF(X509) *chain;
|
||||
|
||||
chain = sk_X509_new_null();
|
||||
if (chain == NULL) {
|
||||
*err = "sk_X509_new_null() failed";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bio = ngx_ssl_cache_create_bio(id, err);
|
||||
if (bio == NULL) {
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
|
||||
if (x509 == NULL) {
|
||||
n = ERR_peek_last_error();
|
||||
|
||||
if (ERR_GET_LIB(n) == ERR_LIB_PEM
|
||||
&& ERR_GET_REASON(n) == PEM_R_NO_START_LINE
|
||||
&& sk_X509_num(chain) > 0)
|
||||
{
|
||||
/* end of file */
|
||||
ERR_clear_error();
|
||||
break;
|
||||
}
|
||||
|
||||
/* some real error */
|
||||
|
||||
*err = "PEM_read_bio_X509_AUX() failed";
|
||||
BIO_free(bio);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sk_X509_push(chain, x509) == 0) {
|
||||
*err = "sk_X509_push() failed";
|
||||
BIO_free(bio);
|
||||
X509_free(x509);
|
||||
sk_X509_pop_free(chain, X509_free);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
|
||||
static BIO *
|
||||
ngx_ssl_cache_create_bio(ngx_ssl_cache_key_t *id, char **err)
|
||||
{
|
||||
BIO *bio;
|
||||
|
||||
if (id->type == NGX_SSL_CACHE_DATA) {
|
||||
|
||||
bio = BIO_new_mem_buf(id->data + sizeof("data:") - 1,
|
||||
id->len - (sizeof("data:") - 1));
|
||||
if (bio == NULL) {
|
||||
*err = "BIO_new_mem_buf() failed";
|
||||
}
|
||||
|
||||
return bio;
|
||||
}
|
||||
|
||||
bio = BIO_new_file((char *) id->data, "r");
|
||||
if (bio == NULL) {
|
||||
*err = "BIO_new_file() failed";
|
||||
}
|
||||
|
||||
return bio;
|
||||
}
|
||||
|
||||
|
||||
static void *
|
||||
ngx_openssl_cache_create_conf(ngx_cycle_t *cycle)
|
||||
{
|
||||
ngx_ssl_cache_t *cache;
|
||||
|
||||
cache = ngx_ssl_cache_init(cycle->pool, 0, 0, 0);
|
||||
if (cache == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cache->inheritable = NGX_CONF_UNSET;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_openssl_cache_init_conf(ngx_cycle_t *cycle, void *conf)
|
||||
{
|
||||
ngx_ssl_cache_t *cache = conf;
|
||||
|
||||
ngx_conf_init_value(cache->inheritable, 1);
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_ssl_cache_t *
|
||||
ngx_ssl_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t valid,
|
||||
time_t inactive)
|
||||
{
|
||||
ngx_ssl_cache_t *cache;
|
||||
ngx_pool_cleanup_t *cln;
|
||||
|
||||
cache = ngx_pcalloc(pool, sizeof(ngx_ssl_cache_t));
|
||||
if (cache == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
|
||||
ngx_ssl_cache_node_insert);
|
||||
|
||||
ngx_queue_init(&cache->expire_queue);
|
||||
|
||||
cache->max = max;
|
||||
cache->valid = valid;
|
||||
cache->inactive = inactive;
|
||||
|
||||
cln = ngx_pool_cleanup_add(pool, 0);
|
||||
if (cln == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cln->handler = ngx_ssl_cache_cleanup;
|
||||
cln->data = cache;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_cache_cleanup(void *data)
|
||||
{
|
||||
ngx_ssl_cache_t *cache = data;
|
||||
|
||||
ngx_rbtree_t *tree;
|
||||
ngx_rbtree_node_t *node;
|
||||
ngx_ssl_cache_node_t *cn;
|
||||
|
||||
tree = &cache->rbtree;
|
||||
|
||||
if (tree->root == tree->sentinel) {
|
||||
return;
|
||||
}
|
||||
|
||||
node = ngx_rbtree_min(tree->root, tree->sentinel);
|
||||
|
||||
while (node != NULL) {
|
||||
cn = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node);
|
||||
node = ngx_rbtree_next(tree, node);
|
||||
|
||||
ngx_ssl_cache_node_free(tree, cn);
|
||||
|
||||
if (cache->max) {
|
||||
cache->current--;
|
||||
}
|
||||
}
|
||||
|
||||
if (cache->current) {
|
||||
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
||||
"%ui items still left in ssl cache",
|
||||
cache->current);
|
||||
}
|
||||
|
||||
if (!ngx_queue_empty(&cache->expire_queue)) {
|
||||
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
||||
"queue still is not empty in ssl cache");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_cache_node_free(ngx_rbtree_t *rbtree, ngx_ssl_cache_node_t *cn)
|
||||
{
|
||||
cn->type->free(cn->value);
|
||||
|
||||
ngx_rbtree_delete(rbtree, &cn->node);
|
||||
|
||||
if (!ngx_queue_empty(&cn->queue)) {
|
||||
ngx_queue_remove(&cn->queue);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
|
||||
"delete cached ssl object: %s", cn->id.data);
|
||||
|
||||
ngx_free(cn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_cache_node_insert(ngx_rbtree_node_t *temp,
|
||||
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
|
||||
{
|
||||
ngx_rbtree_node_t **p;
|
||||
ngx_ssl_cache_node_t *n, *t;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
n = ngx_rbtree_data(node, ngx_ssl_cache_node_t, node);
|
||||
t = ngx_rbtree_data(temp, ngx_ssl_cache_node_t, node);
|
||||
|
||||
if (node->key != temp->key) {
|
||||
|
||||
p = (node->key < temp->key) ? &temp->left : &temp->right;
|
||||
|
||||
} else if (n->type != t->type) {
|
||||
|
||||
p = (n->type < t->type) ? &temp->left : &temp->right;
|
||||
|
||||
} else {
|
||||
|
||||
p = (ngx_memn2cmp(n->id.data, t->id.data, n->id.len, t->id.len)
|
||||
< 0) ? &temp->left : &temp->right;
|
||||
}
|
||||
|
||||
if (*p == sentinel) {
|
||||
break;
|
||||
}
|
||||
|
||||
temp = *p;
|
||||
}
|
||||
|
||||
*p = node;
|
||||
node->parent = temp;
|
||||
node->left = sentinel;
|
||||
node->right = sentinel;
|
||||
ngx_rbt_red(node);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_openssl_cache_init_worker(ngx_cycle_t *cycle)
|
||||
{
|
||||
#ifdef ERR_R_OSSL_STORE_LIB
|
||||
|
||||
if (ngx_process != NGX_PROCESS_WORKER) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
UI_set_default_method(UI_null());
|
||||
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_rbtree_node_t node;
|
||||
|
||||
ngx_str_t staple;
|
||||
ngx_msec_t timeout;
|
||||
|
||||
@@ -154,6 +156,7 @@ static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
|
||||
static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,
|
||||
void *data);
|
||||
static ngx_ssl_stapling_t *ngx_ssl_stapling_lookup(ngx_ssl_t *ssl, X509 *cert);
|
||||
static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple);
|
||||
static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);
|
||||
|
||||
@@ -195,12 +198,12 @@ ngx_int_t
|
||||
ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
|
||||
ngx_str_t *responder, ngx_uint_t verify)
|
||||
{
|
||||
X509 *cert;
|
||||
X509 *cert;
|
||||
ngx_uint_t k;
|
||||
|
||||
for (k = 0; k < ssl->certs.nelts; k++) {
|
||||
cert = ((X509 **) ssl->certs.elts)[k];
|
||||
|
||||
for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
|
||||
cert;
|
||||
cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
|
||||
{
|
||||
if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify)
|
||||
!= NGX_OK)
|
||||
{
|
||||
@@ -235,10 +238,9 @@ ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, X509 *cert,
|
||||
cln->handler = ngx_ssl_stapling_cleanup;
|
||||
cln->data = staple;
|
||||
|
||||
if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "X509_set_ex_data() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
staple->node.key = (ngx_rbtree_key_t) cert;
|
||||
|
||||
ngx_rbtree_insert(&ssl->staple_rbtree, &staple->node);
|
||||
|
||||
#ifdef SSL_CTRL_SELECT_CURRENT_CERT
|
||||
/* OpenSSL 1.0.2+ */
|
||||
@@ -545,14 +547,21 @@ ngx_int_t
|
||||
ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
|
||||
ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
|
||||
{
|
||||
X509 *cert;
|
||||
ngx_rbtree_t *tree;
|
||||
ngx_rbtree_node_t *node;
|
||||
ngx_ssl_stapling_t *staple;
|
||||
|
||||
for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
|
||||
cert;
|
||||
cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
|
||||
tree = &ssl->staple_rbtree;
|
||||
|
||||
if (tree->root == tree->sentinel) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
for (node = ngx_rbtree_min(tree->root, tree->sentinel);
|
||||
node;
|
||||
node = ngx_rbtree_next(tree, node))
|
||||
{
|
||||
staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);
|
||||
staple = ngx_rbtree_data(node, ngx_ssl_stapling_t, node);
|
||||
staple->resolver = resolver;
|
||||
staple->resolver_timeout = resolver_timeout;
|
||||
}
|
||||
@@ -567,6 +576,8 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
|
||||
int rc;
|
||||
X509 *cert;
|
||||
u_char *p;
|
||||
SSL_CTX *ssl_ctx;
|
||||
ngx_ssl_t *ssl;
|
||||
ngx_connection_t *c;
|
||||
ngx_ssl_stapling_t *staple;
|
||||
|
||||
@@ -583,7 +594,10 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
|
||||
return rc;
|
||||
}
|
||||
|
||||
staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);
|
||||
ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
|
||||
ssl = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_index);
|
||||
|
||||
staple = ngx_ssl_stapling_lookup(ssl, cert);
|
||||
|
||||
if (staple == NULL) {
|
||||
return rc;
|
||||
@@ -613,6 +627,30 @@ ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
|
||||
}
|
||||
|
||||
|
||||
static ngx_ssl_stapling_t *
|
||||
ngx_ssl_stapling_lookup(ngx_ssl_t *ssl, X509 *cert)
|
||||
{
|
||||
ngx_rbtree_key_t key;
|
||||
ngx_rbtree_node_t *node, *sentinel;
|
||||
|
||||
node = ssl->staple_rbtree.root;
|
||||
sentinel = ssl->staple_rbtree.sentinel;
|
||||
key = (ngx_rbtree_key_t) cert;
|
||||
|
||||
while (node != sentinel) {
|
||||
|
||||
if (key != node->key) {
|
||||
node = (key < node->key) ? node->left : node->right;
|
||||
continue;
|
||||
}
|
||||
|
||||
return ngx_rbtree_data(node, ngx_ssl_stapling_t, node);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)
|
||||
{
|
||||
@@ -2717,12 +2755,11 @@ ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
|
||||
ngx_str_t *responder, ngx_uint_t verify)
|
||||
{
|
||||
X509 *cert;
|
||||
ngx_uint_t k;
|
||||
|
||||
for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
|
||||
cert;
|
||||
cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))
|
||||
{
|
||||
for (k = 0; k < ssl->certs.nelts; k++) {
|
||||
if (file->len) {
|
||||
cert = ((X509 **) ssl->certs.elts)[k];
|
||||
if (ngx_ssl_stapling_file(cf, ssl, cert, file, responder, verify)
|
||||
!= NGX_OK)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,6 @@ clean:
|
||||
@rm -f $(RESULT) *.o
|
||||
|
||||
debug: $(PROGNAME).o
|
||||
llvm-objdump -S -no-show-raw-insn $<
|
||||
llvm-objdump -S --no-show-raw-insn $<
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
@@ -72,7 +72,7 @@ ngx_quic_connstate_dbg(ngx_connection_t *c)
|
||||
|
||||
if (qc) {
|
||||
|
||||
if (qc->error != (ngx_uint_t) -1) {
|
||||
if (qc->error) {
|
||||
p = ngx_slprintf(p, last, "%s", qc->error_app ? " app" : "");
|
||||
p = ngx_slprintf(p, last, " error:%ui", qc->error);
|
||||
|
||||
@@ -135,6 +135,9 @@ ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp)
|
||||
if (scid.len != ctp->initial_scid.len
|
||||
|| ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0)
|
||||
{
|
||||
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
|
||||
qc->error_reason = "invalid initial_source_connection_id";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic client initial_source_connection_id mismatch");
|
||||
return NGX_ERROR;
|
||||
@@ -257,9 +260,9 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
qc->send_ctx[i].pending_ack = NGX_QUIC_UNSET_PN;
|
||||
}
|
||||
|
||||
qc->send_ctx[0].level = ssl_encryption_initial;
|
||||
qc->send_ctx[1].level = ssl_encryption_handshake;
|
||||
qc->send_ctx[2].level = ssl_encryption_application;
|
||||
qc->send_ctx[0].level = NGX_QUIC_ENCRYPTION_INITIAL;
|
||||
qc->send_ctx[1].level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
|
||||
qc->send_ctx[2].level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
|
||||
ngx_queue_init(&qc->free_frames);
|
||||
|
||||
@@ -308,11 +311,16 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
qc->streams.client_max_streams_uni = qc->tp.initial_max_streams_uni;
|
||||
qc->streams.client_max_streams_bidi = qc->tp.initial_max_streams_bidi;
|
||||
|
||||
qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
|
||||
ngx_max(2 * qc->tp.max_udp_payload_size,
|
||||
qc->congestion.window = ngx_min(10 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
ngx_max(2 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
14720));
|
||||
qc->congestion.ssthresh = (size_t) -1;
|
||||
qc->congestion.recovery_start = ngx_current_msec;
|
||||
qc->congestion.mtu = NGX_QUIC_MIN_INITIAL_SIZE;
|
||||
qc->congestion.recovery_start = ngx_current_msec - 1;
|
||||
|
||||
qc->max_frames = (conf->max_concurrent_streams_uni
|
||||
+ conf->max_concurrent_streams_bidi)
|
||||
* conf->stream_buffer_size / 2000;
|
||||
|
||||
if (pkt->validated && pkt->retried) {
|
||||
qc->tp.retry_scid.len = pkt->dcid.len;
|
||||
@@ -512,7 +520,7 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc)
|
||||
* to terminate the connection immediately.
|
||||
*/
|
||||
|
||||
if (qc->error == (ngx_uint_t) -1) {
|
||||
if (qc->error == 0 && rc == NGX_ERROR) {
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
qc->error_app = 0;
|
||||
}
|
||||
@@ -792,13 +800,13 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
pkt->dcid.len, &pkt->dcid);
|
||||
|
||||
#if (NGX_DEBUG)
|
||||
if (pkt->level != ssl_encryption_application) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic packet rx scid len:%uz %xV",
|
||||
pkt->scid.len, &pkt->scid);
|
||||
}
|
||||
|
||||
if (pkt->level == ssl_encryption_initial) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic address validation token len:%uz %xV",
|
||||
pkt->token.len, &pkt->token);
|
||||
@@ -815,7 +823,7 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
if (pkt->level != ssl_encryption_application) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
|
||||
if (pkt->version != qc->version) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
@@ -845,7 +853,9 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
|
||||
rc = ngx_quic_handle_payload(c, pkt);
|
||||
|
||||
if (rc == NGX_DECLINED && pkt->level == ssl_encryption_application) {
|
||||
if (rc == NGX_DECLINED
|
||||
&& pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION)
|
||||
{
|
||||
if (ngx_quic_handle_stateless_reset(c, pkt) == NGX_OK) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic stateless reset packet detected");
|
||||
@@ -866,11 +876,11 @@ ngx_quic_handle_packet(ngx_connection_t *c, ngx_quic_conf_t *conf,
|
||||
return ngx_quic_negotiate_version(c, pkt);
|
||||
}
|
||||
|
||||
if (pkt->level == ssl_encryption_application) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
return ngx_quic_send_stateless_reset(c, conf, pkt);
|
||||
}
|
||||
|
||||
if (pkt->level != ssl_encryption_initial) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic expected initial, got handshake");
|
||||
return NGX_ERROR;
|
||||
@@ -953,8 +963,8 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
qc->error = (ngx_uint_t) -1;
|
||||
qc->error_reason = 0;
|
||||
qc->error = 0;
|
||||
qc->error_reason = NULL;
|
||||
|
||||
c->log->action = "decrypting packet";
|
||||
|
||||
@@ -965,10 +975,10 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
#if !defined (OPENSSL_IS_BORINGSSL)
|
||||
/* OpenSSL provides read keys for an application level before it's ready */
|
||||
#if (NGX_QUIC_QUICTLS_API)
|
||||
/* QuicTLS provides app read keys before completing handshake */
|
||||
|
||||
if (pkt->level == ssl_encryption_application && !c->ssl->handshaked) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION && !c->ssl->handshaked) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic no %s keys ready, ignoring packet",
|
||||
ngx_quic_level_name(pkt->level));
|
||||
@@ -1006,14 +1016,14 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
}
|
||||
}
|
||||
|
||||
if (pkt->level == ssl_encryption_handshake) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_HANDSHAKE) {
|
||||
/*
|
||||
* RFC 9001, 4.9.1. Discarding Initial Keys
|
||||
*
|
||||
* The successful use of Handshake packets indicates
|
||||
* that no more Initial packets need to be exchanged
|
||||
*/
|
||||
ngx_quic_discard_ctx(c, ssl_encryption_initial);
|
||||
ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_INITIAL);
|
||||
|
||||
if (!qc->path->validated) {
|
||||
qc->path->validated = 1;
|
||||
@@ -1022,14 +1032,14 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
}
|
||||
}
|
||||
|
||||
if (pkt->level == ssl_encryption_application) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
/*
|
||||
* RFC 9001, 4.9.3. Discarding 0-RTT Keys
|
||||
*
|
||||
* After receiving a 1-RTT packet, servers MUST discard
|
||||
* 0-RTT keys within a short time
|
||||
*/
|
||||
ngx_quic_discard_ctx(c, ssl_encryption_early_data);
|
||||
ngx_quic_keys_discard(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA);
|
||||
}
|
||||
|
||||
if (qc->closing) {
|
||||
@@ -1056,7 +1066,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
|
||||
c->log->action = "handling payload";
|
||||
|
||||
if (pkt->level != ssl_encryption_application) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
return ngx_quic_handle_frames(c, pkt);
|
||||
}
|
||||
|
||||
@@ -1081,7 +1091,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
|
||||
ngx_quic_discard_ctx(ngx_connection_t *c, ngx_uint_t level)
|
||||
{
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *f;
|
||||
@@ -1122,7 +1132,7 @@ ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
|
||||
ngx_quic_free_frame(c, f);
|
||||
}
|
||||
|
||||
if (level == ssl_encryption_initial) {
|
||||
if (level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
/* close temporary listener with initial dcid */
|
||||
qsock = ngx_quic_find_socket(c, NGX_QUIC_UNSET_PN);
|
||||
if (qsock) {
|
||||
|
||||
@@ -12,6 +12,22 @@
|
||||
#include <ngx_core.h>
|
||||
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER >= 0x30500010L)
|
||||
#define NGX_QUIC_OPENSSL_API 1
|
||||
|
||||
#elif (defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION)
|
||||
#define NGX_QUIC_QUICTLS_API 1
|
||||
|
||||
#elif (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC \
|
||||
|| defined LIBRESSL_VERSION_NUMBER)
|
||||
#define NGX_QUIC_BORINGSSL_API 1
|
||||
|
||||
#else
|
||||
#define NGX_QUIC_BORINGSSL_API 1
|
||||
#define NGX_QUIC_OPENSSL_COMPAT 1
|
||||
#endif
|
||||
|
||||
|
||||
#define NGX_QUIC_MAX_UDP_PAYLOAD_SIZE 65527
|
||||
|
||||
#define NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT 3
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
/* RFC 9002, 7.6.1. Duration: kPersistentCongestionThreshold */
|
||||
#define NGX_QUIC_PERSISTENT_CONGESTION_THR 3
|
||||
|
||||
/* CUBIC parameters x10 */
|
||||
#define NGX_QUIC_CUBIC_BETA 7
|
||||
#define NGX_QUIC_CUBIC_C 4
|
||||
|
||||
|
||||
/* send time of ACK'ed packets */
|
||||
typedef struct {
|
||||
@@ -29,18 +33,22 @@ typedef struct {
|
||||
} ngx_quic_ack_stat_t;
|
||||
|
||||
|
||||
static ngx_inline ngx_msec_t ngx_quic_lost_threshold(ngx_quic_connection_t *qc);
|
||||
static ngx_inline ngx_msec_t ngx_quic_time_threshold(ngx_quic_connection_t *qc);
|
||||
static uint64_t ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx);
|
||||
static void ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
|
||||
enum ssl_encryption_level_t level, ngx_msec_t send_time);
|
||||
ngx_uint_t level, ngx_msec_t send_time);
|
||||
static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
|
||||
ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max,
|
||||
ngx_quic_ack_stat_t *st);
|
||||
static size_t ngx_quic_congestion_cubic(ngx_connection_t *c);
|
||||
static void ngx_quic_drop_ack_ranges(ngx_connection_t *c,
|
||||
ngx_quic_send_ctx_t *ctx, uint64_t pn);
|
||||
static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c,
|
||||
ngx_quic_ack_stat_t *st);
|
||||
static ngx_msec_t ngx_quic_congestion_cubic_time(ngx_connection_t *c);
|
||||
static ngx_msec_t ngx_quic_pcg_duration(ngx_connection_t *c);
|
||||
static void ngx_quic_persistent_congestion(ngx_connection_t *c);
|
||||
static ngx_msec_t ngx_quic_oldest_sent_packet(ngx_connection_t *c);
|
||||
static void ngx_quic_congestion_lost(ngx_connection_t *c,
|
||||
ngx_quic_frame_t *frame);
|
||||
static void ngx_quic_lost_handler(ngx_event_t *ev);
|
||||
@@ -48,7 +56,7 @@ static void ngx_quic_lost_handler(ngx_event_t *ev);
|
||||
|
||||
/* RFC 9002, 6.1.2. Time Threshold: kTimeThreshold, kGranularity */
|
||||
static ngx_inline ngx_msec_t
|
||||
ngx_quic_lost_threshold(ngx_quic_connection_t *qc)
|
||||
ngx_quic_time_threshold(ngx_quic_connection_t *qc)
|
||||
{
|
||||
ngx_msec_t thr;
|
||||
|
||||
@@ -59,6 +67,29 @@ ngx_quic_lost_threshold(ngx_quic_connection_t *qc)
|
||||
}
|
||||
|
||||
|
||||
static uint64_t
|
||||
ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx)
|
||||
{
|
||||
uint64_t pkt_thr;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *f;
|
||||
|
||||
if (ngx_queue_empty(&ctx->sent)) {
|
||||
return NGX_QUIC_PKT_THR;
|
||||
}
|
||||
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
pkt_thr = (ctx->pnum - f->pnum) / 2;
|
||||
|
||||
if (pkt_thr <= NGX_QUIC_PKT_THR) {
|
||||
return NGX_QUIC_PKT_THR;
|
||||
}
|
||||
|
||||
return pkt_thr;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
ngx_quic_frame_t *f)
|
||||
@@ -77,7 +108,7 @@ ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
ctx = ngx_quic_get_send_ctx(qc, pkt->level);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_handle_ack_frame level:%d", pkt->level);
|
||||
"quic ngx_quic_handle_ack_frame level:%ui", pkt->level);
|
||||
|
||||
ack = &f->u.ack;
|
||||
|
||||
@@ -176,7 +207,7 @@ ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
|
||||
static void
|
||||
ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
|
||||
enum ssl_encryption_level_t level, ngx_msec_t send_time)
|
||||
ngx_uint_t level, ngx_msec_t send_time)
|
||||
{
|
||||
ngx_msec_t latest_rtt, ack_delay, adjusted_rtt, rttvar_sample;
|
||||
ngx_quic_connection_t *qc;
|
||||
@@ -229,7 +260,7 @@ ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
if (ctx->level == ssl_encryption_application) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
if (ngx_quic_handle_path_mtu(c, qc->path, min, max) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
@@ -313,8 +344,9 @@ ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
void
|
||||
ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
{
|
||||
size_t w_cubic;
|
||||
ngx_uint_t blocked;
|
||||
ngx_msec_t timer;
|
||||
ngx_msec_t now, timer;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
@@ -329,16 +361,34 @@ ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
return;
|
||||
}
|
||||
|
||||
now = ngx_current_msec;
|
||||
|
||||
blocked = (cg->in_flight >= cg->window) ? 1 : 0;
|
||||
|
||||
cg->in_flight -= f->plen;
|
||||
|
||||
/* prevent recovery_start from wrapping */
|
||||
|
||||
timer = now - cg->recovery_start;
|
||||
|
||||
if ((ngx_msec_int_t) timer < 0) {
|
||||
cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
|
||||
}
|
||||
|
||||
timer = f->send_time - cg->recovery_start;
|
||||
|
||||
if ((ngx_msec_int_t) timer <= 0) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack recovery win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
"quic congestion ack rec t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (cg->idle) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack idle t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
goto done;
|
||||
}
|
||||
@@ -346,24 +396,51 @@ ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
if (cg->window < cg->ssthresh) {
|
||||
cg->window += f->plen;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion slow start win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack ss t:%M win:%uz ss:%z if:%uz",
|
||||
now, cg->window, cg->ssthresh, cg->in_flight);
|
||||
|
||||
} else {
|
||||
cg->window += qc->tp.max_udp_payload_size * f->plen / cg->window;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion avoidance win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
}
|
||||
/* RFC 9438, 4.2. Window Increase Function */
|
||||
|
||||
/* prevent recovery_start from wrapping */
|
||||
w_cubic = ngx_quic_congestion_cubic(c);
|
||||
|
||||
timer = cg->recovery_start - ngx_current_msec + qc->tp.max_idle_timeout * 2;
|
||||
if (cg->window < cg->w_prior) {
|
||||
cg->w_est += (uint64_t) cg->mtu * f->plen
|
||||
* 3 * (10 - NGX_QUIC_CUBIC_BETA)
|
||||
/ (10 + NGX_QUIC_CUBIC_BETA) / cg->window;
|
||||
|
||||
if ((ngx_msec_int_t) timer < 0) {
|
||||
cg->recovery_start = ngx_current_msec - qc->tp.max_idle_timeout * 2;
|
||||
} else {
|
||||
cg->w_est += (uint64_t) cg->mtu * f->plen / cg->window;
|
||||
}
|
||||
|
||||
if (w_cubic < cg->w_est) {
|
||||
cg->window = cg->w_est;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack reno t:%M win:%uz c:%uz if:%uz",
|
||||
now, cg->window, w_cubic, cg->in_flight);
|
||||
|
||||
} else if (w_cubic > cg->window) {
|
||||
|
||||
if (w_cubic >= cg->window * 3 / 2) {
|
||||
cg->window += cg->mtu / 2;
|
||||
|
||||
} else {
|
||||
cg->window += (uint64_t) cg->mtu * (w_cubic - cg->window)
|
||||
/ cg->window;
|
||||
}
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack cubic t:%M win:%uz c:%uz if:%uz",
|
||||
now, cg->window, w_cubic, cg->in_flight);
|
||||
|
||||
} else {
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion ack skip t:%M win:%uz c:%uz if:%uz",
|
||||
now, cg->window, w_cubic, cg->in_flight);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
@@ -374,6 +451,87 @@ done:
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
ngx_quic_congestion_cubic(ngx_connection_t *c)
|
||||
{
|
||||
int64_t w, t, cc;
|
||||
ngx_msec_t now;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
ngx_quic_congestion_idle(c, cg->idle);
|
||||
|
||||
now = ngx_current_msec;
|
||||
t = (ngx_msec_int_t) (now - cg->k);
|
||||
|
||||
if (t > 1000000) {
|
||||
w = NGX_MAX_SIZE_T_VALUE;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (t < -1000000) {
|
||||
w = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 9438, Figure 1
|
||||
*
|
||||
* w_cubic = C * (t_msec / 1000) ^ 3 * mtu + w_max
|
||||
*/
|
||||
|
||||
cc = 10000000000ll / (int64_t) cg->mtu / NGX_QUIC_CUBIC_C;
|
||||
w = t * t * t / cc + (int64_t) cg->w_max;
|
||||
|
||||
if (w > NGX_MAX_SIZE_T_VALUE) {
|
||||
w = NGX_MAX_SIZE_T_VALUE;
|
||||
}
|
||||
|
||||
if (w < 0) {
|
||||
w = 0;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic cubic t:%L w:%L wm:%uz", t, w, cg->w_max);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle)
|
||||
{
|
||||
ngx_msec_t now;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion idle:%ui", idle);
|
||||
|
||||
if (cg->window >= cg->ssthresh) {
|
||||
/* RFC 9438, 5.8. Behavior for Application-Limited Flows */
|
||||
|
||||
now = ngx_current_msec;
|
||||
|
||||
if (cg->idle) {
|
||||
cg->k += now - cg->idle_start;
|
||||
}
|
||||
|
||||
cg->idle_start = now;
|
||||
}
|
||||
|
||||
cg->idle = idle;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
uint64_t pn)
|
||||
@@ -435,6 +593,7 @@ ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
static ngx_int_t
|
||||
ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
{
|
||||
uint64_t pkt_thr;
|
||||
ngx_uint_t i, nlost;
|
||||
ngx_msec_t now, wait, thr, oldest, newest;
|
||||
ngx_queue_t *q;
|
||||
@@ -444,11 +603,12 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
now = ngx_current_msec;
|
||||
thr = ngx_quic_lost_threshold(qc);
|
||||
thr = ngx_quic_time_threshold(qc);
|
||||
|
||||
/* send time of lost packets across all send contexts */
|
||||
oldest = NGX_TIMER_INFINITE;
|
||||
newest = NGX_TIMER_INFINITE;
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
oldest = now;
|
||||
newest = now;
|
||||
#endif
|
||||
|
||||
nlost = 0;
|
||||
|
||||
@@ -460,6 +620,8 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
continue;
|
||||
}
|
||||
|
||||
pkt_thr = ngx_quic_packet_threshold(ctx);
|
||||
|
||||
while (!ngx_queue_empty(&ctx->sent)) {
|
||||
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
@@ -471,23 +633,27 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
|
||||
wait = start->send_time + thr - now;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic detect_lost pnum:%uL thr:%M wait:%i level:%d",
|
||||
start->pnum, thr, (ngx_int_t) wait, start->level);
|
||||
ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic detect_lost pnum:%uL thr:%M pthr:%uL wait:%i level:%ui",
|
||||
start->pnum, thr, pkt_thr, (ngx_int_t) wait, start->level);
|
||||
|
||||
if ((ngx_msec_int_t) wait > 0
|
||||
&& ctx->largest_ack - start->pnum < NGX_QUIC_PKT_THR)
|
||||
&& ctx->largest_ack - start->pnum < pkt_thr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (start->send_time > qc->first_rtt) {
|
||||
if ((ngx_msec_int_t) (start->send_time - qc->first_rtt) > 0) {
|
||||
|
||||
if (oldest == NGX_TIMER_INFINITE || start->send_time < oldest) {
|
||||
if (nlost == 0
|
||||
|| (ngx_msec_int_t) (start->send_time - oldest) < 0)
|
||||
{
|
||||
oldest = start->send_time;
|
||||
}
|
||||
|
||||
if (newest == NGX_TIMER_INFINITE || start->send_time > newest) {
|
||||
if (nlost == 0
|
||||
|| (ngx_msec_int_t) (start->send_time - newest) > 0)
|
||||
{
|
||||
newest = start->send_time;
|
||||
}
|
||||
|
||||
@@ -508,8 +674,9 @@ ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
|
||||
* latest ACK frame.
|
||||
*/
|
||||
|
||||
if (st && nlost >= 2 && (st->newest < oldest || st->oldest > newest)) {
|
||||
|
||||
if (st && nlost >= 2 && ((ngx_msec_int_t) (st->newest - oldest) < 0
|
||||
|| (ngx_msec_int_t) (st->oldest - newest) > 0))
|
||||
{
|
||||
if (newest - oldest > ngx_quic_pcg_duration(c)) {
|
||||
ngx_quic_persistent_congestion(c);
|
||||
}
|
||||
@@ -547,11 +714,43 @@ ngx_quic_persistent_congestion(ngx_connection_t *c)
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
cg->recovery_start = ngx_current_msec;
|
||||
cg->window = qc->tp.max_udp_payload_size * 2;
|
||||
cg->mtu = qc->path->mtu;
|
||||
cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
|
||||
cg->window = cg->mtu * 2;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic persistent congestion win:%uz", cg->window);
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion persistent t:%M win:%uz",
|
||||
ngx_current_msec, cg->window);
|
||||
}
|
||||
|
||||
|
||||
static ngx_msec_t
|
||||
ngx_quic_oldest_sent_packet(ngx_connection_t *c)
|
||||
{
|
||||
ngx_msec_t oldest;
|
||||
ngx_uint_t i;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *start;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
oldest = ngx_current_msec;
|
||||
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
|
||||
if (!ngx_queue_empty(&ctx->sent)) {
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
start = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
|
||||
if ((ngx_msec_int_t) (start->send_time - oldest) < 0) {
|
||||
oldest = start->send_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return oldest;
|
||||
}
|
||||
|
||||
|
||||
@@ -588,7 +787,7 @@ ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
switch (f->type) {
|
||||
case NGX_QUIC_FT_ACK:
|
||||
case NGX_QUIC_FT_ACK_ECN:
|
||||
if (ctx->level == ssl_encryption_application) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
/* force generation of most recent acknowledgment */
|
||||
ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
|
||||
}
|
||||
@@ -631,13 +830,12 @@ ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
case NGX_QUIC_FT_STREAM:
|
||||
qs = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id);
|
||||
|
||||
if (qs) {
|
||||
if (qs->send_state == NGX_QUIC_STREAM_SEND_RESET_SENT
|
||||
|| qs->send_state == NGX_QUIC_STREAM_SEND_RESET_RECVD)
|
||||
{
|
||||
ngx_quic_free_frame(c, f);
|
||||
break;
|
||||
}
|
||||
if (qs == NULL
|
||||
|| qs->send_state == NGX_QUIC_STREAM_SEND_RESET_SENT
|
||||
|| qs->send_state == NGX_QUIC_STREAM_SEND_RESET_RECVD)
|
||||
{
|
||||
ngx_quic_free_frame(c, f);
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall through */
|
||||
@@ -660,7 +858,7 @@ static void
|
||||
ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
{
|
||||
ngx_uint_t blocked;
|
||||
ngx_msec_t timer;
|
||||
ngx_msec_t now, timer;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
@@ -682,26 +880,41 @@ ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
|
||||
|
||||
timer = f->send_time - cg->recovery_start;
|
||||
|
||||
now = ngx_current_msec;
|
||||
|
||||
if ((ngx_msec_int_t) timer <= 0) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion lost recovery win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
"quic congestion lost rec t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
cg->recovery_start = ngx_current_msec;
|
||||
cg->window /= 2;
|
||||
if (f->ignore_loss) {
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion lost ignore t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
if (cg->window < qc->tp.max_udp_payload_size * 2) {
|
||||
cg->window = qc->tp.max_udp_payload_size * 2;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cg->ssthresh = cg->window;
|
||||
/* RFC 9438, 4.6. Multiplicative Decrease */
|
||||
|
||||
cg->mtu = qc->path->mtu;
|
||||
cg->recovery_start = now;
|
||||
cg->w_prior = cg->window;
|
||||
/* RFC 9438, 4.7. Fast Convergence */
|
||||
cg->w_max = (cg->window < cg->w_max)
|
||||
? cg->window * (10 + NGX_QUIC_CUBIC_BETA) / 20 : cg->window;
|
||||
cg->ssthresh = cg->in_flight * NGX_QUIC_CUBIC_BETA / 10;
|
||||
cg->window = ngx_max(cg->ssthresh, cg->mtu * 2);
|
||||
cg->w_est = cg->window;
|
||||
cg->k = now + ngx_quic_congestion_cubic_time(c);
|
||||
cg->idle_start = now;
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion lost win:%uz ss:%z if:%uz",
|
||||
cg->window, cg->ssthresh, cg->in_flight);
|
||||
"quic congestion lost t:%M win:%uz if:%uz",
|
||||
now, cg->window, cg->in_flight);
|
||||
|
||||
done:
|
||||
|
||||
@@ -711,9 +924,62 @@ done:
|
||||
}
|
||||
|
||||
|
||||
static ngx_msec_t
|
||||
ngx_quic_congestion_cubic_time(ngx_connection_t *c)
|
||||
{
|
||||
int64_t v, x, d, cc;
|
||||
ngx_uint_t n;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
|
||||
/*
|
||||
* RFC 9438, Figure 2
|
||||
*
|
||||
* k_msec = ((w_max - cwnd_epoch) / C / mtu) ^ 1/3 * 1000
|
||||
*/
|
||||
|
||||
if (cg->w_max <= cg->window) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cc = 10000000000ll / (int64_t) cg->mtu / NGX_QUIC_CUBIC_C;
|
||||
v = (int64_t) (cg->w_max - cg->window) * cc;
|
||||
|
||||
/*
|
||||
* Newton-Raphson method for x ^ 3 = v:
|
||||
*
|
||||
* x_next = (2 * x_prev + v / x_prev ^ 2) / 3
|
||||
*/
|
||||
|
||||
x = 5000;
|
||||
|
||||
for (n = 1; n <= 10; n++) {
|
||||
d = (v / x / x - x) / 3;
|
||||
x += d;
|
||||
|
||||
if (ngx_abs(d) <= 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x > NGX_MAX_SIZE_T_VALUE) {
|
||||
return NGX_MAX_SIZE_T_VALUE;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic cubic time:%L n:%ui", x, n);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_set_lost_timer(ngx_connection_t *c)
|
||||
{
|
||||
uint64_t pkt_thr;
|
||||
ngx_uint_t i;
|
||||
ngx_msec_t now;
|
||||
ngx_queue_t *q;
|
||||
@@ -739,10 +1005,12 @@ ngx_quic_set_lost_timer(ngx_connection_t *c)
|
||||
q = ngx_queue_head(&ctx->sent);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
w = (ngx_msec_int_t)
|
||||
(f->send_time + ngx_quic_lost_threshold(qc) - now);
|
||||
(f->send_time + ngx_quic_time_threshold(qc) - now);
|
||||
|
||||
if (f->pnum <= ctx->largest_ack) {
|
||||
if (w < 0 || ctx->largest_ack - f->pnum >= NGX_QUIC_PKT_THR) {
|
||||
pkt_thr = ngx_quic_packet_threshold(ctx);
|
||||
|
||||
if (w < 0 || ctx->largest_ack - f->pnum >= pkt_thr) {
|
||||
w = 0;
|
||||
}
|
||||
|
||||
@@ -805,7 +1073,7 @@ ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
duration = qc->avg_rtt;
|
||||
duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);
|
||||
|
||||
if (ctx->level == ssl_encryption_application && c->ssl->handshaked) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION && c->ssl->handshaked) {
|
||||
duration += qc->ctp.max_ack_delay;
|
||||
}
|
||||
|
||||
@@ -1160,7 +1428,7 @@ ngx_quic_generate_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->level == ssl_encryption_application) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
|
||||
|
||||
delay = ngx_current_msec - ctx->ack_delay_start;
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
@@ -17,6 +17,7 @@ ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
|
||||
|
||||
void ngx_quic_congestion_ack(ngx_connection_t *c,
|
||||
ngx_quic_frame_t *frame);
|
||||
void ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle);
|
||||
void ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
|
||||
void ngx_quic_set_lost_timer(ngx_connection_t *c);
|
||||
void ngx_quic_pto_handler(ngx_event_t *ev);
|
||||
|
||||
@@ -17,6 +17,15 @@
|
||||
/* #define NGX_QUIC_DEBUG_ALLOC */ /* log frames and bufs alloc */
|
||||
/* #define NGX_QUIC_DEBUG_CRYPTO */
|
||||
|
||||
#define NGX_QUIC_ENCRYPTION_INITIAL 0
|
||||
#define NGX_QUIC_ENCRYPTION_EARLY_DATA 1
|
||||
#define NGX_QUIC_ENCRYPTION_HANDSHAKE 2
|
||||
#define NGX_QUIC_ENCRYPTION_APPLICATION 3
|
||||
#define NGX_QUIC_ENCRYPTION_LAST 4
|
||||
|
||||
#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
|
||||
|
||||
|
||||
typedef struct ngx_quic_connection_s ngx_quic_connection_t;
|
||||
typedef struct ngx_quic_server_id_s ngx_quic_server_id_t;
|
||||
typedef struct ngx_quic_client_id_s ngx_quic_client_id_t;
|
||||
@@ -46,8 +55,6 @@ typedef struct ngx_quic_keys_s ngx_quic_keys_t;
|
||||
|
||||
#define NGX_QUIC_UNSET_PN (uint64_t) -1
|
||||
|
||||
#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
|
||||
|
||||
/* 0-RTT and 1-RTT data exist in the same packet number space,
|
||||
* so we have 3 packet number spaces:
|
||||
*
|
||||
@@ -56,9 +63,9 @@ typedef struct ngx_quic_keys_s ngx_quic_keys_t;
|
||||
* 2 - 0-RTT and 1-RTT
|
||||
*/
|
||||
#define ngx_quic_get_send_ctx(qc, level) \
|
||||
((level) == ssl_encryption_initial) ? &((qc)->send_ctx[0]) \
|
||||
: (((level) == ssl_encryption_handshake) ? &((qc)->send_ctx[1]) \
|
||||
: &((qc)->send_ctx[2]))
|
||||
((level) == NGX_QUIC_ENCRYPTION_INITIAL) ? &((qc)->send_ctx[0]) \
|
||||
: (((level) == NGX_QUIC_ENCRYPTION_HANDSHAKE) ? &((qc)->send_ctx[1]) \
|
||||
: &((qc)->send_ctx[2]))
|
||||
|
||||
#define ngx_quic_get_connection(c) \
|
||||
(((c)->udp) ? (((ngx_quic_socket_t *)((c)->udp))->quic) : NULL)
|
||||
@@ -168,7 +175,14 @@ typedef struct {
|
||||
size_t in_flight;
|
||||
size_t window;
|
||||
size_t ssthresh;
|
||||
size_t w_max;
|
||||
size_t w_est;
|
||||
size_t w_prior;
|
||||
size_t mtu;
|
||||
ngx_msec_t recovery_start;
|
||||
ngx_msec_t idle_start;
|
||||
ngx_msec_t k;
|
||||
ngx_uint_t idle; /* unsigned idle:1; */
|
||||
} ngx_quic_congestion_t;
|
||||
|
||||
|
||||
@@ -181,7 +195,7 @@ typedef struct {
|
||||
* are also Initial packets.
|
||||
*/
|
||||
struct ngx_quic_send_ctx_s {
|
||||
enum ssl_encryption_level_t level;
|
||||
ngx_uint_t level;
|
||||
|
||||
ngx_quic_buffer_t crypto;
|
||||
uint64_t crypto_sent;
|
||||
@@ -254,6 +268,7 @@ struct ngx_quic_connection_s {
|
||||
ngx_buf_t *free_shadow_bufs;
|
||||
|
||||
ngx_uint_t nframes;
|
||||
ngx_uint_t max_frames;
|
||||
#ifdef NGX_QUIC_DEBUG_ALLOC
|
||||
ngx_uint_t nbufs;
|
||||
ngx_uint_t nshadowbufs;
|
||||
@@ -271,7 +286,7 @@ struct ngx_quic_connection_s {
|
||||
off_t received;
|
||||
|
||||
ngx_uint_t error;
|
||||
enum ssl_encryption_level_t error_level;
|
||||
ngx_uint_t error_level;
|
||||
ngx_uint_t error_ftype;
|
||||
const char *error_reason;
|
||||
|
||||
@@ -286,13 +301,17 @@ struct ngx_quic_connection_s {
|
||||
unsigned key_phase:1;
|
||||
unsigned validated:1;
|
||||
unsigned client_tp_done:1;
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
unsigned read_level:2;
|
||||
unsigned write_level:2;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c,
|
||||
ngx_quic_tp_t *ctp);
|
||||
void ngx_quic_discard_ctx(ngx_connection_t *c,
|
||||
enum ssl_encryption_level_t level);
|
||||
void ngx_quic_discard_ctx(ngx_connection_t *c, ngx_uint_t level);
|
||||
void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
|
||||
void ngx_quic_shutdown_quic(ngx_connection_t *c);
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
|
||||
frame->u.retire_cid.sequence_number = f->seqnum;
|
||||
|
||||
@@ -452,7 +452,7 @@ ngx_quic_send_server_id(ngx_connection_t *c, ngx_quic_server_id_t *sid)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_NEW_CONNECTION_ID;
|
||||
frame->u.ncid.seqnum = sid->seqnum;
|
||||
frame->u.ncid.retire = 0;
|
||||
@@ -485,7 +485,7 @@ ngx_quic_free_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID;
|
||||
frame->u.retire_cid.sequence_number = cid->seqnum;
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ ngx_quic_alloc_frame(ngx_connection_t *c)
|
||||
"quic reuse frame n:%ui", qc->nframes);
|
||||
#endif
|
||||
|
||||
} else if (qc->nframes < 10000) {
|
||||
} else if (qc->nframes < qc->max_frames) {
|
||||
frame = ngx_palloc(c->pool, sizeof(ngx_quic_frame_t));
|
||||
if (frame == NULL) {
|
||||
return NULL;
|
||||
|
||||
@@ -40,7 +40,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
||||
ngx_quic_frame_t *fp;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
if (pkt->level != ssl_encryption_application || pkt->path_challenged) {
|
||||
if (pkt->level != NGX_QUIC_ENCRYPTION_APPLICATION || pkt->path_challenged) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ignoring PATH_CHALLENGE");
|
||||
return NGX_OK;
|
||||
@@ -55,7 +55,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
fp->level = ssl_encryption_application;
|
||||
fp->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
fp->type = NGX_QUIC_FT_PATH_RESPONSE;
|
||||
fp->u.path_response = *f;
|
||||
|
||||
@@ -93,7 +93,7 @@ ngx_quic_handle_path_challenge_frame(ngx_connection_t *c,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
fp->level = ssl_encryption_application;
|
||||
fp->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
fp->type = NGX_QUIC_FT_PING;
|
||||
|
||||
ngx_quic_queue_frame(qc, fp);
|
||||
@@ -177,16 +177,17 @@ valid:
|
||||
if (rst) {
|
||||
/* prevent old path packets contribution to congestion control */
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
qc->rst_pnum = ctx->pnum;
|
||||
|
||||
ngx_memzero(&qc->congestion, sizeof(ngx_quic_congestion_t));
|
||||
|
||||
qc->congestion.window = ngx_min(10 * qc->tp.max_udp_payload_size,
|
||||
ngx_max(2 * qc->tp.max_udp_payload_size,
|
||||
qc->congestion.window = ngx_min(10 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
ngx_max(2 * NGX_QUIC_MIN_INITIAL_SIZE,
|
||||
14720));
|
||||
qc->congestion.ssthresh = (size_t) -1;
|
||||
qc->congestion.recovery_start = ngx_current_msec;
|
||||
qc->congestion.mtu = NGX_QUIC_MIN_INITIAL_SIZE;
|
||||
qc->congestion.recovery_start = ngx_current_msec - 1;
|
||||
|
||||
ngx_quic_init_rtt(qc);
|
||||
}
|
||||
@@ -548,7 +549,7 @@ ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
|
||||
(void) ngx_quic_send_path_challenge(c, path);
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
pto = ngx_max(ngx_quic_pto(c, ctx), 1000);
|
||||
|
||||
path->expires = ngx_current_msec + pto;
|
||||
@@ -578,7 +579,7 @@ ngx_quic_send_path_challenge(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_PATH_CHALLENGE;
|
||||
|
||||
ngx_memcpy(frame->u.path_challenge.data, path->challenge[n], 8);
|
||||
@@ -766,7 +767,7 @@ ngx_quic_expire_path_validation(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
if (++path->tries < NGX_QUIC_PATH_RETRIES) {
|
||||
pto = ngx_max(ngx_quic_pto(c, ctx), 1000) << path->tries;
|
||||
@@ -829,7 +830,7 @@ ngx_quic_expire_path_mtu_delay(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
path->tries = 0;
|
||||
|
||||
@@ -875,7 +876,7 @@ ngx_quic_expire_path_mtu_discovery(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
if (++path->tries < NGX_QUIC_PATH_RETRIES) {
|
||||
rc = ngx_quic_send_path_mtu_probe(c, path);
|
||||
@@ -921,11 +922,13 @@ ngx_quic_send_path_mtu_probe(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_PING;
|
||||
frame->ignore_loss = 1;
|
||||
frame->ignore_congestion = 1;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
pnum = ctx->pnum;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
|
||||
@@ -35,8 +35,6 @@ typedef struct {
|
||||
ngx_str_t payload;
|
||||
uint64_t number;
|
||||
ngx_quic_compat_keys_t *keys;
|
||||
|
||||
enum ssl_encryption_level_t level;
|
||||
} ngx_quic_compat_record_t;
|
||||
|
||||
|
||||
@@ -391,6 +389,7 @@ SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method)
|
||||
|
||||
wbio = BIO_new(BIO_s_null());
|
||||
if (wbio == NULL) {
|
||||
BIO_free(rbio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -434,11 +433,10 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
|
||||
|
||||
case SSL3_RT_HANDSHAKE:
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic compat tx %s len:%uz ",
|
||||
ngx_quic_level_name(level), len);
|
||||
"quic compat tx level:%d len:%uz", level, len);
|
||||
|
||||
if (com->method->add_handshake_data(ssl, level, buf, len) != 1) {
|
||||
goto failed;
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -448,11 +446,11 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
|
||||
alert = ((u_char *) buf)[1];
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic compat %s alert:%ui len:%uz ",
|
||||
ngx_quic_level_name(level), alert, len);
|
||||
"quic compat level:%d alert:%ui len:%uz",
|
||||
level, alert, len);
|
||||
|
||||
if (com->method->send_alert(ssl, level, alert) != 1) {
|
||||
goto failed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,10 +458,6 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type,
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
failed:
|
||||
|
||||
ngx_post_event(&qc->close, &ngx_posted_events);
|
||||
}
|
||||
|
||||
|
||||
@@ -486,8 +480,8 @@ SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
|
||||
|
||||
c = ngx_ssl_get_connection(ssl);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic compat rx %s len:%uz",
|
||||
ngx_quic_level_name(level), len);
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic compat rx level:%d len:%uz", level, len);
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
com = qc->compat;
|
||||
@@ -500,7 +494,6 @@ SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level,
|
||||
rec.log = c->log;
|
||||
rec.number = com->read_record++;
|
||||
rec.keys = &com->keys;
|
||||
rec.level = level;
|
||||
|
||||
if (level == ssl_encryption_initial) {
|
||||
n = ngx_min(len, 65535);
|
||||
|
||||
@@ -7,11 +7,6 @@
|
||||
#ifndef _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
|
||||
#define _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_
|
||||
|
||||
#if defined SSL_R_MISSING_QUIC_TRANSPORT_PARAMETERS_EXTENSION \
|
||||
|| defined LIBRESSL_VERSION_NUMBER
|
||||
#undef NGX_QUIC_OPENSSL_COMPAT
|
||||
#else
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
@@ -53,7 +48,4 @@ int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params,
|
||||
void SSL_get_peer_quic_transport_params(const SSL *ssl,
|
||||
const uint8_t **out_params, size_t *out_params_len);
|
||||
|
||||
|
||||
#endif /* TLSEXT_TYPE_quic_transport_parameters */
|
||||
|
||||
#endif /* _NGX_EVENT_QUIC_OPENSSL_COMPAT_H_INCLUDED_ */
|
||||
@@ -45,9 +45,9 @@
|
||||
|
||||
|
||||
static ngx_int_t ngx_quic_create_datagrams(ngx_connection_t *c);
|
||||
static void ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
|
||||
static void ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
uint64_t pnum);
|
||||
static void ngx_quic_commit_send(ngx_connection_t *c);
|
||||
static void ngx_quic_revert_send(ngx_connection_t *c,
|
||||
uint64_t preserved_pnum[NGX_QUIC_SEND_CTX_LAST]);
|
||||
#if ((NGX_HAVE_UDP_SEGMENT) && (NGX_HAVE_MSGHDR_MSG_CONTROL))
|
||||
static ngx_uint_t ngx_quic_allow_segmentation(ngx_connection_t *c);
|
||||
static ngx_int_t ngx_quic_create_segments(ngx_connection_t *c);
|
||||
@@ -55,7 +55,8 @@ static ssize_t ngx_quic_send_segments(ngx_connection_t *c, u_char *buf,
|
||||
size_t len, struct sockaddr *sockaddr, socklen_t socklen, size_t segment);
|
||||
#endif
|
||||
static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
|
||||
ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
|
||||
ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min,
|
||||
ngx_uint_t ack_only);
|
||||
static void ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
ngx_quic_header_t *pkt, ngx_quic_path_t *path);
|
||||
static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
|
||||
@@ -127,8 +128,11 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
cg = &qc->congestion;
|
||||
path = qc->path;
|
||||
|
||||
while (cg->in_flight < cg->window) {
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
ngx_memzero(preserved_pnum, sizeof(preserved_pnum));
|
||||
#endif
|
||||
|
||||
do {
|
||||
p = dst;
|
||||
|
||||
len = ngx_quic_path_limit(c, path, path->mtu);
|
||||
@@ -150,16 +154,12 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
|
||||
if (min > len) {
|
||||
/* padding can't be applied - avoid sending the packet */
|
||||
|
||||
while (i-- > 0) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
ngx_quic_revert_send(c, ctx, preserved_pnum[i]);
|
||||
}
|
||||
|
||||
ngx_quic_revert_send(c, preserved_pnum);
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
n = ngx_quic_output_packet(c, ctx, p, len, min);
|
||||
n = ngx_quic_output_packet(c, ctx, p, len, min,
|
||||
cg->in_flight >= cg->window);
|
||||
if (n == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
@@ -180,73 +180,95 @@ ngx_quic_create_datagrams(ngx_connection_t *c)
|
||||
}
|
||||
|
||||
if (n == NGX_AGAIN) {
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ngx_quic_revert_send(c, &qc->send_ctx[i], preserved_pnum[i]);
|
||||
}
|
||||
|
||||
ngx_quic_revert_send(c, preserved_pnum);
|
||||
ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ngx_quic_commit_send(c, &qc->send_ctx[i]);
|
||||
}
|
||||
ngx_quic_commit_send(c);
|
||||
|
||||
path->sent += len;
|
||||
}
|
||||
|
||||
} while (cg->in_flight < cg->window);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_quic_commit_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
|
||||
ngx_quic_commit_send(ngx_connection_t *c)
|
||||
{
|
||||
ngx_uint_t i, idle;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_frame_t *f;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
cg = &qc->congestion;
|
||||
|
||||
while (!ngx_queue_empty(&ctx->sending)) {
|
||||
idle = 1;
|
||||
|
||||
q = ngx_queue_head(&ctx->sending);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
|
||||
ngx_queue_remove(q);
|
||||
if (!ngx_queue_empty(&ctx->frames)) {
|
||||
idle = 0;
|
||||
}
|
||||
|
||||
if (f->pkt_need_ack && !qc->closing) {
|
||||
ngx_queue_insert_tail(&ctx->sent, q);
|
||||
while (!ngx_queue_empty(&ctx->sending)) {
|
||||
|
||||
cg->in_flight += f->plen;
|
||||
q = ngx_queue_head(&ctx->sending);
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
|
||||
} else {
|
||||
ngx_quic_free_frame(c, f);
|
||||
ngx_queue_remove(q);
|
||||
|
||||
if (f->pkt_need_ack && !qc->closing) {
|
||||
ngx_queue_insert_tail(&ctx->sent, q);
|
||||
|
||||
cg->in_flight += f->plen;
|
||||
|
||||
} else {
|
||||
ngx_quic_free_frame(c, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic congestion send if:%uz", cg->in_flight);
|
||||
|
||||
ngx_quic_congestion_idle(c, idle);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_quic_revert_send(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
uint64_t pnum)
|
||||
ngx_quic_revert_send(ngx_connection_t *c, uint64_t pnum[NGX_QUIC_SEND_CTX_LAST])
|
||||
{
|
||||
ngx_queue_t *q;
|
||||
ngx_uint_t i;
|
||||
ngx_queue_t *q;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
while (!ngx_queue_empty(&ctx->sending)) {
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
q = ngx_queue_last(&ctx->sending);
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_head(&ctx->frames, q);
|
||||
for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
|
||||
ctx = &qc->send_ctx[i];
|
||||
|
||||
if (ngx_queue_empty(&ctx->sending)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
q = ngx_queue_last(&ctx->sending);
|
||||
ngx_queue_remove(q);
|
||||
ngx_queue_insert_head(&ctx->frames, q);
|
||||
} while (!ngx_queue_empty(&ctx->sending));
|
||||
|
||||
ctx->pnum = pnum[i];
|
||||
}
|
||||
|
||||
ctx->pnum = pnum;
|
||||
ngx_quic_congestion_idle(c, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -272,17 +294,17 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_INITIAL);
|
||||
if (!ngx_queue_empty(&ctx->frames)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_HANDSHAKE);
|
||||
if (!ngx_queue_empty(&ctx->frames)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
bytes = 0;
|
||||
len = ngx_min(qc->path->mtu, NGX_QUIC_MAX_UDP_SEGMENT_BUF);
|
||||
@@ -295,6 +317,10 @@ ngx_quic_allow_segmentation(ngx_connection_t *c)
|
||||
|
||||
bytes += f->len;
|
||||
|
||||
if (qc->congestion.in_flight + bytes >= qc->congestion.window) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bytes > len * 3) {
|
||||
/* require at least ~3 full packets to batch */
|
||||
return 1;
|
||||
@@ -311,19 +337,19 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
size_t len, segsize;
|
||||
ssize_t n;
|
||||
u_char *p, *end;
|
||||
uint64_t preserved_pnum;
|
||||
ngx_uint_t nseg;
|
||||
ngx_uint_t nseg, level;
|
||||
ngx_quic_path_t *path;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_congestion_t *cg;
|
||||
ngx_quic_connection_t *qc;
|
||||
static u_char dst[NGX_QUIC_MAX_UDP_SEGMENT_BUF];
|
||||
static uint64_t preserved_pnum[NGX_QUIC_SEND_CTX_LAST];
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cg = &qc->congestion;
|
||||
path = qc->path;
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_APPLICATION);
|
||||
|
||||
if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
@@ -335,7 +361,8 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
|
||||
nseg = 0;
|
||||
|
||||
preserved_pnum = ctx->pnum;
|
||||
level = ctx - qc->send_ctx;
|
||||
preserved_pnum[level] = ctx->pnum;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
@@ -343,7 +370,7 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
|
||||
if (len && cg->in_flight + (p - dst) < cg->window) {
|
||||
|
||||
n = ngx_quic_output_packet(c, ctx, p, len, len);
|
||||
n = ngx_quic_output_packet(c, ctx, p, len, len, 0);
|
||||
if (n == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
@@ -369,19 +396,18 @@ ngx_quic_create_segments(ngx_connection_t *c)
|
||||
}
|
||||
|
||||
if (n == NGX_AGAIN) {
|
||||
ngx_quic_revert_send(c, ctx, preserved_pnum);
|
||||
|
||||
ngx_quic_revert_send(c, preserved_pnum);
|
||||
ngx_add_timer(&qc->push, NGX_QUIC_SOCKET_RETRY_DELAY);
|
||||
break;
|
||||
}
|
||||
|
||||
ngx_quic_commit_send(c, ctx);
|
||||
ngx_quic_commit_send(c);
|
||||
|
||||
path->sent += n;
|
||||
|
||||
p = dst;
|
||||
nseg = 0;
|
||||
preserved_pnum = ctx->pnum;
|
||||
preserved_pnum[level] = ctx->pnum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,7 +437,7 @@ ngx_quic_send_segments(ngx_connection_t *c, u_char *buf, size_t len,
|
||||
ngx_memzero(msg_control, sizeof(msg_control));
|
||||
|
||||
iov.iov_len = len;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_base = (void *) buf;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
@@ -474,7 +500,7 @@ ngx_quic_get_padding_level(ngx_connection_t *c)
|
||||
*/
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_INITIAL);
|
||||
|
||||
for (q = ngx_queue_head(&ctx->frames);
|
||||
q != ngx_queue_sentinel(&ctx->frames);
|
||||
@@ -501,7 +527,7 @@ ngx_quic_get_padding_level(ngx_connection_t *c)
|
||||
|
||||
static ssize_t
|
||||
ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
u_char *data, size_t max, size_t min)
|
||||
u_char *data, size_t max, size_t min, ngx_uint_t ack_only)
|
||||
{
|
||||
size_t len, pad, min_payload, max_payload;
|
||||
u_char *p;
|
||||
@@ -565,6 +591,10 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
{
|
||||
f = ngx_queue_data(q, ngx_quic_frame_t, queue);
|
||||
|
||||
if (ack_only && f->type != NGX_QUIC_FT_ACK) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len >= max_payload) {
|
||||
break;
|
||||
}
|
||||
@@ -657,10 +687,10 @@ ngx_quic_init_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
|
||||
|
||||
pkt->flags = NGX_QUIC_PKT_FIXED_BIT;
|
||||
|
||||
if (ctx->level == ssl_encryption_initial) {
|
||||
if (ctx->level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
|
||||
|
||||
} else if (ctx->level == ssl_encryption_handshake) {
|
||||
} else if (ctx->level == NGX_QUIC_ENCRYPTION_HANDSHAKE) {
|
||||
pkt->flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;
|
||||
|
||||
} else {
|
||||
@@ -699,7 +729,7 @@ ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len,
|
||||
ngx_memzero(&msg, sizeof(struct msghdr));
|
||||
|
||||
iov.iov_len = len;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_base = (void *) buf;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
@@ -1073,7 +1103,7 @@ ngx_quic_send_new_token(ngx_connection_t *c, ngx_quic_path_t *path)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_NEW_TOKEN;
|
||||
frame->data = out;
|
||||
frame->u.token.length = token.len;
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_event.h>
|
||||
#include <ngx_event_quic_connection.h>
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
#include <openssl/hkdf.h>
|
||||
#include <openssl/chacha.h>
|
||||
#else
|
||||
#include <openssl/kdf.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* RFC 9001, 5.4.1. Header Protection Application: 5-byte mask */
|
||||
@@ -18,6 +24,9 @@
|
||||
#define NGX_QUIC_INITIAL_CIPHER TLS1_3_CK_AES_128_GCM_SHA256
|
||||
|
||||
|
||||
#define ngx_quic_md(str) { sizeof(str) - 1, str }
|
||||
|
||||
|
||||
static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len,
|
||||
const EVP_MD *digest, const u_char *prk, size_t prk_len,
|
||||
const u_char *info, size_t info_len);
|
||||
@@ -29,10 +38,10 @@ static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask,
|
||||
uint64_t *largest_pn);
|
||||
|
||||
static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out,
|
||||
u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
|
||||
#if !(NGX_QUIC_BORINGSSL_EVP_API)
|
||||
static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
|
||||
u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
|
||||
const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
|
||||
#endif
|
||||
|
||||
static ngx_int_t ngx_quic_crypto_hp_init(const EVP_CIPHER *cipher,
|
||||
@@ -55,7 +64,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
|
||||
switch (id) {
|
||||
|
||||
case TLS1_3_CK_AES_128_GCM_SHA256:
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
ciphers->c = EVP_aead_aes_128_gcm();
|
||||
#else
|
||||
ciphers->c = EVP_aes_128_gcm();
|
||||
@@ -66,7 +75,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
|
||||
break;
|
||||
|
||||
case TLS1_3_CK_AES_256_GCM_SHA384:
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
ciphers->c = EVP_aead_aes_256_gcm();
|
||||
#else
|
||||
ciphers->c = EVP_aes_256_gcm();
|
||||
@@ -77,12 +86,12 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
|
||||
break;
|
||||
|
||||
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
ciphers->c = EVP_aead_chacha20_poly1305();
|
||||
#else
|
||||
ciphers->c = EVP_chacha20_poly1305();
|
||||
#endif
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
ciphers->hp = (const EVP_CIPHER *) EVP_aead_chacha20_poly1305();
|
||||
#else
|
||||
ciphers->hp = EVP_chacha20();
|
||||
@@ -91,7 +100,7 @@ ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers)
|
||||
len = 32;
|
||||
break;
|
||||
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
#if !(NGX_QUIC_BORINGSSL_EVP_API)
|
||||
case TLS1_3_CK_AES_128_CCM_SHA256:
|
||||
ciphers->c = EVP_aes_128_ccm();
|
||||
ciphers->hp = EVP_aes_128_ctr();
|
||||
@@ -122,12 +131,13 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret,
|
||||
ngx_quic_secret_t *client, *server;
|
||||
ngx_quic_ciphers_t ciphers;
|
||||
|
||||
static const uint8_t salt[20] =
|
||||
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17"
|
||||
"\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a";
|
||||
static const uint8_t salt[20] = {
|
||||
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
|
||||
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
|
||||
};
|
||||
|
||||
client = &keys->secrets[ssl_encryption_initial].client;
|
||||
server = &keys->secrets[ssl_encryption_initial].server;
|
||||
client = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].client;
|
||||
server = &keys->secrets[NGX_QUIC_ENCRYPTION_INITIAL].server;
|
||||
|
||||
/*
|
||||
* RFC 9001, section 5. Packet Protection
|
||||
@@ -259,7 +269,7 @@ static ngx_int_t
|
||||
ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest,
|
||||
const uint8_t *prk, size_t prk_len, const u_char *info, size_t info_len)
|
||||
{
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
|
||||
if (HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len)
|
||||
== 0)
|
||||
@@ -321,7 +331,7 @@ ngx_hkdf_extract(u_char *out_key, size_t *out_len, const EVP_MD *digest,
|
||||
const u_char *secret, size_t secret_len, const u_char *salt,
|
||||
size_t salt_len)
|
||||
{
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
|
||||
if (HKDF_extract(out_key, out_len, digest, secret, secret_len, salt,
|
||||
salt_len)
|
||||
@@ -384,7 +394,7 @@ ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s,
|
||||
ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log)
|
||||
{
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
EVP_AEAD_CTX *ctx;
|
||||
|
||||
ctx = EVP_AEAD_CTX_new(cipher, key->data, key->len,
|
||||
@@ -441,10 +451,10 @@ ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s,
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce,
|
||||
ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
|
||||
ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
|
||||
{
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
if (EVP_AEAD_CTX_open(s->ctx, out->data, &out->len, out->len, nonce,
|
||||
s->iv.len, in->data, in->len, ad->data, ad->len)
|
||||
!= 1)
|
||||
@@ -461,10 +471,10 @@ ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce,
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce,
|
||||
ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, const u_char *nonce,
|
||||
ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
|
||||
{
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
if (EVP_AEAD_CTX_seal(s->ctx, out->data, &out->len, out->len, nonce,
|
||||
s->iv.len, in->data, in->len, ad->data, ad->len)
|
||||
!= 1)
|
||||
@@ -480,11 +490,11 @@ ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce,
|
||||
}
|
||||
|
||||
|
||||
#ifndef OPENSSL_IS_BORINGSSL
|
||||
#if !(NGX_QUIC_BORINGSSL_EVP_API)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce,
|
||||
ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
|
||||
ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out,
|
||||
const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log)
|
||||
{
|
||||
int len, enc;
|
||||
ngx_quic_crypto_ctx_t *ctx;
|
||||
@@ -559,7 +569,7 @@ void
|
||||
ngx_quic_crypto_cleanup(ngx_quic_secret_t *s)
|
||||
{
|
||||
if (s->ctx) {
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
EVP_AEAD_CTX_free(s->ctx);
|
||||
#else
|
||||
EVP_CIPHER_CTX_free(s->ctx);
|
||||
@@ -575,7 +585,7 @@ ngx_quic_crypto_hp_init(const EVP_CIPHER *cipher, ngx_quic_secret_t *s,
|
||||
{
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
if (cipher == (EVP_CIPHER *) EVP_aead_chacha20_poly1305()) {
|
||||
/* no EVP interface */
|
||||
s->hp_ctx = NULL;
|
||||
@@ -606,11 +616,12 @@ ngx_quic_crypto_hp(ngx_quic_secret_t *s, u_char *out, u_char *in,
|
||||
{
|
||||
int outlen;
|
||||
EVP_CIPHER_CTX *ctx;
|
||||
u_char zero[NGX_QUIC_HP_LEN] = {0};
|
||||
|
||||
static const u_char zero[NGX_QUIC_HP_LEN];
|
||||
|
||||
ctx = s->hp_ctx;
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (NGX_QUIC_BORINGSSL_EVP_API)
|
||||
uint32_t cnt;
|
||||
|
||||
if (ctx == NULL) {
|
||||
@@ -651,8 +662,8 @@ ngx_quic_crypto_hp_cleanup(ngx_quic_secret_t *s)
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write,
|
||||
ngx_quic_keys_t *keys, enum ssl_encryption_level_t level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len)
|
||||
ngx_quic_keys_t *keys, ngx_uint_t level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *secret, size_t secret_len)
|
||||
{
|
||||
ngx_int_t key_len;
|
||||
ngx_str_t secret_str;
|
||||
@@ -717,8 +728,8 @@ ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write,
|
||||
|
||||
|
||||
ngx_uint_t
|
||||
ngx_quic_keys_available(ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level, ngx_uint_t is_write)
|
||||
ngx_quic_keys_available(ngx_quic_keys_t *keys, ngx_uint_t level,
|
||||
ngx_uint_t is_write)
|
||||
{
|
||||
if (is_write == 0) {
|
||||
return keys->secrets[level].client.ctx != NULL;
|
||||
@@ -729,8 +740,7 @@ ngx_quic_keys_available(ngx_quic_keys_t *keys,
|
||||
|
||||
|
||||
void
|
||||
ngx_quic_keys_discard(ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level)
|
||||
ngx_quic_keys_discard(ngx_quic_keys_t *keys, ngx_uint_t level)
|
||||
{
|
||||
ngx_quic_secret_t *client, *server;
|
||||
|
||||
@@ -760,7 +770,7 @@ ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys)
|
||||
{
|
||||
ngx_quic_secrets_t *current, *next, tmp;
|
||||
|
||||
current = &keys->secrets[ssl_encryption_application];
|
||||
current = &keys->secrets[NGX_QUIC_ENCRYPTION_APPLICATION];
|
||||
next = &keys->next_key;
|
||||
|
||||
ngx_quic_crypto_cleanup(¤t->client);
|
||||
@@ -789,7 +799,7 @@ ngx_quic_keys_update(ngx_event_t *ev)
|
||||
qc = ngx_quic_get_connection(c);
|
||||
keys = qc->keys;
|
||||
|
||||
current = &keys->secrets[ssl_encryption_application];
|
||||
current = &keys->secrets[NGX_QUIC_ENCRYPTION_APPLICATION];
|
||||
next = &keys->next_key;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic key update");
|
||||
@@ -948,16 +958,16 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res)
|
||||
{
|
||||
u_char *start;
|
||||
ngx_str_t ad, itag;
|
||||
ngx_quic_md_t key;
|
||||
ngx_quic_secret_t secret;
|
||||
ngx_quic_ciphers_t ciphers;
|
||||
|
||||
/* 5.8. Retry Packet Integrity */
|
||||
static u_char key_data[16] =
|
||||
"\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e";
|
||||
static u_char nonce[NGX_QUIC_IV_LEN] =
|
||||
"\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb";
|
||||
static ngx_str_t in = ngx_string("");
|
||||
static ngx_quic_md_t key = ngx_quic_md(
|
||||
"\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e");
|
||||
static const u_char nonce[NGX_QUIC_IV_LEN] = {
|
||||
0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb
|
||||
};
|
||||
static ngx_str_t in = ngx_string("");
|
||||
|
||||
ad.data = res->data;
|
||||
ad.len = ngx_quic_create_retry_itag(pkt, ad.data, &start);
|
||||
@@ -974,8 +984,6 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
key.len = sizeof(key_data);
|
||||
ngx_memcpy(key.data, key_data, sizeof(key_data));
|
||||
secret.iv.len = NGX_QUIC_IV_LEN;
|
||||
|
||||
if (ngx_quic_crypto_init(ciphers.c, &secret, &key, 1, pkt->log)
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
#include <ngx_event_quic_transport.h>
|
||||
|
||||
|
||||
#define NGX_QUIC_ENCRYPTION_LAST ((ssl_encryption_application) + 1)
|
||||
|
||||
/* RFC 5116, 5.1/5.3 and RFC 8439, 2.3/2.5 for all supported ciphers */
|
||||
#define NGX_QUIC_IV_LEN 12
|
||||
#define NGX_QUIC_TAG_LEN 16
|
||||
@@ -24,10 +22,12 @@
|
||||
#define NGX_QUIC_MAX_MD_SIZE 48
|
||||
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC)
|
||||
#define NGX_QUIC_BORINGSSL_EVP_API 1
|
||||
#define ngx_quic_cipher_t EVP_AEAD
|
||||
#define ngx_quic_crypto_ctx_t EVP_AEAD_CTX
|
||||
#else
|
||||
#define NGX_QUIC_BORINGSSL_EVP_API 0
|
||||
#define ngx_quic_cipher_t EVP_CIPHER
|
||||
#define ngx_quic_crypto_ctx_t EVP_CIPHER_CTX
|
||||
#endif
|
||||
@@ -94,13 +94,11 @@ typedef struct {
|
||||
ngx_int_t ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys,
|
||||
ngx_str_t *secret, ngx_log_t *log);
|
||||
ngx_int_t ngx_quic_keys_set_encryption_secret(ngx_log_t *log,
|
||||
ngx_uint_t is_write, ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *secret, size_t secret_len);
|
||||
ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level, ngx_uint_t is_write);
|
||||
void ngx_quic_keys_discard(ngx_quic_keys_t *keys,
|
||||
enum ssl_encryption_level_t level);
|
||||
ngx_uint_t is_write, ngx_quic_keys_t *keys, ngx_uint_t level,
|
||||
const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len);
|
||||
ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys, ngx_uint_t level,
|
||||
ngx_uint_t is_write);
|
||||
void ngx_quic_keys_discard(ngx_quic_keys_t *keys, ngx_uint_t level);
|
||||
void ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys);
|
||||
void ngx_quic_keys_update(ngx_event_t *ev);
|
||||
void ngx_quic_keys_cleanup(ngx_quic_keys_t *keys);
|
||||
@@ -111,7 +109,7 @@ ngx_int_t ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers);
|
||||
ngx_int_t ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher,
|
||||
ngx_quic_secret_t *s, ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log);
|
||||
ngx_int_t ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out,
|
||||
u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
|
||||
const u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log);
|
||||
void ngx_quic_crypto_cleanup(ngx_quic_secret_t *s);
|
||||
ngx_int_t ngx_quic_hkdf_expand(ngx_quic_hkdf_t *hkdf, const EVP_MD *digest,
|
||||
ngx_log_t *log);
|
||||
|
||||
+522
-126
@@ -10,13 +10,6 @@
|
||||
#include <ngx_event_quic_connection.h>
|
||||
|
||||
|
||||
#if defined OPENSSL_IS_BORINGSSL \
|
||||
|| defined LIBRESSL_VERSION_NUMBER \
|
||||
|| NGX_QUIC_OPENSSL_COMPAT
|
||||
#define NGX_QUIC_BORINGSSL_API 1
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* RFC 9000, 7.5. Cryptographic Message Buffering
|
||||
*
|
||||
@@ -25,43 +18,349 @@
|
||||
#define NGX_QUIC_MAX_BUFFERED 65535
|
||||
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
|
||||
static int ngx_quic_cbs_send(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char *data, size_t len, size_t *consumed, void *arg);
|
||||
static int ngx_quic_cbs_recv_rcd(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char **data, size_t *bytes_read, void *arg);
|
||||
static int ngx_quic_cbs_release_rcd(ngx_ssl_conn_t *ssl_conn,
|
||||
size_t bytes_read, void *arg);
|
||||
static int ngx_quic_cbs_yield_secret(ngx_ssl_conn_t *ssl_conn, uint32_t level,
|
||||
int direction, const unsigned char *secret, size_t secret_len, void *arg);
|
||||
static int ngx_quic_cbs_got_transport_params(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char *params, size_t params_len, void *arg);
|
||||
static int ngx_quic_cbs_alert(ngx_ssl_conn_t *ssl_conn, unsigned char alert,
|
||||
void *arg);
|
||||
|
||||
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
|
||||
|
||||
static ngx_inline ngx_uint_t ngx_quic_map_encryption_level(
|
||||
enum ssl_encryption_level_t ssl_level);
|
||||
|
||||
#if (NGX_QUIC_BORINGSSL_API)
|
||||
static int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *secret, size_t secret_len);
|
||||
static int ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *secret, size_t secret_len);
|
||||
#else
|
||||
#else /* NGX_QUIC_QUICTLS_API */
|
||||
static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const uint8_t *read_secret,
|
||||
enum ssl_encryption_level_t ssl_level, const uint8_t *read_secret,
|
||||
const uint8_t *write_secret, size_t secret_len);
|
||||
#endif
|
||||
|
||||
static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const uint8_t *data, size_t len);
|
||||
enum ssl_encryption_level_t ssl_level, const uint8_t *data, size_t len);
|
||||
static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
|
||||
static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, uint8_t alert);
|
||||
static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
enum ssl_encryption_level_t level);
|
||||
enum ssl_encryption_level_t ssl_level, uint8_t alert);
|
||||
|
||||
#endif
|
||||
|
||||
static ngx_int_t ngx_quic_handshake(ngx_connection_t *c);
|
||||
static ngx_int_t ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level);
|
||||
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_send(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char *data, size_t len, size_t *consumed, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_chain_t *out;
|
||||
unsigned int alpn_len;
|
||||
ngx_quic_frame_t *frame;
|
||||
const unsigned char *alpn_data;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_send len:%uz", len);
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
*consumed = 0;
|
||||
|
||||
SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);
|
||||
|
||||
if (alpn_len == 0) {
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||
qc->error_reason = "missing ALPN extension";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic missing ALPN extension");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!qc->client_tp_done) {
|
||||
/* RFC 9001, 8.2. QUIC Transport Parameters Extension */
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
|
||||
qc->error_reason = "missing transport parameters";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"missing transport parameters");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, qc->write_level);
|
||||
|
||||
out = ngx_quic_copy_buffer(c, (u_char *) data, len);
|
||||
if (out == NGX_CHAIN_ERROR) {
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
frame = ngx_quic_alloc_frame(c);
|
||||
if (frame == NULL) {
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
frame->data = out;
|
||||
frame->level = qc->write_level;
|
||||
frame->type = NGX_QUIC_FT_CRYPTO;
|
||||
frame->u.crypto.offset = ctx->crypto_sent;
|
||||
frame->u.crypto.length = len;
|
||||
|
||||
ctx->crypto_sent += len;
|
||||
*consumed = len;
|
||||
|
||||
ngx_quic_queue_frame(qc, frame);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_recv_rcd(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char **data, size_t *bytes_read, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_recv_rcd");
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, qc->read_level);
|
||||
|
||||
for (cl = ctx->crypto.chain; cl; cl = cl->next) {
|
||||
b = cl->buf;
|
||||
|
||||
if (b->sync) {
|
||||
/* hole */
|
||||
|
||||
*bytes_read = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
*data = b->pos;
|
||||
*bytes_read = b->last - b->pos;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_release_rcd(ngx_ssl_conn_t *ssl_conn, size_t bytes_read, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_chain_t *cl;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_release_rcd len:%uz", bytes_read);
|
||||
|
||||
/* already closed on handshake failure */
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
if (qc == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, qc->read_level);
|
||||
|
||||
cl = ngx_quic_read_buffer(c, &ctx->crypto, bytes_read);
|
||||
if (cl == NGX_CHAIN_ERROR) {
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ngx_quic_free_chain(c, cl);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_yield_secret(ngx_ssl_conn_t *ssl_conn, uint32_t ssl_level,
|
||||
int direction, const unsigned char *secret, size_t secret_len, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_uint_t level;
|
||||
const SSL_CIPHER *cipher;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_yield_secret() level:%uD", ssl_level);
|
||||
#ifdef NGX_QUIC_DEBUG_CRYPTO
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic %s secret len:%uz %*xs",
|
||||
direction ? "write" : "read", secret_len,
|
||||
secret_len, secret);
|
||||
#endif
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
cipher = SSL_get_current_cipher(ssl_conn);
|
||||
|
||||
switch (ssl_level) {
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_NONE:
|
||||
level = NGX_QUIC_ENCRYPTION_INITIAL;
|
||||
break;
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_EARLY:
|
||||
level = NGX_QUIC_ENCRYPTION_EARLY_DATA;
|
||||
break;
|
||||
case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE:
|
||||
level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
|
||||
break;
|
||||
default: /* OSSL_RECORD_PROTECTION_LEVEL_APPLICATION */
|
||||
level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ngx_quic_keys_set_encryption_secret(c->log, direction, qc->keys, level,
|
||||
cipher, secret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (direction) {
|
||||
qc->write_level = level;
|
||||
|
||||
} else {
|
||||
qc->read_level = level;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_got_transport_params(ngx_ssl_conn_t *ssl_conn,
|
||||
const unsigned char *params, size_t params_len, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
u_char *p, *end;
|
||||
ngx_quic_tp_t ctp;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_got_transport_params() len:%uz",
|
||||
params_len);
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
/* defaults for parameters not sent by client */
|
||||
ngx_memcpy(&ctp, &qc->ctp, sizeof(ngx_quic_tp_t));
|
||||
|
||||
p = (u_char *) params;
|
||||
end = p + params_len;
|
||||
|
||||
if (ngx_quic_parse_transport_params(p, end, &ctp, c->log) != NGX_OK) {
|
||||
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
|
||||
qc->error_reason = "failed to process transport parameters";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
qc->client_tp_done = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_cbs_alert(ngx_ssl_conn_t *ssl_conn, unsigned char alert, void *arg)
|
||||
{
|
||||
ngx_connection_t *c = arg;
|
||||
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_cbs_alert() alert:%d", (int) alert);
|
||||
|
||||
/* already closed on regular shutdown */
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
if (qc == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(alert);
|
||||
qc->error_reason = "handshake failed";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
|
||||
|
||||
|
||||
static ngx_inline ngx_uint_t
|
||||
ngx_quic_map_encryption_level(enum ssl_encryption_level_t ssl_level)
|
||||
{
|
||||
switch (ssl_level) {
|
||||
case ssl_encryption_initial:
|
||||
return NGX_QUIC_ENCRYPTION_INITIAL;
|
||||
case ssl_encryption_early_data:
|
||||
return NGX_QUIC_ENCRYPTION_EARLY_DATA;
|
||||
case ssl_encryption_handshake:
|
||||
return NGX_QUIC_ENCRYPTION_HANDSHAKE;
|
||||
default: /* ssl_encryption_application */
|
||||
return NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if (NGX_QUIC_BORINGSSL_API)
|
||||
|
||||
static int
|
||||
ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *rsecret, size_t secret_len)
|
||||
{
|
||||
ngx_uint_t level;
|
||||
ngx_connection_t *c;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
qc = ngx_quic_get_connection(c);
|
||||
level = ngx_quic_map_encryption_level(ssl_level);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_set_read_secret() level:%d", level);
|
||||
"quic ngx_quic_set_read_secret() level:%d", ssl_level);
|
||||
#ifdef NGX_QUIC_DEBUG_CRYPTO
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic read secret len:%uz %*xs", secret_len,
|
||||
@@ -72,7 +371,7 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
cipher, rsecret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -81,17 +380,19 @@ ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
|
||||
static int
|
||||
ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const SSL_CIPHER *cipher,
|
||||
enum ssl_encryption_level_t ssl_level, const SSL_CIPHER *cipher,
|
||||
const uint8_t *wsecret, size_t secret_len)
|
||||
{
|
||||
ngx_uint_t level;
|
||||
ngx_connection_t *c;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
qc = ngx_quic_get_connection(c);
|
||||
level = ngx_quic_map_encryption_level(ssl_level);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_set_write_secret() level:%d", level);
|
||||
"quic ngx_quic_set_write_secret() level:%d", ssl_level);
|
||||
#ifdef NGX_QUIC_DEBUG_CRYPTO
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic write secret len:%uz %*xs", secret_len,
|
||||
@@ -102,28 +403,31 @@ ngx_quic_set_write_secret(ngx_ssl_conn_t *ssl_conn,
|
||||
cipher, wsecret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
#else /* NGX_QUIC_QUICTLS_API */
|
||||
|
||||
static int
|
||||
ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const uint8_t *rsecret,
|
||||
enum ssl_encryption_level_t ssl_level, const uint8_t *rsecret,
|
||||
const uint8_t *wsecret, size_t secret_len)
|
||||
{
|
||||
ngx_uint_t level;
|
||||
ngx_connection_t *c;
|
||||
const SSL_CIPHER *cipher;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
qc = ngx_quic_get_connection(c);
|
||||
level = ngx_quic_map_encryption_level(ssl_level);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_set_encryption_secrets() level:%d", level);
|
||||
"quic ngx_quic_set_encryption_secrets() level:%d",
|
||||
ssl_level);
|
||||
#ifdef NGX_QUIC_DEBUG_CRYPTO
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic read secret len:%uz %*xs", secret_len,
|
||||
@@ -136,10 +440,11 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
cipher, rsecret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (level == ssl_encryption_early_data) {
|
||||
if (level == NGX_QUIC_ENCRYPTION_EARLY_DATA) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -153,7 +458,7 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
cipher, wsecret, secret_len)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -164,24 +469,24 @@ ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
|
||||
|
||||
static int
|
||||
ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t level, const uint8_t *data, size_t len)
|
||||
enum ssl_encryption_level_t ssl_level, const uint8_t *data, size_t len)
|
||||
{
|
||||
u_char *p, *end;
|
||||
size_t client_params_len;
|
||||
ngx_uint_t level;
|
||||
ngx_chain_t *out;
|
||||
unsigned int alpn_len;
|
||||
const uint8_t *client_params;
|
||||
ngx_quic_tp_t ctp;
|
||||
ngx_quic_frame_t *frame;
|
||||
ngx_connection_t *c;
|
||||
const unsigned char *alpn_data;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
||||
unsigned int alpn_len;
|
||||
const unsigned char *alpn_data;
|
||||
#endif
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
qc = ngx_quic_get_connection(c);
|
||||
level = ngx_quic_map_encryption_level(ssl_level);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_add_handshake_data");
|
||||
@@ -193,21 +498,20 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
* here;
|
||||
*/
|
||||
|
||||
#if defined(TLSEXT_TYPE_application_layer_protocol_negotiation)
|
||||
|
||||
SSL_get0_alpn_selected(ssl_conn, &alpn_data, &alpn_len);
|
||||
|
||||
if (alpn_len == 0) {
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||
qc->error_reason = "unsupported protocol in ALPN extension";
|
||||
if (qc->error == 0) {
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_NO_APPLICATION_PROTOCOL);
|
||||
qc->error_reason = "missing ALPN extension";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic unsupported protocol in ALPN extension");
|
||||
return 0;
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"quic missing ALPN extension");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SSL_get_peer_quic_transport_params(ssl_conn, &client_params,
|
||||
&client_params_len);
|
||||
|
||||
@@ -217,12 +521,16 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
|
||||
if (client_params_len == 0) {
|
||||
/* RFC 9001, 8.2. QUIC Transport Parameters Extension */
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
|
||||
qc->error_reason = "missing transport parameters";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"missing transport parameters");
|
||||
return 0;
|
||||
if (qc->error == 0) {
|
||||
qc->error = NGX_QUIC_ERR_CRYPTO(SSL_AD_MISSING_EXTENSION);
|
||||
qc->error_reason = "missing transport parameters";
|
||||
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
"missing transport parameters");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
p = (u_char *) client_params;
|
||||
@@ -237,11 +545,11 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
qc->error = NGX_QUIC_ERR_TRANSPORT_PARAMETER_ERROR;
|
||||
qc->error_reason = "failed to process transport parameters";
|
||||
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ngx_quic_apply_transport_params(c, &ctp) != NGX_OK) {
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
qc->client_tp_done = 1;
|
||||
@@ -251,12 +559,14 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
|
||||
|
||||
out = ngx_quic_copy_buffer(c, (u_char *) data, len);
|
||||
if (out == NGX_CHAIN_ERROR) {
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
frame = ngx_quic_alloc_frame(c);
|
||||
if (frame == NULL) {
|
||||
return 0;
|
||||
qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
|
||||
return 1;
|
||||
}
|
||||
|
||||
frame->data = out;
|
||||
@@ -279,7 +589,7 @@ ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
|
||||
#if (NGX_DEBUG)
|
||||
ngx_connection_t *c;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_flush_flight()");
|
||||
@@ -289,17 +599,17 @@ ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
|
||||
|
||||
|
||||
static int
|
||||
ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
|
||||
uint8_t alert)
|
||||
ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
|
||||
enum ssl_encryption_level_t ssl_level, uint8_t alert)
|
||||
{
|
||||
ngx_connection_t *c;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
|
||||
c = ngx_ssl_get_connection(ssl_conn);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
||||
"quic ngx_quic_send_alert() level:%s alert:%d",
|
||||
ngx_quic_level_name(level), (int) alert);
|
||||
"quic ngx_quic_send_alert() level:%d alert:%d",
|
||||
ssl_level, (int) alert);
|
||||
|
||||
/* already closed on regular shutdown */
|
||||
|
||||
@@ -314,13 +624,14 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
ngx_quic_frame_t *frame)
|
||||
{
|
||||
uint64_t last;
|
||||
ngx_chain_t *cl;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
ngx_quic_crypto_frame_t *f;
|
||||
@@ -343,13 +654,13 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
}
|
||||
|
||||
if (last <= ctx->crypto.offset) {
|
||||
if (pkt->level == ssl_encryption_initial) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
/* speeding up handshake completion */
|
||||
|
||||
if (!ngx_queue_empty(&ctx->sent)) {
|
||||
ngx_quic_resend_frames(c, ctx);
|
||||
|
||||
ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
|
||||
ctx = ngx_quic_get_send_ctx(qc, NGX_QUIC_ENCRYPTION_HANDSHAKE);
|
||||
while (!ngx_queue_empty(&ctx->sent)) {
|
||||
ngx_quic_resend_frames(c, ctx);
|
||||
}
|
||||
@@ -359,43 +670,25 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (f->offset == ctx->crypto.offset) {
|
||||
if (ngx_quic_crypto_input(c, frame->data, pkt->level) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_quic_skip_buffer(c, &ctx->crypto, last);
|
||||
|
||||
} else {
|
||||
if (ngx_quic_write_buffer(c, &ctx->crypto, frame->data, f->length,
|
||||
f->offset)
|
||||
== NGX_CHAIN_ERROR)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
if (ngx_quic_write_buffer(c, &ctx->crypto, frame->data, f->length,
|
||||
f->offset)
|
||||
== NGX_CHAIN_ERROR)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1);
|
||||
|
||||
if (cl) {
|
||||
if (ngx_quic_crypto_input(c, cl, pkt->level) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_quic_free_chain(c, cl);
|
||||
if (ngx_quic_crypto_provide(c, pkt->level) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
return ngx_quic_handshake(c);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
enum ssl_encryption_level_t level)
|
||||
ngx_quic_handshake(ngx_connection_t *c)
|
||||
{
|
||||
int n, sslerr;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
ngx_ssl_conn_t *ssl_conn;
|
||||
ngx_quic_frame_t *frame;
|
||||
ngx_quic_connection_t *qc;
|
||||
@@ -404,16 +697,6 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
|
||||
ssl_conn = c->ssl->connection;
|
||||
|
||||
for (cl = data; cl; cl = cl->next) {
|
||||
b = cl->buf;
|
||||
|
||||
if (!SSL_provide_quic_data(ssl_conn, level, b->pos, b->last - b->pos)) {
|
||||
ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
|
||||
"SSL_provide_quic_data() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
n = SSL_do_handshake(ssl_conn);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
|
||||
@@ -424,22 +707,31 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d",
|
||||
sslerr);
|
||||
|
||||
if (c->ssl->handshake_rejected) {
|
||||
ngx_connection_error(c, 0, "handshake rejected");
|
||||
ERR_clear_error();
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (qc->error) {
|
||||
ngx_connection_error(c, 0, "SSL_do_handshake() failed");
|
||||
ERR_clear_error();
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (sslerr != SSL_ERROR_WANT_READ) {
|
||||
|
||||
if (c->ssl->handshake_rejected) {
|
||||
ngx_connection_error(c, 0, "handshake rejected");
|
||||
ERR_clear_error();
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_ssl_error(NGX_LOG_ERR, c->log, 0, "SSL_do_handshake() failed");
|
||||
ngx_ssl_connection_error(c, sslerr, 0, "SSL_do_handshake() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (n <= 0 || SSL_in_init(ssl_conn)) {
|
||||
if (ngx_quic_keys_available(qc->keys, ssl_encryption_early_data, 0)
|
||||
if (qc->error) {
|
||||
ngx_connection_error(c, 0, "SSL_do_handshake() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (!SSL_is_init_finished(ssl_conn)) {
|
||||
if (ngx_quic_keys_available(qc->keys, NGX_QUIC_ENCRYPTION_EARLY_DATA, 0)
|
||||
&& qc->client_tp_done)
|
||||
{
|
||||
if (ngx_quic_init_streams(c) != NGX_OK) {
|
||||
@@ -461,7 +753,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_HANDSHAKE_DONE;
|
||||
ngx_quic_queue_frame(qc, frame);
|
||||
|
||||
@@ -485,7 +777,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
* An endpoint MUST discard its Handshake keys
|
||||
* when the TLS handshake is confirmed.
|
||||
*/
|
||||
ngx_quic_discard_ctx(c, ssl_encryption_handshake);
|
||||
ngx_quic_discard_ctx(c, NGX_QUIC_ENCRYPTION_HANDSHAKE);
|
||||
|
||||
ngx_quic_discover_path_mtu(c, qc->path);
|
||||
|
||||
@@ -502,17 +794,97 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data,
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_quic_crypto_provide(ngx_connection_t *c, ngx_uint_t level)
|
||||
{
|
||||
#if (NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API)
|
||||
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *out, *cl;
|
||||
ngx_quic_send_ctx_t *ctx;
|
||||
ngx_quic_connection_t *qc;
|
||||
enum ssl_encryption_level_t ssl_level;
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
ctx = ngx_quic_get_send_ctx(qc, level);
|
||||
|
||||
out = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1);
|
||||
if (out == NGX_CHAIN_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
switch (level) {
|
||||
case NGX_QUIC_ENCRYPTION_INITIAL:
|
||||
ssl_level = ssl_encryption_initial;
|
||||
break;
|
||||
case NGX_QUIC_ENCRYPTION_EARLY_DATA:
|
||||
ssl_level = ssl_encryption_early_data;
|
||||
break;
|
||||
case NGX_QUIC_ENCRYPTION_HANDSHAKE:
|
||||
ssl_level = ssl_encryption_handshake;
|
||||
break;
|
||||
default: /* NGX_QUIC_ENCRYPTION_APPLICATION */
|
||||
ssl_level = ssl_encryption_application;
|
||||
break;
|
||||
}
|
||||
|
||||
for (cl = out; cl; cl = cl->next) {
|
||||
b = cl->buf;
|
||||
|
||||
if (!SSL_provide_quic_data(c->ssl->connection, ssl_level, b->pos,
|
||||
b->last - b->pos))
|
||||
{
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"SSL_provide_quic_data() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
ngx_quic_free_chain(c, out);
|
||||
|
||||
#endif
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_quic_init_connection(ngx_connection_t *c)
|
||||
{
|
||||
u_char *p;
|
||||
size_t clen;
|
||||
ssize_t len;
|
||||
ngx_str_t dcid;
|
||||
ngx_ssl_conn_t *ssl_conn;
|
||||
ngx_quic_socket_t *qsock;
|
||||
ngx_quic_connection_t *qc;
|
||||
static SSL_QUIC_METHOD quic_method;
|
||||
u_char *p;
|
||||
size_t clen;
|
||||
ssize_t len;
|
||||
ngx_str_t dcid;
|
||||
ngx_ssl_conn_t *ssl_conn;
|
||||
ngx_quic_socket_t *qsock;
|
||||
ngx_quic_connection_t *qc;
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
static const OSSL_DISPATCH qtdis[] = {
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_SEND,
|
||||
(void (*)(void)) ngx_quic_cbs_send },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RECV_RCD,
|
||||
(void (*)(void)) ngx_quic_cbs_recv_rcd },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_CRYPTO_RELEASE_RCD,
|
||||
(void (*)(void)) ngx_quic_cbs_release_rcd },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_YIELD_SECRET,
|
||||
(void (*)(void)) ngx_quic_cbs_yield_secret },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_GOT_TRANSPORT_PARAMS,
|
||||
(void (*)(void)) ngx_quic_cbs_got_transport_params },
|
||||
|
||||
{ OSSL_FUNC_SSL_QUIC_TLS_ALERT,
|
||||
(void (*)(void)) ngx_quic_cbs_alert },
|
||||
|
||||
{ 0, NULL }
|
||||
};
|
||||
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
|
||||
static SSL_QUIC_METHOD quic_method;
|
||||
#endif
|
||||
|
||||
qc = ngx_quic_get_connection(c);
|
||||
|
||||
@@ -524,6 +896,20 @@ ngx_quic_init_connection(ngx_connection_t *c)
|
||||
|
||||
ssl_conn = c->ssl->connection;
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
|
||||
if (SSL_set_quic_tls_cbs(ssl_conn, qtdis, c) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_tls_cbs() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
|
||||
SSL_set_quic_tls_early_data_enabled(ssl_conn, 1);
|
||||
}
|
||||
|
||||
#else /* NGX_QUIC_BORINGSSL_API || NGX_QUIC_QUICTLS_API */
|
||||
|
||||
if (!quic_method.send_alert) {
|
||||
#if (NGX_QUIC_BORINGSSL_API)
|
||||
quic_method.set_read_secret = ngx_quic_set_read_secret;
|
||||
@@ -537,15 +923,17 @@ ngx_quic_init_connection(ngx_connection_t *c)
|
||||
}
|
||||
|
||||
if (SSL_set_quic_method(ssl_conn, &quic_method) == 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_method() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_INFO_QUIC
|
||||
#if (NGX_QUIC_QUICTLS_API)
|
||||
if (SSL_CTX_get_max_early_data(qc->conf->ssl->ctx)) {
|
||||
SSL_set_quic_early_data_enabled(ssl_conn, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
qsock = ngx_quic_get_socket(c);
|
||||
@@ -577,15 +965,23 @@ ngx_quic_init_connection(ngx_connection_t *c)
|
||||
"quic transport parameters len:%uz %*xs", len, len, p);
|
||||
#endif
|
||||
|
||||
#if (NGX_QUIC_OPENSSL_API)
|
||||
if (SSL_set_quic_tls_transport_params(ssl_conn, p, len) == 0) {
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_tls_transport_params() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#else
|
||||
if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_transport_params() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
#if (defined OPENSSL_IS_BORINGSSL || defined OPENSSL_IS_AWSLC)
|
||||
if (SSL_set_quic_early_data_context(ssl_conn, p, clen) == 0) {
|
||||
ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
||||
ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
|
||||
"quic SSL_set_quic_early_data_context() failed");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ ngx_int_t
|
||||
ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc)
|
||||
{
|
||||
ngx_pool_t *pool;
|
||||
ngx_queue_t *q;
|
||||
ngx_queue_t *q, posted_events;
|
||||
ngx_rbtree_t *tree;
|
||||
ngx_connection_t *sc;
|
||||
ngx_rbtree_node_t *node;
|
||||
@@ -197,6 +197,8 @@ ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc)
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ngx_queue_init(&posted_events);
|
||||
|
||||
node = ngx_rbtree_min(tree->root, tree->sentinel);
|
||||
|
||||
while (node) {
|
||||
@@ -213,15 +215,21 @@ ngx_quic_close_streams(ngx_connection_t *c, ngx_quic_connection_t *qc)
|
||||
}
|
||||
|
||||
sc->read->error = 1;
|
||||
sc->read->ready = 1;
|
||||
sc->write->error = 1;
|
||||
|
||||
ngx_quic_set_event(sc->read);
|
||||
ngx_quic_set_event(sc->write);
|
||||
sc->write->ready = 1;
|
||||
|
||||
sc->close = 1;
|
||||
sc->read->handler(sc->read);
|
||||
|
||||
if (sc->read->posted) {
|
||||
ngx_delete_posted_event(sc->read);
|
||||
}
|
||||
|
||||
ngx_post_event(sc->read, &posted_events);
|
||||
}
|
||||
|
||||
ngx_event_process_posted((ngx_cycle_t *) ngx_cycle, &posted_events);
|
||||
|
||||
if (tree->root == tree->sentinel) {
|
||||
return NGX_OK;
|
||||
}
|
||||
@@ -272,7 +280,7 @@ ngx_quic_do_reset_stream(ngx_quic_stream_t *qs, ngx_uint_t err)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_RESET_STREAM;
|
||||
frame->u.reset_stream.id = qs->id;
|
||||
frame->u.reset_stream.error_code = err;
|
||||
@@ -359,7 +367,7 @@ ngx_quic_shutdown_stream_recv(ngx_connection_t *c)
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, 0,
|
||||
"quic stream id:0x%xL recv shutdown", qs->id);
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_STOP_SENDING;
|
||||
frame->u.stop_sending.id = qs->id;
|
||||
frame->u.stop_sending.error_code = qc->conf->stream_close_code;
|
||||
@@ -519,7 +527,7 @@ ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_RESET_STREAM;
|
||||
frame->u.reset_stream.id = id;
|
||||
frame->u.reset_stream.error_code = code;
|
||||
@@ -532,7 +540,7 @@ ngx_quic_reject_stream(ngx_connection_t *c, uint64_t id)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_STOP_SENDING;
|
||||
frame->u.stop_sending.id = id;
|
||||
frame->u.stop_sending.error_code = code;
|
||||
@@ -1054,7 +1062,7 @@ ngx_quic_stream_flush(ngx_quic_stream_t *qs)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_STREAM;
|
||||
frame->data = out;
|
||||
|
||||
@@ -1172,7 +1180,7 @@ ngx_quic_close_stream(ngx_quic_stream_t *qs)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_MAX_STREAMS;
|
||||
|
||||
if (qs->id & NGX_QUIC_STREAM_UNIDIRECTIONAL) {
|
||||
@@ -1763,7 +1771,7 @@ ngx_quic_update_max_stream_data(ngx_quic_stream_t *qs)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_MAX_STREAM_DATA;
|
||||
frame->u.max_stream_data.id = qs->id;
|
||||
frame->u.max_stream_data.limit = qs->recv_max_data;
|
||||
@@ -1799,7 +1807,7 @@ ngx_quic_update_max_data(ngx_connection_t *c)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
frame->level = ssl_encryption_application;
|
||||
frame->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
frame->type = NGX_QUIC_FT_MAX_DATA;
|
||||
frame->u.max_data.max_data = qc->streams.recv_max_data;
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ ngx_int_t
|
||||
ngx_quic_parse_packet(ngx_quic_header_t *pkt)
|
||||
{
|
||||
if (!ngx_quic_long_pkt(pkt->flags)) {
|
||||
pkt->level = ssl_encryption_application;
|
||||
pkt->level = NGX_QUIC_ENCRYPTION_APPLICATION;
|
||||
|
||||
if (ngx_quic_parse_short_header(pkt, NGX_QUIC_SERVER_CID_LEN) != NGX_OK)
|
||||
{
|
||||
@@ -295,6 +295,11 @@ ngx_quic_parse_packet(ngx_quic_header_t *pkt)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (pkt->version == 0) {
|
||||
/* version negotiation */
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (!ngx_quic_supported_version(pkt->version)) {
|
||||
return NGX_ABORT;
|
||||
}
|
||||
@@ -463,13 +468,13 @@ ngx_quic_parse_long_header_v1(ngx_quic_header_t *pkt)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
pkt->level = ssl_encryption_initial;
|
||||
pkt->level = NGX_QUIC_ENCRYPTION_INITIAL;
|
||||
|
||||
} else if (ngx_quic_pkt_zrtt(pkt->flags)) {
|
||||
pkt->level = ssl_encryption_early_data;
|
||||
pkt->level = NGX_QUIC_ENCRYPTION_EARLY_DATA;
|
||||
|
||||
} else if (ngx_quic_pkt_hs(pkt->flags)) {
|
||||
pkt->level = ssl_encryption_handshake;
|
||||
pkt->level = NGX_QUIC_ENCRYPTION_HANDSHAKE;
|
||||
|
||||
} else {
|
||||
ngx_log_error(NGX_LOG_INFO, pkt->log, 0,
|
||||
@@ -588,7 +593,7 @@ ngx_quic_payload_size(ngx_quic_header_t *pkt, size_t pkt_len)
|
||||
|
||||
/* flags, version, dcid and scid with lengths and zero-length token */
|
||||
len = 5 + 2 + pkt->dcid.len + pkt->scid.len
|
||||
+ (pkt->level == ssl_encryption_initial ? 1 : 0);
|
||||
+ (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL ? 1 : 0);
|
||||
|
||||
if (len > pkt_len) {
|
||||
return 0;
|
||||
@@ -627,7 +632,7 @@ ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
|
||||
if (out == NULL) {
|
||||
return 5 + 2 + pkt->dcid.len + pkt->scid.len
|
||||
+ ngx_quic_varint_len(rem_len) + pkt->num_len
|
||||
+ (pkt->level == ssl_encryption_initial ? 1 : 0);
|
||||
+ (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL ? 1 : 0);
|
||||
}
|
||||
|
||||
p = start = out;
|
||||
@@ -642,7 +647,7 @@ ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out,
|
||||
*p++ = pkt->scid.len;
|
||||
p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len);
|
||||
|
||||
if (pkt->level == ssl_encryption_initial) {
|
||||
if (pkt->level == NGX_QUIC_ENCRYPTION_INITIAL) {
|
||||
ngx_quic_build_int(&p, 0);
|
||||
}
|
||||
|
||||
@@ -1768,7 +1773,7 @@ ngx_quic_parse_transport_params(u_char *p, u_char *end, ngx_quic_tp_t *tp,
|
||||
}
|
||||
|
||||
if (rc == NGX_DECLINED) {
|
||||
ngx_log_error(NGX_LOG_INFO, log, 0,
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
|
||||
"quic %s transport param id:0x%xL, skipped",
|
||||
(id % 31 == 27) ? "reserved" : "unknown", id);
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@
|
||||
(ngx_quic_long_pkt(flags) ? 0x0F : 0x1F)
|
||||
|
||||
#define ngx_quic_level_name(lvl) \
|
||||
(lvl == ssl_encryption_application) ? "app" \
|
||||
: (lvl == ssl_encryption_initial) ? "init" \
|
||||
: (lvl == ssl_encryption_handshake) ? "hs" : "early"
|
||||
(lvl == NGX_QUIC_ENCRYPTION_APPLICATION) ? "app" \
|
||||
: (lvl == NGX_QUIC_ENCRYPTION_INITIAL) ? "init" \
|
||||
: (lvl == NGX_QUIC_ENCRYPTION_HANDSHAKE) ? "hs" : "early"
|
||||
|
||||
#define NGX_QUIC_MAX_CID_LEN 20
|
||||
#define NGX_QUIC_SERVER_CID_LEN NGX_QUIC_MAX_CID_LEN
|
||||
@@ -262,7 +262,7 @@ typedef struct ngx_quic_frame_s ngx_quic_frame_t;
|
||||
|
||||
struct ngx_quic_frame_s {
|
||||
ngx_uint_t type;
|
||||
enum ssl_encryption_level_t level;
|
||||
ngx_uint_t level;
|
||||
ngx_queue_t queue;
|
||||
uint64_t pnum;
|
||||
size_t plen;
|
||||
@@ -271,6 +271,7 @@ struct ngx_quic_frame_s {
|
||||
unsigned need_ack:1;
|
||||
unsigned pkt_need_ack:1;
|
||||
unsigned ignore_congestion:1;
|
||||
unsigned ignore_loss:1;
|
||||
|
||||
ngx_chain_t *data;
|
||||
union {
|
||||
@@ -309,7 +310,7 @@ typedef struct {
|
||||
uint8_t flags;
|
||||
uint32_t version;
|
||||
ngx_str_t token;
|
||||
enum ssl_encryption_level_t level;
|
||||
ngx_uint_t level;
|
||||
ngx_uint_t error;
|
||||
|
||||
/* filled in by parser */
|
||||
|
||||
@@ -245,7 +245,7 @@ ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -253,7 +253,8 @@ ngx_http_auth_basic_handler(ngx_http_request_t *r)
|
||||
pwd.len = i - passwd;
|
||||
pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);
|
||||
if (pwd.data == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);
|
||||
|
||||
@@ -1332,6 +1332,12 @@ ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||
table = ctx->table;
|
||||
|
||||
if (ctx->charset->utf8) {
|
||||
if (value[1].len / 2 > NGX_UTF_LEN - 1) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid value \"%V\"", &value[1]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
p = &table->src2dst[src * NGX_UTF_LEN];
|
||||
|
||||
*p++ = (u_char) (value[1].len / 2);
|
||||
@@ -1558,7 +1564,7 @@ ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_charset_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,9 @@ ngx_http_chunked_header_filter(ngx_http_request_t *r)
|
||||
|| r->headers_out.status == NGX_HTTP_NO_CONTENT
|
||||
|| r->headers_out.status < NGX_HTTP_OK
|
||||
|| r != r->main
|
||||
|| r->method == NGX_HTTP_HEAD)
|
||||
|| r->method == NGX_HTTP_HEAD
|
||||
|| (r->method == NGX_HTTP_CONNECT
|
||||
&& r->headers_out.status < NGX_HTTP_SPECIAL_RESPONSE))
|
||||
{
|
||||
return ngx_http_next_header_filter(r);
|
||||
}
|
||||
|
||||
@@ -2877,7 +2877,7 @@ ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)
|
||||
* conf->upstream.store_lengths = NULL;
|
||||
* conf->upstream.store_values = NULL;
|
||||
*
|
||||
* conf->index.len = { 0, NULL };
|
||||
* conf->index = { 0, NULL };
|
||||
*/
|
||||
|
||||
conf->upstream.store = NGX_CONF_UNSET;
|
||||
@@ -3130,7 +3130,7 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
|
||||
prev->upstream.temp_path,
|
||||
&ngx_http_fastcgi_temp_path)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
@@ -3781,6 +3781,11 @@ ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (value[1].len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty path");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
if (flcf->upstream.cache > 0) {
|
||||
return "is incompatible with \"fastcgi_cache\"";
|
||||
|
||||
@@ -63,6 +63,7 @@ typedef struct {
|
||||
unsigned allow_binary_include:1;
|
||||
unsigned binary_include:1;
|
||||
unsigned proxy_recursive:1;
|
||||
unsigned no_cacheable:1;
|
||||
} ngx_http_geo_conf_ctx_t;
|
||||
|
||||
|
||||
@@ -463,6 +464,7 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
+ sizeof(ngx_http_variable_value_t)
|
||||
+ 0x10000 * sizeof(ngx_http_geo_range_t *);
|
||||
ctx.allow_binary_include = 1;
|
||||
ctx.no_cacheable = 0;
|
||||
|
||||
save = *cf;
|
||||
cf->pool = pool;
|
||||
@@ -478,6 +480,10 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (ctx.no_cacheable) {
|
||||
var->flags |= NGX_HTTP_VAR_NOCACHEABLE;
|
||||
}
|
||||
|
||||
geo->proxies = ctx.proxies;
|
||||
geo->proxy_recursive = ctx.proxy_recursive;
|
||||
|
||||
@@ -623,6 +629,12 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
|
||||
rv = NGX_CONF_OK;
|
||||
goto done;
|
||||
}
|
||||
|
||||
else if (ngx_strcmp(value[0].data, "volatile") == 0) {
|
||||
ctx->no_cacheable = 1;
|
||||
rv = NGX_CONF_OK;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (cf->args->nelts != 2) {
|
||||
|
||||
@@ -205,6 +205,8 @@ static char *ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
static char *ngx_http_grpc_ssl_certificate_cache(ngx_conf_t *cf,
|
||||
ngx_command_t *cmd, void *conf);
|
||||
static char *ngx_http_grpc_ssl_password_file(ngx_conf_t *cf,
|
||||
ngx_command_t *cmd, void *conf);
|
||||
static char *ngx_http_grpc_ssl_conf_command_check(ngx_conf_t *cf, void *post,
|
||||
@@ -437,6 +439,13 @@ static ngx_command_t ngx_http_grpc_commands[] = {
|
||||
offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_certificate_key),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("grpc_ssl_certificate_cache"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
|
||||
ngx_http_grpc_ssl_certificate_cache,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("grpc_ssl_password_file"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_grpc_ssl_password_file,
|
||||
@@ -1860,7 +1869,8 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
if (status < NGX_HTTP_OK) {
|
||||
if (status < NGX_HTTP_OK && status != NGX_HTTP_EARLY_HINTS)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent unexpected :status \"%V\"",
|
||||
status_line);
|
||||
@@ -1893,6 +1903,10 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
|
||||
h->lowcase_key = h->key.data;
|
||||
h->hash = ngx_hash_key(h->key.data, h->key.len);
|
||||
|
||||
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
|
||||
h->lowcase_key, h->key.len);
|
||||
|
||||
@@ -1914,6 +1928,17 @@ ngx_http_grpc_process_header(ngx_http_request_t *r)
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"grpc header done");
|
||||
|
||||
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||
if (ctx->end_stream) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream prematurely closed stream");
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
ctx->status = 0;
|
||||
return NGX_HTTP_UPSTREAM_EARLY_HINTS;
|
||||
}
|
||||
|
||||
if (ctx->end_stream) {
|
||||
u->headers_in.content_length_n = 0;
|
||||
|
||||
@@ -4386,6 +4411,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
|
||||
conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
|
||||
conf->upstream.ssl_certificate = NGX_CONF_UNSET_PTR;
|
||||
conf->upstream.ssl_certificate_key = NGX_CONF_UNSET_PTR;
|
||||
conf->upstream.ssl_certificate_cache = NGX_CONF_UNSET_PTR;
|
||||
conf->upstream.ssl_passwords = NGX_CONF_UNSET_PTR;
|
||||
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
|
||||
#endif
|
||||
@@ -4403,6 +4429,7 @@ ngx_http_grpc_create_loc_conf(ngx_conf_t *cf)
|
||||
conf->upstream.pass_request_body = 1;
|
||||
conf->upstream.force_ranges = 0;
|
||||
conf->upstream.pass_trailers = 1;
|
||||
conf->upstream.pass_early_hints = 1;
|
||||
conf->upstream.preserve_output = 1;
|
||||
|
||||
conf->headers_source = NGX_CONF_UNSET_PTR;
|
||||
@@ -4476,9 +4503,7 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
prev->upstream.ssl_session_reuse, 1);
|
||||
|
||||
ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
|
||||
(NGX_CONF_BITMASK_SET
|
||||
|NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
|
||||
|NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3));
|
||||
(NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS));
|
||||
|
||||
ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,
|
||||
"DEFAULT");
|
||||
@@ -4499,8 +4524,15 @@ ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
prev->upstream.ssl_certificate, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_key,
|
||||
prev->upstream.ssl_certificate_key, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,
|
||||
prev->upstream.ssl_passwords, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_cache,
|
||||
prev->upstream.ssl_certificate_cache, NULL);
|
||||
|
||||
if (ngx_http_upstream_merge_ssl_passwords(cf, &conf->upstream,
|
||||
&prev->upstream)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
|
||||
prev->ssl_conf_commands, NULL);
|
||||
@@ -4849,6 +4881,100 @@ ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
|
||||
static char *
|
||||
ngx_http_grpc_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf)
|
||||
{
|
||||
ngx_http_grpc_loc_conf_t *plcf = conf;
|
||||
|
||||
time_t inactive, valid;
|
||||
ngx_str_t *value, s;
|
||||
ngx_int_t max;
|
||||
ngx_uint_t i;
|
||||
|
||||
if (plcf->upstream.ssl_certificate_cache != NGX_CONF_UNSET_PTR) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
max = 0;
|
||||
inactive = 10;
|
||||
valid = 60;
|
||||
|
||||
for (i = 1; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
|
||||
|
||||
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
|
||||
if (max <= 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
|
||||
|
||||
s.len = value[i].len - 9;
|
||||
s.data = value[i].data + 9;
|
||||
|
||||
inactive = ngx_parse_time(&s, 1);
|
||||
if (inactive == (time_t) NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
|
||||
|
||||
s.len = value[i].len - 6;
|
||||
s.data = value[i].data + 6;
|
||||
|
||||
valid = ngx_parse_time(&s, 1);
|
||||
if (valid == (time_t) NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "off") == 0) {
|
||||
|
||||
plcf->upstream.ssl_certificate_cache = NULL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
failed:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (plcf->upstream.ssl_certificate_cache == NULL) {
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (max == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"\"grpc_ssl_certificate_cache\" must have "
|
||||
"the \"max\" parameter");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
plcf->upstream.ssl_certificate_cache = ngx_ssl_cache_init(cf->pool, max,
|
||||
valid, inactive);
|
||||
if (plcf->upstream.ssl_certificate_cache == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_grpc_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
@@ -4973,16 +5099,9 @@ ngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (glcf->upstream.ssl_certificate->lengths
|
||||
|| glcf->upstream.ssl_certificate_key->lengths)
|
||||
if (glcf->upstream.ssl_certificate->lengths == NULL
|
||||
&& glcf->upstream.ssl_certificate_key->lengths == NULL)
|
||||
{
|
||||
glcf->upstream.ssl_passwords =
|
||||
ngx_ssl_preserve_passwords(cf, glcf->upstream.ssl_passwords);
|
||||
if (glcf->upstream.ssl_passwords == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (ngx_ssl_certificate(cf, glcf->upstream.ssl,
|
||||
&glcf->upstream.ssl_certificate->value,
|
||||
&glcf->upstream.ssl_certificate_key->value,
|
||||
|
||||
@@ -304,7 +304,7 @@ ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,
|
||||
{
|
||||
int rc;
|
||||
|
||||
ctx->zstream.next_in = Z_NULL;
|
||||
ctx->zstream.next_in = NULL;
|
||||
ctx->zstream.avail_in = 0;
|
||||
|
||||
ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;
|
||||
|
||||
@@ -516,8 +516,10 @@ ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
|
||||
/*
|
||||
* Another zlib variant, https://github.com/zlib-ng/zlib-ng.
|
||||
* It used to force window bits to 13 for fast compression level,
|
||||
* uses (64 + sizeof(void*)) additional space on all allocations
|
||||
* for alignment, 16-byte padding in one of window-sized buffers,
|
||||
* used (64 + sizeof(void*)) additional space on all allocations
|
||||
* for alignment and 16-byte padding in one of window-sized buffers,
|
||||
* uses a single allocation with up to 200 bytes for alignment and
|
||||
* internal pointers, 5/4 times more memory for the pending buffer,
|
||||
* and 128K hash.
|
||||
*/
|
||||
|
||||
@@ -526,7 +528,7 @@ ngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
|
||||
}
|
||||
|
||||
ctx->allocated = 8192 + 16 + (1 << (wbits + 2))
|
||||
+ 131072 + (1 << (memlevel + 8))
|
||||
+ 131072 + (5 << (memlevel + 6))
|
||||
+ 4 * (64 + sizeof(void*));
|
||||
ctx->zlib_ng = 1;
|
||||
}
|
||||
@@ -1113,7 +1115,7 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_HEADERS_INHERIT_OFF 0
|
||||
#define NGX_HTTP_HEADERS_INHERIT_ON 1
|
||||
#define NGX_HTTP_HEADERS_INHERIT_MERGE 2
|
||||
|
||||
|
||||
typedef struct ngx_http_header_val_s ngx_http_header_val_t;
|
||||
|
||||
typedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,
|
||||
@@ -49,6 +54,8 @@ typedef struct {
|
||||
ngx_http_complex_value_t *expires_value;
|
||||
ngx_array_t *headers;
|
||||
ngx_array_t *trailers;
|
||||
ngx_uint_t headers_inherit;
|
||||
ngx_uint_t trailers_inherit;
|
||||
} ngx_http_headers_conf_t;
|
||||
|
||||
|
||||
@@ -97,6 +104,14 @@ static ngx_http_set_header_t ngx_http_set_headers[] = {
|
||||
};
|
||||
|
||||
|
||||
static ngx_conf_enum_t ngx_http_headers_inherit[] = {
|
||||
{ ngx_string("off"), NGX_HTTP_HEADERS_INHERIT_OFF },
|
||||
{ ngx_string("on"), NGX_HTTP_HEADERS_INHERIT_ON },
|
||||
{ ngx_string("merge"), NGX_HTTP_HEADERS_INHERIT_MERGE },
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
|
||||
static ngx_command_t ngx_http_headers_filter_commands[] = {
|
||||
|
||||
{ ngx_string("expires"),
|
||||
@@ -123,6 +138,22 @@ static ngx_command_t ngx_http_headers_filter_commands[] = {
|
||||
offsetof(ngx_http_headers_conf_t, trailers),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("add_header_inherit"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|
||||
|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_headers_conf_t, headers_inherit),
|
||||
&ngx_http_headers_inherit },
|
||||
|
||||
{ ngx_string("add_trailer_inherit"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|
||||
|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_headers_conf_t, trailers_inherit),
|
||||
&ngx_http_headers_inherit },
|
||||
|
||||
ngx_null_command
|
||||
};
|
||||
|
||||
@@ -657,6 +688,8 @@ ngx_http_headers_create_conf(ngx_conf_t *cf)
|
||||
*/
|
||||
|
||||
conf->expires = NGX_HTTP_EXPIRES_UNSET;
|
||||
conf->headers_inherit = NGX_CONF_UNSET_UINT;
|
||||
conf->trailers_inherit = NGX_CONF_UNSET_UINT;
|
||||
|
||||
return conf;
|
||||
}
|
||||
@@ -668,6 +701,8 @@ ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
ngx_http_headers_conf_t *prev = parent;
|
||||
ngx_http_headers_conf_t *conf = child;
|
||||
|
||||
ngx_http_header_val_t *hv;
|
||||
|
||||
if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {
|
||||
conf->expires = prev->expires;
|
||||
conf->expires_time = prev->expires_time;
|
||||
@@ -678,12 +713,43 @@ ngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
}
|
||||
}
|
||||
|
||||
if (conf->headers == NULL) {
|
||||
conf->headers = prev->headers;
|
||||
ngx_conf_merge_uint_value(conf->headers_inherit, prev->headers_inherit,
|
||||
NGX_HTTP_HEADERS_INHERIT_ON);
|
||||
ngx_conf_merge_uint_value(conf->trailers_inherit, prev->trailers_inherit,
|
||||
NGX_HTTP_HEADERS_INHERIT_ON);
|
||||
|
||||
if (conf->headers_inherit != NGX_HTTP_HEADERS_INHERIT_OFF
|
||||
&& prev->headers)
|
||||
{
|
||||
if (conf->headers == NULL) {
|
||||
conf->headers = prev->headers;
|
||||
|
||||
} else if (conf->headers_inherit == NGX_HTTP_HEADERS_INHERIT_MERGE) {
|
||||
hv = ngx_array_push_n(conf->headers, prev->headers->nelts);
|
||||
if (hv == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(hv, prev->headers->elts,
|
||||
sizeof(ngx_http_header_val_t) * prev->headers->nelts);
|
||||
}
|
||||
}
|
||||
|
||||
if (conf->trailers == NULL) {
|
||||
conf->trailers = prev->trailers;
|
||||
if (conf->trailers_inherit != NGX_HTTP_HEADERS_INHERIT_OFF
|
||||
&& prev->trailers)
|
||||
{
|
||||
if (conf->trailers == NULL) {
|
||||
conf->trailers = prev->trailers;
|
||||
|
||||
} else if (conf->trailers_inherit == NGX_HTTP_HEADERS_INHERIT_MERGE) {
|
||||
hv = ngx_array_push_n(conf->trailers, prev->trailers->nelts);
|
||||
if (hv == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(hv, prev->trailers->elts,
|
||||
sizeof(ngx_http_header_val_t) * prev->trailers->nelts);
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
|
||||
@@ -490,7 +490,7 @@ ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
if (value[i].len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"index \"%V\" in \"index\" directive is invalid",
|
||||
&value[1]);
|
||||
&value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@@ -3176,7 +3176,10 @@ ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
|
||||
|
||||
start_sample -= n;
|
||||
|
||||
prev_samples = samples;
|
||||
if (next_chunk > chunk) {
|
||||
prev_samples = samples;
|
||||
}
|
||||
|
||||
chunk = next_chunk;
|
||||
samples = ngx_mp4_get_32value(entry->samples);
|
||||
id = ngx_mp4_get_32value(entry->id);
|
||||
@@ -3186,6 +3189,13 @@ ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,
|
||||
|
||||
next_chunk = trak->chunks + 1;
|
||||
|
||||
if (next_chunk < chunk) {
|
||||
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
|
||||
"unordered mp4 stsc chunks in \"%s\"",
|
||||
mp4->file.name.data);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,
|
||||
"sample:%uD, chunk:%uD, chunks:%uD, samples:%uD",
|
||||
start_sample, chunk, next_chunk - chunk, samples);
|
||||
@@ -3211,6 +3221,12 @@ found:
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (chunk == 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,
|
||||
"zero chunk in \"%s\"", mp4->file.name.data);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
target_chunk = chunk - 1;
|
||||
target_chunk += start_sample / samples;
|
||||
chunk_samples = start_sample % samples;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_http_proxy_module.h>
|
||||
|
||||
|
||||
#define NGX_HTTP_PROXY_COOKIE_SECURE 0x0001
|
||||
@@ -23,11 +24,6 @@
|
||||
#define NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF 0x0400
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t caches; /* ngx_http_file_cache_t * */
|
||||
} ngx_http_proxy_main_conf_t;
|
||||
|
||||
|
||||
typedef struct ngx_http_proxy_rewrite_s ngx_http_proxy_rewrite_t;
|
||||
|
||||
typedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r,
|
||||
@@ -61,94 +57,6 @@ typedef struct {
|
||||
} ngx_http_proxy_cookie_flags_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t key_start;
|
||||
ngx_str_t schema;
|
||||
ngx_str_t host_header;
|
||||
ngx_str_t port;
|
||||
ngx_str_t uri;
|
||||
} ngx_http_proxy_vars_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *flushes;
|
||||
ngx_array_t *lengths;
|
||||
ngx_array_t *values;
|
||||
ngx_hash_t hash;
|
||||
} ngx_http_proxy_headers_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_upstream_conf_t upstream;
|
||||
|
||||
ngx_array_t *body_flushes;
|
||||
ngx_array_t *body_lengths;
|
||||
ngx_array_t *body_values;
|
||||
ngx_str_t body_source;
|
||||
|
||||
ngx_http_proxy_headers_t headers;
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_http_proxy_headers_t headers_cache;
|
||||
#endif
|
||||
ngx_array_t *headers_source;
|
||||
|
||||
ngx_array_t *proxy_lengths;
|
||||
ngx_array_t *proxy_values;
|
||||
|
||||
ngx_array_t *redirects;
|
||||
ngx_array_t *cookie_domains;
|
||||
ngx_array_t *cookie_paths;
|
||||
ngx_array_t *cookie_flags;
|
||||
|
||||
ngx_http_complex_value_t *method;
|
||||
ngx_str_t location;
|
||||
ngx_str_t url;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_http_complex_value_t cache_key;
|
||||
#endif
|
||||
|
||||
ngx_http_proxy_vars_t vars;
|
||||
|
||||
ngx_flag_t redirect;
|
||||
|
||||
ngx_uint_t http_version;
|
||||
|
||||
ngx_uint_t headers_hash_max_size;
|
||||
ngx_uint_t headers_hash_bucket_size;
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
ngx_uint_t ssl;
|
||||
ngx_uint_t ssl_protocols;
|
||||
ngx_str_t ssl_ciphers;
|
||||
ngx_uint_t ssl_verify_depth;
|
||||
ngx_str_t ssl_trusted_certificate;
|
||||
ngx_str_t ssl_crl;
|
||||
ngx_array_t *ssl_conf_commands;
|
||||
#endif
|
||||
} ngx_http_proxy_loc_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_status_t status;
|
||||
ngx_http_chunked_t chunked;
|
||||
ngx_http_proxy_vars_t vars;
|
||||
off_t internal_body_length;
|
||||
|
||||
ngx_chain_t *free;
|
||||
ngx_chain_t *busy;
|
||||
|
||||
unsigned head:1;
|
||||
unsigned internal_chunked:1;
|
||||
unsigned header_sent:1;
|
||||
} ngx_http_proxy_ctx_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r,
|
||||
ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf);
|
||||
#if (NGX_HTTP_CACHE)
|
||||
static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);
|
||||
#endif
|
||||
static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in);
|
||||
@@ -163,6 +71,8 @@ static ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data,
|
||||
ssize_t bytes);
|
||||
static ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data,
|
||||
ssize_t bytes);
|
||||
static ngx_int_t ngx_http_proxy_process_trailer(ngx_http_request_t *r,
|
||||
ngx_buf_t *buf);
|
||||
static void ngx_http_proxy_abort_request(ngx_http_request_t *r);
|
||||
static void ngx_http_proxy_finalize_request(ngx_http_request_t *r,
|
||||
ngx_int_t rc);
|
||||
@@ -174,15 +84,14 @@ static ngx_int_t ngx_http_proxy_port_variable(ngx_http_request_t *r,
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_internal_connection_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data);
|
||||
static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
|
||||
ngx_table_elt_t *h, size_t prefix);
|
||||
static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
|
||||
ngx_table_elt_t *h);
|
||||
static ngx_int_t ngx_http_proxy_parse_cookie(ngx_str_t *value,
|
||||
ngx_array_t *attrs);
|
||||
static ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r,
|
||||
@@ -222,6 +131,8 @@ static char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
#endif
|
||||
#if (NGX_HTTP_SSL)
|
||||
static char *ngx_http_proxy_ssl_certificate_cache(ngx_conf_t *cf,
|
||||
ngx_command_t *cmd, void *conf);
|
||||
static char *ngx_http_proxy_ssl_password_file(ngx_conf_t *cf,
|
||||
ngx_command_t *cmd, void *conf);
|
||||
#endif
|
||||
@@ -287,6 +198,9 @@ static ngx_conf_post_t ngx_http_proxy_ssl_conf_command_post =
|
||||
static ngx_conf_enum_t ngx_http_proxy_http_version[] = {
|
||||
{ ngx_string("1.0"), NGX_HTTP_VERSION_10 },
|
||||
{ ngx_string("1.1"), NGX_HTTP_VERSION_11 },
|
||||
#if (NGX_HTTP_V2)
|
||||
{ ngx_string("2"), NGX_HTTP_VERSION_20 },
|
||||
#endif
|
||||
{ ngx_null_string, 0 }
|
||||
};
|
||||
|
||||
@@ -457,6 +371,13 @@ static ngx_command_t ngx_http_proxy_commands[] = {
|
||||
offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_body),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("proxy_pass_trailers"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_trailers),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("proxy_buffer_size"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_size_slot,
|
||||
@@ -764,6 +685,13 @@ static ngx_command_t ngx_http_proxy_commands[] = {
|
||||
offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_certificate_key),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("proxy_ssl_certificate_cache"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,
|
||||
ngx_http_proxy_ssl_certificate_cache,
|
||||
NGX_HTTP_LOC_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("proxy_ssl_password_file"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_proxy_ssl_password_file,
|
||||
@@ -820,8 +748,8 @@ static char ngx_http_proxy_version_11[] = " HTTP/1.1" CRLF;
|
||||
|
||||
|
||||
static ngx_keyval_t ngx_http_proxy_headers[] = {
|
||||
{ ngx_string("Host"), ngx_string("$proxy_host") },
|
||||
{ ngx_string("Connection"), ngx_string("close") },
|
||||
{ ngx_string("Host"), ngx_string("$proxy_internal_host") },
|
||||
{ ngx_string("Connection"), ngx_string("$proxy_internal_connection") },
|
||||
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
|
||||
{ ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
|
||||
{ ngx_string("TE"), ngx_string("") },
|
||||
@@ -848,8 +776,8 @@ static ngx_str_t ngx_http_proxy_hide_headers[] = {
|
||||
#if (NGX_HTTP_CACHE)
|
||||
|
||||
static ngx_keyval_t ngx_http_proxy_cache_headers[] = {
|
||||
{ ngx_string("Host"), ngx_string("$proxy_host") },
|
||||
{ ngx_string("Connection"), ngx_string("close") },
|
||||
{ ngx_string("Host"), ngx_string("$proxy_internal_host") },
|
||||
{ ngx_string("Connection"), ngx_string("$proxy_internal_connection") },
|
||||
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
|
||||
{ ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
|
||||
{ ngx_string("TE"), ngx_string("") },
|
||||
@@ -884,6 +812,14 @@ static ngx_http_variable_t ngx_http_proxy_vars[] = {
|
||||
{ ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 },
|
||||
#endif
|
||||
|
||||
{ ngx_string("proxy_internal_host"), NULL,
|
||||
ngx_http_proxy_host_variable, 1,
|
||||
NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
|
||||
|
||||
{ ngx_string("proxy_internal_connection"), NULL,
|
||||
ngx_http_proxy_internal_connection_variable, 0,
|
||||
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
|
||||
|
||||
{ ngx_string("proxy_internal_body_length"), NULL,
|
||||
ngx_http_proxy_internal_body_length_variable, 0,
|
||||
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
|
||||
@@ -942,6 +878,14 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
|
||||
ngx_http_proxy_main_conf_t *pmcf;
|
||||
#endif
|
||||
|
||||
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
|
||||
|
||||
#if (NGX_HTTP_V2)
|
||||
if (plcf->http_version == NGX_HTTP_VERSION_20) {
|
||||
return ngx_http_proxy_v2_handler(r);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ngx_http_upstream_create(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
@@ -951,9 +895,9 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);
|
||||
ctx->legacy = 1;
|
||||
|
||||
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);
|
||||
|
||||
u = r->upstream;
|
||||
|
||||
@@ -1030,7 +974,7 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_int_t
|
||||
ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
|
||||
ngx_http_proxy_loc_conf_t *plcf)
|
||||
{
|
||||
@@ -1134,7 +1078,7 @@ ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_int_t
|
||||
ngx_http_proxy_create_key(ngx_http_request_t *r)
|
||||
{
|
||||
size_t len, loc_len;
|
||||
@@ -1186,7 +1130,8 @@ ngx_http_proxy_create_key(ngx_http_request_t *r)
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
loc_len = (r->valid_location && ctx->vars.uri.len) ? plcf->location.len : 0;
|
||||
loc_len = (r->valid_location && ctx->vars.uri.len)
|
||||
? ngx_min(plcf->location.len, r->uri.len) : 0;
|
||||
|
||||
if (r->quoted_uri || r->internal) {
|
||||
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
|
||||
@@ -1298,8 +1243,8 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
|
||||
uri_len = r->unparsed_uri.len;
|
||||
|
||||
} else {
|
||||
loc_len = (r->valid_location && ctx->vars.uri.len) ?
|
||||
plcf->location.len : 0;
|
||||
loc_len = (r->valid_location && ctx->vars.uri.len)
|
||||
? ngx_min(plcf->location.len, r->uri.len) : 0;
|
||||
|
||||
if (r->quoted_uri || r->internal) {
|
||||
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
|
||||
@@ -1868,6 +1813,13 @@ ngx_http_proxy_process_status_line(ngx_http_request_t *r)
|
||||
u->headers_in.status_n, &u->headers_in.status_line);
|
||||
|
||||
if (ctx->status.http_version < NGX_HTTP_VERSION_11) {
|
||||
|
||||
if (ctx->status.code == NGX_HTTP_EARLY_HINTS) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent HTTP/1.0 response with early hints");
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
u->headers_in.connection_close = 1;
|
||||
}
|
||||
|
||||
@@ -1929,6 +1881,14 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
||||
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \"%V: %V\"",
|
||||
&h->key, &h->value);
|
||||
|
||||
if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
|
||||
h->lowcase_key, h->key.len);
|
||||
|
||||
@@ -1940,10 +1900,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
||||
}
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \"%V: %V\"",
|
||||
&h->key, &h->value);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1954,6 +1910,20 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header done");
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
|
||||
|
||||
if (r->upstream->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||
ctx->status.code = 0;
|
||||
ctx->status.count = 0;
|
||||
ctx->status.start = NULL;
|
||||
ctx->status.end = NULL;
|
||||
|
||||
r->upstream->process_header =
|
||||
ngx_http_proxy_process_status_line;
|
||||
r->state = 0;
|
||||
return NGX_HTTP_UPSTREAM_EARLY_HINTS;
|
||||
}
|
||||
|
||||
/*
|
||||
* if no "Server" and "Date" in header line,
|
||||
* then add the special empty headers
|
||||
@@ -2001,8 +1971,6 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
|
||||
* connections alive in case of r->header_only or X-Accel-Redirect
|
||||
*/
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
|
||||
|
||||
if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
|
||||
|| u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
|
||||
|| ctx->head
|
||||
@@ -2075,7 +2043,7 @@ ngx_http_proxy_input_filter_init(void *data)
|
||||
/* chunked */
|
||||
|
||||
u->pipe->input_filter = ngx_http_proxy_chunked_filter;
|
||||
u->pipe->length = 3; /* "0" LF LF */
|
||||
u->pipe->length = 5; /* "0" CRLF CRLF */
|
||||
|
||||
u->input_filter = ngx_http_proxy_non_buffered_chunked_filter;
|
||||
u->length = 1;
|
||||
@@ -2181,11 +2149,12 @@ ngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b, **prev;
|
||||
ngx_chain_t *cl;
|
||||
ngx_http_request_t *r;
|
||||
ngx_http_proxy_ctx_t *ctx;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b, **prev;
|
||||
ngx_chain_t *cl;
|
||||
ngx_http_request_t *r;
|
||||
ngx_http_proxy_ctx_t *ctx;
|
||||
ngx_http_proxy_loc_conf_t *plcf;
|
||||
|
||||
if (buf->pos == buf->last) {
|
||||
return NGX_OK;
|
||||
@@ -2216,11 +2185,39 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
}
|
||||
|
||||
b = NULL;
|
||||
|
||||
if (ctx->trailers) {
|
||||
rc = ngx_http_proxy_process_trailer(r, buf);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
/* a whole response has been parsed successfully */
|
||||
|
||||
p->length = 0;
|
||||
r->upstream->keepalive = !r->upstream->headers_in.connection_close;
|
||||
|
||||
if (buf->pos != buf->last) {
|
||||
ngx_log_error(NGX_LOG_WARN, p->log, 0,
|
||||
"upstream sent data after trailers");
|
||||
r->upstream->keepalive = 0;
|
||||
}
|
||||
}
|
||||
|
||||
goto free_buf;
|
||||
}
|
||||
|
||||
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
|
||||
|
||||
prev = &buf->shadow;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);
|
||||
rc = ngx_http_parse_chunked(r, buf, &ctx->chunked,
|
||||
plcf->upstream.pass_trailers);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
@@ -2275,6 +2272,19 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
|
||||
if (plcf->upstream.pass_trailers) {
|
||||
rc = ngx_http_proxy_process_trailer(r, buf);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
p->length = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* a whole response has been parsed successfully */
|
||||
|
||||
p->length = 0;
|
||||
@@ -2306,6 +2316,8 @@ ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
free_buf:
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, p->log, 0,
|
||||
"http proxy chunked state %ui, length %O",
|
||||
ctx->chunked.state, p->length);
|
||||
@@ -2401,11 +2413,14 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
|
||||
{
|
||||
ngx_http_request_t *r = data;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b, *buf;
|
||||
ngx_chain_t *cl, **ll;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_proxy_ctx_t *ctx;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b, *buf;
|
||||
ngx_chain_t *cl, **ll;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_proxy_ctx_t *ctx;
|
||||
ngx_http_proxy_loc_conf_t *plcf;
|
||||
|
||||
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
|
||||
|
||||
@@ -2419,13 +2434,38 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
|
||||
buf->pos = buf->last;
|
||||
buf->last += bytes;
|
||||
|
||||
if (ctx->trailers) {
|
||||
rc = ngx_http_proxy_process_trailer(r, buf);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
/* a whole response has been parsed successfully */
|
||||
|
||||
r->upstream->keepalive = !u->headers_in.connection_close;
|
||||
u->length = 0;
|
||||
|
||||
if (buf->pos != buf->last) {
|
||||
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
|
||||
"upstream sent data after trailers");
|
||||
u->keepalive = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);
|
||||
rc = ngx_http_parse_chunked(r, buf, &ctx->chunked,
|
||||
plcf->upstream.pass_trailers);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
@@ -2467,6 +2507,19 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
|
||||
if (plcf->upstream.pass_trailers) {
|
||||
rc = ngx_http_proxy_process_trailer(r, buf);
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
u->length = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* a whole response has been parsed successfully */
|
||||
|
||||
u->keepalive = !u->headers_in.connection_close;
|
||||
@@ -2497,6 +2550,115 @@ ngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_process_trailer(ngx_http_request_t *r, ngx_buf_t *buf)
|
||||
{
|
||||
size_t len;
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b;
|
||||
ngx_table_elt_t *h;
|
||||
ngx_http_proxy_ctx_t *ctx;
|
||||
ngx_http_proxy_loc_conf_t *plcf;
|
||||
|
||||
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
|
||||
|
||||
if (ctx->trailers == NULL) {
|
||||
ctx->trailers = ngx_create_temp_buf(r->pool,
|
||||
plcf->upstream.buffer_size);
|
||||
if (ctx->trailers == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
b = ctx->trailers;
|
||||
len = ngx_min(buf->last - buf->pos, b->end - b->last);
|
||||
|
||||
b->last = ngx_cpymem(b->last, buf->pos, len);
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
rc = ngx_http_parse_header_line(r, b, 1);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
/* a header line has been parsed successfully */
|
||||
|
||||
h = ngx_list_push(&r->upstream->headers_in.trailers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
h->hash = r->header_hash;
|
||||
|
||||
h->key.len = r->header_name_end - r->header_name_start;
|
||||
h->value.len = r->header_end - r->header_start;
|
||||
|
||||
h->key.data = ngx_pnalloc(r->pool,
|
||||
h->key.len + 1 + h->value.len + 1 + h->key.len);
|
||||
if (h->key.data == NULL) {
|
||||
h->hash = 0;
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
h->value.data = h->key.data + h->key.len + 1;
|
||||
h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
|
||||
|
||||
ngx_memcpy(h->key.data, r->header_name_start, h->key.len);
|
||||
h->key.data[h->key.len] = '\0';
|
||||
ngx_memcpy(h->value.data, r->header_start, h->value.len);
|
||||
h->value.data[h->value.len] = '\0';
|
||||
|
||||
if (h->key.len == r->lowcase_index) {
|
||||
ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
|
||||
|
||||
} else {
|
||||
ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy trailer: \"%V: %V\"",
|
||||
&h->key, &h->value);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
|
||||
|
||||
/* a whole header has been parsed successfully */
|
||||
|
||||
buf->pos += len - (b->last - b->pos);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy trailer done");
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
buf->pos += len;
|
||||
|
||||
if (b->last == b->end) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent too big trailers");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
/* rc == NGX_HTTP_PARSE_INVALID_HEADER */
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid trailer: \"%*s\\x%02xd...\"",
|
||||
r->header_end - r->header_name_start,
|
||||
r->header_name_start, *r->header_end);
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_proxy_abort_request(ngx_http_request_t *r)
|
||||
{
|
||||
@@ -2530,6 +2692,11 @@ ngx_http_proxy_host_variable(ngx_http_request_t *r,
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (data == 1 && !ctx->legacy) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->len = ctx->vars.host_header.len;
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
@@ -2610,6 +2777,29 @@ ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_internal_connection_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
{
|
||||
ngx_http_proxy_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
|
||||
|
||||
if (ctx == NULL || !ctx->legacy) {
|
||||
v->not_found = 1;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
v->valid = 1;
|
||||
v->no_cacheable = 0;
|
||||
v->not_found = 0;
|
||||
|
||||
ngx_str_set(v, "close");
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
|
||||
ngx_http_variable_value_t *v, uintptr_t data)
|
||||
@@ -2663,7 +2853,7 @@ ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_int_t
|
||||
ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
|
||||
size_t prefix)
|
||||
{
|
||||
@@ -2695,7 +2885,7 @@ ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_int_t
|
||||
ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)
|
||||
{
|
||||
u_char *p;
|
||||
@@ -3340,6 +3530,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
|
||||
* conf->headers.values = NULL;
|
||||
* conf->headers.hash = { NULL, 0 };
|
||||
* conf->headers_cache.lengths = NULL;
|
||||
* conf->host_set = 0;
|
||||
* conf->headers_cache.values = NULL;
|
||||
* conf->headers_cache.hash = { NULL, 0 };
|
||||
* conf->body_lengths = NULL;
|
||||
@@ -3379,6 +3570,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
|
||||
|
||||
conf->upstream.pass_request_headers = NGX_CONF_UNSET;
|
||||
conf->upstream.pass_request_body = NGX_CONF_UNSET;
|
||||
conf->upstream.pass_trailers = NGX_CONF_UNSET;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
conf->upstream.cache = NGX_CONF_UNSET;
|
||||
@@ -3407,15 +3599,16 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
|
||||
conf->upstream.ssl_verify = NGX_CONF_UNSET;
|
||||
conf->upstream.ssl_certificate = NGX_CONF_UNSET_PTR;
|
||||
conf->upstream.ssl_certificate_key = NGX_CONF_UNSET_PTR;
|
||||
conf->upstream.ssl_certificate_cache = NGX_CONF_UNSET_PTR;
|
||||
conf->upstream.ssl_passwords = NGX_CONF_UNSET_PTR;
|
||||
conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;
|
||||
conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;
|
||||
#endif
|
||||
|
||||
/* "proxy_cyclic_temp_file" is disabled */
|
||||
/* the hardcoded values */
|
||||
conf->upstream.cyclic_temp_file = 0;
|
||||
|
||||
conf->upstream.change_buffering = 1;
|
||||
conf->upstream.pass_early_hints = 1;
|
||||
|
||||
conf->headers_source = NGX_CONF_UNSET_PTR;
|
||||
|
||||
@@ -3628,7 +3821,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
|
||||
prev->upstream.temp_path,
|
||||
&ngx_http_proxy_temp_path)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
@@ -3721,6 +3914,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
ngx_conf_merge_value(conf->upstream.pass_request_body,
|
||||
prev->upstream.pass_request_body, 1);
|
||||
|
||||
ngx_conf_merge_value(conf->upstream.pass_trailers,
|
||||
prev->upstream.pass_trailers, 0);
|
||||
|
||||
ngx_conf_merge_value(conf->upstream.intercept_errors,
|
||||
prev->upstream.intercept_errors, 0);
|
||||
|
||||
@@ -3734,9 +3930,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
prev->upstream.ssl_session_reuse, 1);
|
||||
|
||||
ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,
|
||||
(NGX_CONF_BITMASK_SET
|
||||
|NGX_SSL_TLSv1|NGX_SSL_TLSv1_1
|
||||
|NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3));
|
||||
(NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS));
|
||||
|
||||
ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,
|
||||
"DEFAULT");
|
||||
@@ -3757,8 +3951,15 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
prev->upstream.ssl_certificate, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_key,
|
||||
prev->upstream.ssl_certificate_key, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,
|
||||
prev->upstream.ssl_passwords, NULL);
|
||||
ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_cache,
|
||||
prev->upstream.ssl_certificate_cache, NULL);
|
||||
|
||||
if (ngx_http_upstream_merge_ssl_passwords(cf, &conf->upstream,
|
||||
&prev->upstream)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->ssl_conf_commands,
|
||||
prev->ssl_conf_commands, NULL);
|
||||
@@ -3905,6 +4106,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
#if (NGX_HTTP_CACHE)
|
||||
conf->headers_cache = prev->headers_cache;
|
||||
#endif
|
||||
conf->host_set = prev->host_set;
|
||||
}
|
||||
|
||||
rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers,
|
||||
@@ -3937,6 +4139,7 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
#if (NGX_HTTP_CACHE)
|
||||
prev->headers_cache = conf->headers_cache;
|
||||
#endif
|
||||
prev->host_set = conf->host_set;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
@@ -3989,6 +4192,12 @@ ngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,
|
||||
src = conf->headers_source->elts;
|
||||
for (i = 0; i < conf->headers_source->nelts; i++) {
|
||||
|
||||
if (src[i].key.len == 4
|
||||
&& ngx_strncasecmp(src[i].key.data, (u_char *) "Host", 4) == 0)
|
||||
{
|
||||
conf->host_set = 1;
|
||||
}
|
||||
|
||||
s = ngx_array_push(&headers_merged);
|
||||
if (s == NULL) {
|
||||
return NGX_ERROR;
|
||||
@@ -4736,6 +4945,11 @@ ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (value[1].len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty path");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
if (plcf->upstream.cache > 0) {
|
||||
return "is incompatible with \"proxy_cache\"";
|
||||
@@ -4862,6 +5076,100 @@ ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
|
||||
static char *
|
||||
ngx_http_proxy_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf)
|
||||
{
|
||||
ngx_http_proxy_loc_conf_t *plcf = conf;
|
||||
|
||||
time_t inactive, valid;
|
||||
ngx_str_t *value, s;
|
||||
ngx_int_t max;
|
||||
ngx_uint_t i;
|
||||
|
||||
if (plcf->upstream.ssl_certificate_cache != NGX_CONF_UNSET_PTR) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
max = 0;
|
||||
inactive = 10;
|
||||
valid = 60;
|
||||
|
||||
for (i = 1; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
|
||||
|
||||
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
|
||||
if (max <= 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
|
||||
|
||||
s.len = value[i].len - 9;
|
||||
s.data = value[i].data + 9;
|
||||
|
||||
inactive = ngx_parse_time(&s, 1);
|
||||
if (inactive == (time_t) NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
|
||||
|
||||
s.len = value[i].len - 6;
|
||||
s.data = value[i].data + 6;
|
||||
|
||||
valid = ngx_parse_time(&s, 1);
|
||||
if (valid == (time_t) NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "off") == 0) {
|
||||
|
||||
plcf->upstream.ssl_certificate_cache = NULL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
failed:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (plcf->upstream.ssl_certificate_cache == NULL) {
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (max == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"\"proxy_ssl_certificate_cache\" must have "
|
||||
"the \"max\" parameter");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
plcf->upstream.ssl_certificate_cache = ngx_ssl_cache_init(cf->pool, max,
|
||||
valid, inactive);
|
||||
if (plcf->upstream.ssl_certificate_cache == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
@@ -5019,16 +5327,9 @@ ngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (plcf->upstream.ssl_certificate->lengths
|
||||
|| plcf->upstream.ssl_certificate_key->lengths)
|
||||
if (plcf->upstream.ssl_certificate->lengths == NULL
|
||||
&& plcf->upstream.ssl_certificate_key->lengths == NULL)
|
||||
{
|
||||
plcf->upstream.ssl_passwords =
|
||||
ngx_ssl_preserve_passwords(cf, plcf->upstream.ssl_passwords);
|
||||
if (plcf->upstream.ssl_passwords == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (ngx_ssl_certificate(cf, plcf->upstream.ssl,
|
||||
&plcf->upstream.ssl_certificate->value,
|
||||
&plcf->upstream.ssl_certificate_key->value,
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Igor Sysoev
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _NGX_HTTP_PROXY_H_INCLUDED_
|
||||
#define _NGX_HTTP_PROXY_H_INCLUDED_
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t caches; /* ngx_http_file_cache_t * */
|
||||
} ngx_http_proxy_main_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_str_t key_start;
|
||||
ngx_str_t schema;
|
||||
ngx_str_t host_header;
|
||||
ngx_str_t port;
|
||||
ngx_str_t uri;
|
||||
} ngx_http_proxy_vars_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_array_t *flushes;
|
||||
ngx_array_t *lengths;
|
||||
ngx_array_t *values;
|
||||
ngx_hash_t hash;
|
||||
} ngx_http_proxy_headers_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_upstream_conf_t upstream;
|
||||
|
||||
ngx_array_t *body_flushes;
|
||||
ngx_array_t *body_lengths;
|
||||
ngx_array_t *body_values;
|
||||
ngx_str_t body_source;
|
||||
|
||||
ngx_http_proxy_headers_t headers;
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_http_proxy_headers_t headers_cache;
|
||||
#endif
|
||||
ngx_array_t *headers_source;
|
||||
ngx_uint_t host_set;
|
||||
|
||||
ngx_array_t *proxy_lengths;
|
||||
ngx_array_t *proxy_values;
|
||||
|
||||
ngx_array_t *redirects;
|
||||
ngx_array_t *cookie_domains;
|
||||
ngx_array_t *cookie_paths;
|
||||
ngx_array_t *cookie_flags;
|
||||
|
||||
ngx_http_complex_value_t *method;
|
||||
ngx_str_t location;
|
||||
ngx_str_t url;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_http_complex_value_t cache_key;
|
||||
#endif
|
||||
|
||||
ngx_http_proxy_vars_t vars;
|
||||
|
||||
ngx_flag_t redirect;
|
||||
|
||||
ngx_uint_t http_version;
|
||||
|
||||
ngx_uint_t headers_hash_max_size;
|
||||
ngx_uint_t headers_hash_bucket_size;
|
||||
|
||||
#if (NGX_HTTP_SSL || NGX_COMPAT)
|
||||
ngx_uint_t ssl;
|
||||
ngx_uint_t ssl_protocols;
|
||||
ngx_str_t ssl_ciphers;
|
||||
ngx_uint_t ssl_verify_depth;
|
||||
ngx_str_t ssl_trusted_certificate;
|
||||
ngx_str_t ssl_crl;
|
||||
ngx_array_t *ssl_conf_commands;
|
||||
#endif
|
||||
} ngx_http_proxy_loc_conf_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_status_t status;
|
||||
ngx_http_chunked_t chunked;
|
||||
ngx_http_proxy_vars_t vars;
|
||||
off_t internal_body_length;
|
||||
|
||||
ngx_chain_t *free;
|
||||
ngx_chain_t *busy;
|
||||
|
||||
ngx_buf_t *trailers;
|
||||
|
||||
unsigned head:1;
|
||||
unsigned internal_chunked:1;
|
||||
unsigned header_sent:1;
|
||||
unsigned legacy:1;
|
||||
} ngx_http_proxy_ctx_t;
|
||||
|
||||
|
||||
ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,
|
||||
ngx_http_proxy_loc_conf_t *plcf);
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);
|
||||
#endif
|
||||
ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
|
||||
ngx_table_elt_t *h, size_t prefix);
|
||||
ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
|
||||
ngx_table_elt_t *h);
|
||||
|
||||
#if (NGX_HTTP_V2)
|
||||
ngx_int_t ngx_http_proxy_v2_handler(ngx_http_request_t *r);
|
||||
#endif
|
||||
|
||||
|
||||
extern ngx_module_t ngx_http_proxy_module;
|
||||
|
||||
|
||||
#endif /* _NGX_HTTP_PROXY_H_INCLUDED_ */
|
||||
@@ -0,0 +1,4160 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) Maxim Dounin
|
||||
* Copyright (C) Nginx, Inc.
|
||||
*/
|
||||
|
||||
|
||||
#include <ngx_config.h>
|
||||
#include <ngx_core.h>
|
||||
#include <ngx_http.h>
|
||||
#include <ngx_http_proxy_module.h>
|
||||
|
||||
|
||||
typedef enum {
|
||||
ngx_http_proxy_v2_st_start = 0,
|
||||
ngx_http_proxy_v2_st_length_2,
|
||||
ngx_http_proxy_v2_st_length_3,
|
||||
ngx_http_proxy_v2_st_type,
|
||||
ngx_http_proxy_v2_st_flags,
|
||||
ngx_http_proxy_v2_st_stream_id,
|
||||
ngx_http_proxy_v2_st_stream_id_2,
|
||||
ngx_http_proxy_v2_st_stream_id_3,
|
||||
ngx_http_proxy_v2_st_stream_id_4,
|
||||
ngx_http_proxy_v2_st_payload,
|
||||
ngx_http_proxy_v2_st_padding
|
||||
} ngx_http_proxy_v2_state_e;
|
||||
|
||||
|
||||
typedef struct {
|
||||
size_t init_window;
|
||||
size_t send_window;
|
||||
size_t recv_window;
|
||||
ngx_uint_t last_stream_id;
|
||||
} ngx_http_proxy_v2_conn_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
ngx_http_proxy_ctx_t ctx;
|
||||
|
||||
ngx_http_proxy_v2_state_e state;
|
||||
ngx_uint_t frame_state;
|
||||
ngx_uint_t fragment_state;
|
||||
|
||||
ngx_chain_t *in;
|
||||
ngx_chain_t *out;
|
||||
ngx_chain_t *free;
|
||||
ngx_chain_t *busy;
|
||||
|
||||
ngx_http_proxy_v2_conn_t *connection;
|
||||
|
||||
ngx_uint_t id;
|
||||
|
||||
ngx_uint_t pings;
|
||||
ngx_uint_t settings;
|
||||
|
||||
off_t length;
|
||||
|
||||
ssize_t send_window;
|
||||
size_t recv_window;
|
||||
|
||||
size_t rest;
|
||||
ngx_uint_t stream_id;
|
||||
u_char type;
|
||||
u_char flags;
|
||||
u_char padding;
|
||||
|
||||
ngx_uint_t error;
|
||||
ngx_uint_t window_update;
|
||||
|
||||
ngx_uint_t setting_id;
|
||||
ngx_uint_t setting_value;
|
||||
|
||||
u_char ping_data[8];
|
||||
|
||||
ngx_uint_t index;
|
||||
ngx_str_t name;
|
||||
ngx_str_t value;
|
||||
|
||||
u_char *field_end;
|
||||
size_t field_length;
|
||||
size_t field_rest;
|
||||
u_char field_state;
|
||||
|
||||
unsigned literal:1;
|
||||
unsigned field_huffman:1;
|
||||
|
||||
unsigned header_sent:1;
|
||||
unsigned output_closed:1;
|
||||
unsigned output_blocked:1;
|
||||
unsigned parsing_headers:1;
|
||||
unsigned end_stream:1;
|
||||
unsigned done:1;
|
||||
unsigned status:1;
|
||||
unsigned rst:1;
|
||||
unsigned goaway:1;
|
||||
} ngx_http_proxy_v2_ctx_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
u_char length_0;
|
||||
u_char length_1;
|
||||
u_char length_2;
|
||||
u_char type;
|
||||
u_char flags;
|
||||
u_char stream_id_0;
|
||||
u_char stream_id_1;
|
||||
u_char stream_id_2;
|
||||
u_char stream_id_3;
|
||||
} ngx_http_proxy_v2_frame_t;
|
||||
|
||||
|
||||
static ngx_int_t ngx_http_proxy_v2_create_request(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_proxy_v2_reinit_request(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_proxy_v2_body_output_filter(void *data,
|
||||
ngx_chain_t *in);
|
||||
static ngx_int_t ngx_http_proxy_v2_process_header(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_proxy_v2_filter_init(void *data);
|
||||
static ngx_int_t ngx_http_proxy_v2_non_buffered_filter(void *data,
|
||||
ssize_t bytes);
|
||||
static ngx_int_t ngx_http_proxy_v2_body_filter(ngx_event_pipe_t *p,
|
||||
ngx_buf_t *buf);
|
||||
static ngx_int_t ngx_http_proxy_v2_process_control_frame(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
static ngx_int_t ngx_http_proxy_v2_skip_frame(ngx_http_proxy_v2_ctx_t *ctx,
|
||||
ngx_buf_t *b);
|
||||
static ngx_int_t ngx_http_proxy_v2_process_frames(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
|
||||
static ngx_int_t ngx_http_proxy_v2_parse_frame(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
static ngx_int_t ngx_http_proxy_v2_parse_header(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
static ngx_int_t ngx_http_proxy_v2_parse_fragment(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
static ngx_int_t ngx_http_proxy_v2_validate_header_name(ngx_http_request_t *r,
|
||||
ngx_str_t *s);
|
||||
static ngx_int_t ngx_http_proxy_v2_validate_header_value(ngx_http_request_t *r,
|
||||
ngx_str_t *s);
|
||||
static ngx_int_t ngx_http_proxy_v2_parse_rst_stream(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
static ngx_int_t ngx_http_proxy_v2_parse_goaway(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
static ngx_int_t ngx_http_proxy_v2_parse_window_update(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
static ngx_int_t ngx_http_proxy_v2_parse_settings(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
static ngx_int_t ngx_http_proxy_v2_parse_ping(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b);
|
||||
|
||||
static ngx_int_t ngx_http_proxy_v2_send_settings_ack(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_proxy_v2_send_ping_ack(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx);
|
||||
static ngx_int_t ngx_http_proxy_v2_send_window_update(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx);
|
||||
|
||||
static ngx_chain_t *ngx_http_proxy_v2_get_buf(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx);
|
||||
static ngx_http_proxy_v2_ctx_t *
|
||||
ngx_http_proxy_v2_get_ctx(ngx_http_request_t *r);
|
||||
static ngx_int_t ngx_http_proxy_v2_get_connection_data(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_peer_connection_t *pc);
|
||||
static void ngx_http_proxy_v2_cleanup(void *data);
|
||||
|
||||
static void ngx_http_proxy_v2_abort_request(ngx_http_request_t *r);
|
||||
static void ngx_http_proxy_v2_finalize_request(ngx_http_request_t *r,
|
||||
ngx_int_t rc);
|
||||
|
||||
|
||||
static ngx_http_module_t ngx_http_proxy_v2_module_ctx = {
|
||||
NULL, /* preconfiguration */
|
||||
NULL, /* postconfiguration */
|
||||
|
||||
NULL, /* create main configuration */
|
||||
NULL, /* init main configuration */
|
||||
|
||||
NULL, /* create server configuration */
|
||||
NULL, /* merge server configuration */
|
||||
|
||||
NULL, /* create location configuration */
|
||||
NULL /* merge location configuration */
|
||||
};
|
||||
|
||||
|
||||
ngx_module_t ngx_http_proxy_v2_module = {
|
||||
NGX_MODULE_V1,
|
||||
&ngx_http_proxy_v2_module_ctx, /* module context */
|
||||
NULL, /* module directives */
|
||||
NGX_HTTP_MODULE, /* module type */
|
||||
NULL, /* init master */
|
||||
NULL, /* init module */
|
||||
NULL, /* init process */
|
||||
NULL, /* init thread */
|
||||
NULL, /* exit thread */
|
||||
NULL, /* exit process */
|
||||
NULL, /* exit master */
|
||||
NGX_MODULE_V1_PADDING
|
||||
};
|
||||
|
||||
|
||||
static u_char ngx_http_proxy_v2_connection_start[] =
|
||||
"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" /* connection preface */
|
||||
|
||||
"\x00\x00\x12\x04\x00\x00\x00\x00\x00" /* settings frame */
|
||||
"\x00\x01\x00\x00\x00\x00" /* header table size */
|
||||
"\x00\x02\x00\x00\x00\x00" /* disable push */
|
||||
"\x00\x04\x7f\xff\xff\xff" /* initial window */
|
||||
|
||||
"\x00\x00\x04\x08\x00\x00\x00\x00\x00" /* window update frame */
|
||||
"\x7f\xff\x00\x00";
|
||||
|
||||
|
||||
ngx_int_t
|
||||
ngx_http_proxy_v2_handler(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_proxy_v2_ctx_t *ctx;
|
||||
ngx_http_proxy_loc_conf_t *plcf;
|
||||
#if (NGX_HTTP_CACHE)
|
||||
ngx_http_proxy_main_conf_t *pmcf;
|
||||
#endif
|
||||
|
||||
if (ngx_http_upstream_create(r) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_v2_ctx_t));
|
||||
if (ctx == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_proxy_v2_module);
|
||||
|
||||
ngx_http_set_ctx(r, &ctx->ctx, ngx_http_proxy_module);
|
||||
|
||||
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
|
||||
|
||||
plcf->upstream.preserve_output = 1;
|
||||
|
||||
u = r->upstream;
|
||||
|
||||
if (plcf->proxy_lengths == NULL) {
|
||||
ctx->ctx.vars = plcf->vars;
|
||||
u->schema = plcf->vars.schema;
|
||||
#if (NGX_HTTP_SSL)
|
||||
u->ssl = plcf->ssl;
|
||||
#endif
|
||||
|
||||
} else {
|
||||
if (ngx_http_proxy_eval(r, &ctx->ctx, plcf) != NGX_OK) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
ngx_str_set(&u->ssl_alpn_protocol, NGX_HTTP_V2_ALPN_PROTO);
|
||||
#endif
|
||||
|
||||
u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_v2_module;
|
||||
|
||||
u->conf = &plcf->upstream;
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
pmcf = ngx_http_get_module_main_conf(r, ngx_http_proxy_module);
|
||||
|
||||
u->caches = &pmcf->caches;
|
||||
u->create_key = ngx_http_proxy_create_key;
|
||||
#endif
|
||||
|
||||
u->create_request = ngx_http_proxy_v2_create_request;
|
||||
u->reinit_request = ngx_http_proxy_v2_reinit_request;
|
||||
u->process_header = ngx_http_proxy_v2_process_header;
|
||||
u->abort_request = ngx_http_proxy_v2_abort_request;
|
||||
u->finalize_request = ngx_http_proxy_v2_finalize_request;
|
||||
|
||||
if (plcf->redirects) {
|
||||
u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
|
||||
}
|
||||
|
||||
if (plcf->cookie_domains || plcf->cookie_paths || plcf->cookie_flags) {
|
||||
u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;
|
||||
}
|
||||
|
||||
u->buffering = plcf->upstream.buffering;
|
||||
|
||||
u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
|
||||
if (u->pipe == NULL) {
|
||||
return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
u->pipe->input_filter = ngx_http_proxy_v2_body_filter;
|
||||
u->pipe->input_ctx = r;
|
||||
|
||||
u->input_filter_init = ngx_http_proxy_v2_filter_init;
|
||||
u->input_filter = ngx_http_proxy_v2_non_buffered_filter;
|
||||
u->input_filter_ctx = r;
|
||||
|
||||
u->accel = 1;
|
||||
|
||||
if (!plcf->upstream.request_buffering
|
||||
&& plcf->body_values == NULL && plcf->upstream.pass_request_body)
|
||||
{
|
||||
r->request_body_no_buffering = 1;
|
||||
}
|
||||
|
||||
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
|
||||
|
||||
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_create_request(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *p, *tmp, *key_tmp, *val_tmp, *headers_frame;
|
||||
size_t len, tmp_len, key_len, val_len, uri_len,
|
||||
loc_len, body_len;
|
||||
uintptr_t escape;
|
||||
ngx_buf_t *b;
|
||||
ngx_str_t method, *host;
|
||||
ngx_uint_t i, next, unparsed_uri;
|
||||
ngx_chain_t *cl, *body;
|
||||
ngx_list_part_t *part;
|
||||
ngx_table_elt_t *header;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_proxy_v2_ctx_t *ctx;
|
||||
ngx_http_script_code_pt code;
|
||||
ngx_http_script_engine_t e, le;
|
||||
ngx_http_proxy_headers_t *headers;
|
||||
ngx_http_proxy_v2_frame_t *f;
|
||||
ngx_http_proxy_loc_conf_t *plcf;
|
||||
ngx_http_script_len_code_pt lcode;
|
||||
|
||||
u = r->upstream;
|
||||
|
||||
plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
headers = u->cacheable ? &plcf->headers_cache : &plcf->headers;
|
||||
#else
|
||||
headers = &plcf->headers;
|
||||
#endif
|
||||
|
||||
if (u->method.len) {
|
||||
/* HEAD was changed to GET to cache response */
|
||||
method = u->method;
|
||||
|
||||
} else if (plcf->method) {
|
||||
if (ngx_http_complex_value(r, plcf->method, &method) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
method = r->method_name;
|
||||
}
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
|
||||
|
||||
if (method.len == 4
|
||||
&& ngx_strncasecmp(method.data, (u_char *) "HEAD", 4) == 0)
|
||||
{
|
||||
ctx->ctx.head = 1;
|
||||
}
|
||||
|
||||
len = sizeof(ngx_http_proxy_v2_connection_start) - 1
|
||||
+ sizeof(ngx_http_proxy_v2_frame_t); /* headers frame */
|
||||
|
||||
/* :method header */
|
||||
|
||||
if ((method.len == 3 && ngx_strncmp(method.data, "GET", 3) == 0)
|
||||
|| (method.len == 4 && ngx_strncmp(method.data, "POST", 4) == 0))
|
||||
{
|
||||
len += 1;
|
||||
tmp_len = 0;
|
||||
|
||||
} else {
|
||||
len += 1 + NGX_HTTP_V2_INT_OCTETS + method.len;
|
||||
tmp_len = method.len;
|
||||
}
|
||||
|
||||
/* :scheme header */
|
||||
|
||||
len += 1;
|
||||
|
||||
/* :path header */
|
||||
|
||||
escape = 0;
|
||||
loc_len = 0;
|
||||
unparsed_uri = 0;
|
||||
|
||||
if (plcf->proxy_lengths && ctx->ctx.vars.uri.len) {
|
||||
uri_len = ctx->ctx.vars.uri.len;
|
||||
|
||||
} else if (ctx->ctx.vars.uri.len == 0 && r->valid_unparsed_uri) {
|
||||
unparsed_uri = 1;
|
||||
uri_len = r->unparsed_uri.len;
|
||||
|
||||
} else {
|
||||
loc_len = (r->valid_location && ctx->ctx.vars.uri.len)
|
||||
? ngx_min(plcf->location.len, r->uri.len) : 0;
|
||||
|
||||
if (r->quoted_uri || r->internal) {
|
||||
escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
|
||||
r->uri.len - loc_len, NGX_ESCAPE_URI);
|
||||
}
|
||||
|
||||
uri_len = ctx->ctx.vars.uri.len + r->uri.len - loc_len + escape
|
||||
+ sizeof("?") - 1 + r->args.len;
|
||||
}
|
||||
|
||||
if (uri_len == 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"zero length URI to proxy");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len;
|
||||
|
||||
if (tmp_len < uri_len) {
|
||||
tmp_len = uri_len;
|
||||
}
|
||||
|
||||
/* :authority header */
|
||||
|
||||
host = &ctx->ctx.vars.host_header;
|
||||
|
||||
if (!plcf->host_set) {
|
||||
len += 1 + NGX_HTTP_V2_INT_OCTETS + host->len;
|
||||
|
||||
if (tmp_len < host->len) {
|
||||
tmp_len = host->len;
|
||||
}
|
||||
}
|
||||
|
||||
/* other headers */
|
||||
|
||||
ngx_memzero(&le, sizeof(ngx_http_script_engine_t));
|
||||
|
||||
ngx_http_script_flush_no_cacheable_variables(r, plcf->body_flushes);
|
||||
ngx_http_script_flush_no_cacheable_variables(r, headers->flushes);
|
||||
|
||||
body_len = 0;
|
||||
|
||||
if (plcf->body_lengths) {
|
||||
le.ip = plcf->body_lengths->elts;
|
||||
le.request = r;
|
||||
le.flushed = 1;
|
||||
|
||||
while (*(uintptr_t *) le.ip) {
|
||||
lcode = *(ngx_http_script_len_code_pt *) le.ip;
|
||||
body_len += lcode(&le);
|
||||
}
|
||||
|
||||
ctx->ctx.internal_body_length = body_len;
|
||||
|
||||
len += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
len += body_len;
|
||||
|
||||
} else if (r->headers_in.chunked && r->reading_body) {
|
||||
ctx->ctx.internal_body_length = -1;
|
||||
|
||||
} else {
|
||||
ctx->ctx.internal_body_length = r->headers_in.content_length_n;
|
||||
}
|
||||
|
||||
le.ip = headers->lengths->elts;
|
||||
le.request = r;
|
||||
le.flushed = 1;
|
||||
|
||||
while (*(uintptr_t *) le.ip) {
|
||||
|
||||
lcode = *(ngx_http_script_len_code_pt *) le.ip;
|
||||
key_len = lcode(&le);
|
||||
|
||||
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
|
||||
lcode = *(ngx_http_script_len_code_pt *) le.ip;
|
||||
}
|
||||
le.ip += sizeof(uintptr_t);
|
||||
|
||||
if (val_len == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len
|
||||
+ NGX_HTTP_V2_INT_OCTETS + val_len;
|
||||
|
||||
if (tmp_len < key_len) {
|
||||
tmp_len = key_len;
|
||||
}
|
||||
|
||||
if (tmp_len < val_len) {
|
||||
tmp_len = val_len;
|
||||
}
|
||||
}
|
||||
|
||||
if (plcf->upstream.pass_request_headers) {
|
||||
part = &r->headers_in.headers.part;
|
||||
header = part->elts;
|
||||
|
||||
for (i = 0; /* void */; i++) {
|
||||
|
||||
if (i >= part->nelts) {
|
||||
if (part->next == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
part = part->next;
|
||||
header = part->elts;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (ngx_hash_find(&headers->hash, header[i].hash,
|
||||
header[i].lowcase_key, header[i].key.len))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
|
||||
+ NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
|
||||
|
||||
if (tmp_len < header[i].key.len) {
|
||||
tmp_len = header[i].key.len;
|
||||
}
|
||||
|
||||
if (tmp_len < header[i].value.len) {
|
||||
tmp_len = header[i].value.len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* continuation frames */
|
||||
|
||||
len += sizeof(ngx_http_proxy_v2_frame_t)
|
||||
* (len / NGX_HTTP_V2_DEFAULT_FRAME_SIZE);
|
||||
|
||||
|
||||
b = ngx_create_temp_buf(r->pool, len);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl = ngx_alloc_chain_link(r->pool);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl->buf = b;
|
||||
cl->next = NULL;
|
||||
|
||||
tmp = ngx_palloc(r->pool, tmp_len * 3);
|
||||
if (tmp == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
key_tmp = tmp + tmp_len;
|
||||
val_tmp = tmp + 2 * tmp_len;
|
||||
|
||||
/* connection preface */
|
||||
|
||||
b->last = ngx_copy(b->last, ngx_http_proxy_v2_connection_start,
|
||||
sizeof(ngx_http_proxy_v2_connection_start) - 1);
|
||||
|
||||
/* headers frame */
|
||||
|
||||
headers_frame = b->last;
|
||||
|
||||
f = (ngx_http_proxy_v2_frame_t *) b->last;
|
||||
b->last += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
f->length_0 = 0;
|
||||
f->length_1 = 0;
|
||||
f->length_2 = 0;
|
||||
f->type = NGX_HTTP_V2_HEADERS_FRAME;
|
||||
f->flags = 0;
|
||||
f->stream_id_0 = 0;
|
||||
f->stream_id_1 = 0;
|
||||
f->stream_id_2 = 0;
|
||||
f->stream_id_3 = 1;
|
||||
|
||||
if (method.len == 3 && ngx_strncmp(method.data, "GET", 3) == 0) {
|
||||
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \":method: GET\"");
|
||||
|
||||
} else if (method.len == 4 && ngx_strncmp(method.data, "POST", 4) == 0) {
|
||||
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_POST_INDEX);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \":method: POST\"");
|
||||
|
||||
} else {
|
||||
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_METHOD_INDEX);
|
||||
b->last = ngx_http_v2_write_value(b->last, method.data,
|
||||
method.len, tmp);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \":method: %V\"", &method);
|
||||
}
|
||||
|
||||
#if (NGX_HTTP_SSL)
|
||||
if (u->ssl) {
|
||||
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \":scheme: https\"");
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \":scheme: http\"");
|
||||
}
|
||||
|
||||
if (plcf->proxy_lengths && ctx->ctx.vars.uri.len) {
|
||||
|
||||
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
|
||||
b->last = ngx_http_v2_write_value(b->last, ctx->ctx.vars.uri.data,
|
||||
ctx->ctx.vars.uri.len, tmp);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \":path: %V\"", &ctx->ctx.vars.uri);
|
||||
|
||||
} else if (unparsed_uri) {
|
||||
|
||||
if (r->unparsed_uri.len == 1 && r->unparsed_uri.data[0] == '/') {
|
||||
*b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_PATH_ROOT_INDEX);
|
||||
|
||||
} else {
|
||||
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
|
||||
b->last = ngx_http_v2_write_value(b->last, r->unparsed_uri.data,
|
||||
r->unparsed_uri.len, tmp);
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \":path: %V\"", &r->unparsed_uri);
|
||||
|
||||
} else {
|
||||
p = val_tmp;
|
||||
|
||||
if (r->valid_location) {
|
||||
p = ngx_copy(p, ctx->ctx.vars.uri.data, ctx->ctx.vars.uri.len);
|
||||
}
|
||||
|
||||
if (escape) {
|
||||
ngx_escape_uri(p, r->uri.data + loc_len,
|
||||
r->uri.len - loc_len, NGX_ESCAPE_URI);
|
||||
p += r->uri.len - loc_len + escape;
|
||||
|
||||
} else {
|
||||
p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len);
|
||||
}
|
||||
|
||||
if (r->args.len > 0) {
|
||||
*p++ = '?';
|
||||
p = ngx_copy(p, r->args.data, r->args.len);
|
||||
}
|
||||
|
||||
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
|
||||
b->last = ngx_http_v2_write_value(b->last, val_tmp, p - val_tmp, tmp);
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \":path: %*s\"", p - val_tmp,
|
||||
val_tmp);
|
||||
}
|
||||
|
||||
if (!plcf->host_set) {
|
||||
*b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX);
|
||||
b->last = ngx_http_v2_write_value(b->last, host->data, host->len, tmp);
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \":authority: %V\"", host);
|
||||
}
|
||||
|
||||
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
|
||||
|
||||
e.ip = headers->values->elts;
|
||||
e.request = r;
|
||||
e.flushed = 1;
|
||||
|
||||
le.ip = headers->lengths->elts;
|
||||
|
||||
while (*(uintptr_t *) le.ip) {
|
||||
|
||||
lcode = *(ngx_http_script_len_code_pt *) le.ip;
|
||||
key_len = lcode(&le);
|
||||
|
||||
for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {
|
||||
lcode = *(ngx_http_script_len_code_pt *) le.ip;
|
||||
}
|
||||
le.ip += sizeof(uintptr_t);
|
||||
|
||||
if (val_len == 0) {
|
||||
e.skip = 1;
|
||||
|
||||
while (*(uintptr_t *) e.ip) {
|
||||
code = *(ngx_http_script_code_pt *) e.ip;
|
||||
code((ngx_http_script_engine_t *) &e);
|
||||
}
|
||||
e.ip += sizeof(uintptr_t);
|
||||
|
||||
e.skip = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
*b->last++ = 0;
|
||||
|
||||
e.pos = key_tmp;
|
||||
|
||||
code = *(ngx_http_script_code_pt *) e.ip;
|
||||
code((ngx_http_script_engine_t *) &e);
|
||||
|
||||
b->last = ngx_http_v2_write_name(b->last, key_tmp, key_len, tmp);
|
||||
|
||||
e.pos = val_tmp;
|
||||
|
||||
while (*(uintptr_t *) e.ip) {
|
||||
code = *(ngx_http_script_code_pt *) e.ip;
|
||||
code((ngx_http_script_engine_t *) &e);
|
||||
}
|
||||
e.ip += sizeof(uintptr_t);
|
||||
|
||||
b->last = ngx_http_v2_write_value(b->last, val_tmp, val_len, tmp);
|
||||
|
||||
#if (NGX_DEBUG)
|
||||
if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
|
||||
ngx_strlow(key_tmp, key_tmp, key_len);
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \"%*s: %*s\"",
|
||||
key_len, key_tmp, val_len, val_tmp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (plcf->upstream.pass_request_headers) {
|
||||
part = &r->headers_in.headers.part;
|
||||
header = part->elts;
|
||||
|
||||
for (i = 0; /* void */; i++) {
|
||||
|
||||
if (i >= part->nelts) {
|
||||
if (part->next == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
part = part->next;
|
||||
header = part->elts;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (ngx_hash_find(&headers->hash, header[i].hash,
|
||||
header[i].lowcase_key, header[i].key.len))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
*b->last++ = 0;
|
||||
|
||||
b->last = ngx_http_v2_write_name(b->last, header[i].key.data,
|
||||
header[i].key.len, tmp);
|
||||
|
||||
b->last = ngx_http_v2_write_value(b->last, header[i].value.data,
|
||||
header[i].value.len, tmp);
|
||||
|
||||
#if (NGX_DEBUG)
|
||||
if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {
|
||||
ngx_strlow(tmp, header[i].key.data, header[i].key.len);
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \"%*s: %V\"",
|
||||
header[i].key.len, tmp, &header[i].value);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* update headers frame length */
|
||||
|
||||
len = b->last - headers_frame - sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {
|
||||
len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
|
||||
next = 1;
|
||||
|
||||
} else {
|
||||
next = 0;
|
||||
}
|
||||
|
||||
f = (ngx_http_proxy_v2_frame_t *) headers_frame;
|
||||
|
||||
f->length_0 = (u_char) ((len >> 16) & 0xff);
|
||||
f->length_1 = (u_char) ((len >> 8) & 0xff);
|
||||
f->length_2 = (u_char) (len & 0xff);
|
||||
|
||||
/* create additional continuation frames */
|
||||
|
||||
p = headers_frame;
|
||||
|
||||
while (next) {
|
||||
p += sizeof(ngx_http_proxy_v2_frame_t) + NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
|
||||
len = b->last - p;
|
||||
|
||||
ngx_memmove(p + sizeof(ngx_http_proxy_v2_frame_t), p, len);
|
||||
b->last += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {
|
||||
len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;
|
||||
next = 1;
|
||||
|
||||
} else {
|
||||
next = 0;
|
||||
}
|
||||
|
||||
f = (ngx_http_proxy_v2_frame_t *) p;
|
||||
|
||||
f->length_0 = (u_char) ((len >> 16) & 0xff);
|
||||
f->length_1 = (u_char) ((len >> 8) & 0xff);
|
||||
f->length_2 = (u_char) (len & 0xff);
|
||||
f->type = NGX_HTTP_V2_CONTINUATION_FRAME;
|
||||
f->flags = 0;
|
||||
f->stream_id_0 = 0;
|
||||
f->stream_id_1 = 0;
|
||||
f->stream_id_2 = 0;
|
||||
f->stream_id_3 = 1;
|
||||
}
|
||||
|
||||
f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
|
||||
|
||||
if (plcf->body_values) {
|
||||
f = (ngx_http_proxy_v2_frame_t *) b->last;
|
||||
b->last += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
f->length_0 = (u_char) ((body_len >> 16) & 0xff);
|
||||
f->length_1 = (u_char) ((body_len >> 8) & 0xff);
|
||||
f->length_2 = (u_char) (body_len & 0xff);
|
||||
f->type = NGX_HTTP_V2_DATA_FRAME;
|
||||
f->flags = NGX_HTTP_V2_END_STREAM_FLAG;
|
||||
f->stream_id_0 = 0;
|
||||
f->stream_id_1 = 0;
|
||||
f->stream_id_2 = 0;
|
||||
f->stream_id_3 = 1;
|
||||
|
||||
e.ip = plcf->body_values->elts;
|
||||
e.pos = b->last;
|
||||
e.request = r;
|
||||
e.flushed = 1;
|
||||
e.skip = 0;
|
||||
|
||||
while (*(uintptr_t *) e.ip) {
|
||||
code = *(ngx_http_script_code_pt *) e.ip;
|
||||
code((ngx_http_script_engine_t *) &e);
|
||||
}
|
||||
|
||||
b->last = e.pos;
|
||||
}
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: %*xs%s, len: %uz",
|
||||
(size_t) ngx_min(b->last - b->pos, 256), b->pos,
|
||||
b->last - b->pos > 256 ? "..." : "",
|
||||
b->last - b->pos);
|
||||
|
||||
if (r->request_body_no_buffering) {
|
||||
|
||||
u->request_bufs = cl;
|
||||
|
||||
} else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {
|
||||
|
||||
body = u->request_bufs;
|
||||
u->request_bufs = cl;
|
||||
|
||||
if (body == NULL) {
|
||||
f = (ngx_http_proxy_v2_frame_t *) headers_frame;
|
||||
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
|
||||
}
|
||||
|
||||
while (body) {
|
||||
b = ngx_alloc_buf(r->pool);
|
||||
if (b == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));
|
||||
|
||||
cl->next = ngx_alloc_chain_link(r->pool);
|
||||
if (cl->next == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cl = cl->next;
|
||||
cl->buf = b;
|
||||
|
||||
body = body->next;
|
||||
}
|
||||
|
||||
b->last_buf = 1;
|
||||
|
||||
} else {
|
||||
u->request_bufs = cl;
|
||||
|
||||
if (plcf->body_values == NULL) {
|
||||
f = (ngx_http_proxy_v2_frame_t *) headers_frame;
|
||||
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
u->output.output_filter = ngx_http_proxy_v2_body_output_filter;
|
||||
u->output.filter_ctx = r;
|
||||
|
||||
b->flush = 1;
|
||||
cl->next = NULL;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_reinit_request(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_proxy_v2_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx->state = 0;
|
||||
ctx->header_sent = 0;
|
||||
ctx->output_closed = 0;
|
||||
ctx->output_blocked = 0;
|
||||
ctx->parsing_headers = 0;
|
||||
ctx->end_stream = 0;
|
||||
ctx->done = 0;
|
||||
ctx->status = 0;
|
||||
ctx->rst = 0;
|
||||
ctx->goaway = 0;
|
||||
ctx->connection = NULL;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_body_output_filter(void *data, ngx_chain_t *in)
|
||||
{
|
||||
ngx_http_request_t *r = data;
|
||||
|
||||
off_t file_pos;
|
||||
u_char *p, *pos, *start;
|
||||
size_t len, limit;
|
||||
ngx_buf_t *b;
|
||||
ngx_int_t rc;
|
||||
ngx_uint_t next, last;
|
||||
ngx_chain_t *cl, *out, *ln, **ll;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_proxy_v2_ctx_t *ctx;
|
||||
ngx_http_proxy_v2_frame_t *f;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy output filter");
|
||||
|
||||
ctx = ngx_http_proxy_v2_get_ctx(r);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (in) {
|
||||
if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
out = NULL;
|
||||
ll = &out;
|
||||
|
||||
if (!ctx->header_sent) {
|
||||
/* first buffer contains headers */
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy output header");
|
||||
|
||||
ctx->header_sent = 1;
|
||||
|
||||
if (ctx->id != 1) {
|
||||
/*
|
||||
* keepalive connection: skip connection preface,
|
||||
* update stream identifiers
|
||||
*/
|
||||
|
||||
b = ctx->in->buf;
|
||||
b->pos += sizeof(ngx_http_proxy_v2_connection_start) - 1;
|
||||
|
||||
p = b->pos;
|
||||
|
||||
while (p < b->last) {
|
||||
f = (ngx_http_proxy_v2_frame_t *) p;
|
||||
p += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
|
||||
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
|
||||
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
|
||||
f->stream_id_3 = (u_char) (ctx->id & 0xff);
|
||||
|
||||
p += (f->length_0 << 16) + (f->length_1 << 8) + f->length_2;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->in->buf->last_buf) {
|
||||
ctx->output_closed = 1;
|
||||
}
|
||||
|
||||
*ll = ctx->in;
|
||||
ll = &ctx->in->next;
|
||||
|
||||
ctx->in = ctx->in->next;
|
||||
}
|
||||
|
||||
if (ctx->out) {
|
||||
/* queued control frames */
|
||||
|
||||
*ll = ctx->out;
|
||||
|
||||
for (cl = ctx->out, ll = &cl->next; cl; cl = cl->next) {
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
ctx->out = NULL;
|
||||
}
|
||||
|
||||
f = NULL;
|
||||
last = 0;
|
||||
|
||||
limit = ngx_max(0, ctx->send_window);
|
||||
|
||||
if (limit > ctx->connection->send_window) {
|
||||
limit = ctx->connection->send_window;
|
||||
}
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy output limit: %uz w:%z:%uz",
|
||||
limit, ctx->send_window, ctx->connection->send_window);
|
||||
|
||||
#if (NGX_SUPPRESS_WARN)
|
||||
file_pos = 0;
|
||||
pos = NULL;
|
||||
cl = NULL;
|
||||
#endif
|
||||
|
||||
in = ctx->in;
|
||||
|
||||
while (in && limit > 0) {
|
||||
|
||||
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
|
||||
"http proxy output in l:%d f:%d %p, pos %p, size: %z "
|
||||
"file: %O, size: %O",
|
||||
in->buf->last_buf,
|
||||
in->buf->in_file,
|
||||
in->buf->start, in->buf->pos,
|
||||
in->buf->last - in->buf->pos,
|
||||
in->buf->file_pos,
|
||||
in->buf->file_last - in->buf->file_pos);
|
||||
|
||||
if (ngx_buf_special(in->buf)) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (in->buf->in_file) {
|
||||
file_pos = in->buf->file_pos;
|
||||
|
||||
} else {
|
||||
pos = in->buf->pos;
|
||||
}
|
||||
|
||||
next = 0;
|
||||
|
||||
do {
|
||||
|
||||
cl = ngx_http_proxy_v2_get_buf(r, ctx);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = cl->buf;
|
||||
|
||||
f = (ngx_http_proxy_v2_frame_t *) b->last;
|
||||
b->last += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
*ll = cl;
|
||||
ll = &cl->next;
|
||||
|
||||
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = cl->buf;
|
||||
start = b->start;
|
||||
|
||||
ngx_memcpy(b, in->buf, sizeof(ngx_buf_t));
|
||||
|
||||
/*
|
||||
* restore b->start to preserve memory allocated in the buffer,
|
||||
* to reuse it later for headers and control frames
|
||||
*/
|
||||
|
||||
b->start = start;
|
||||
|
||||
if (in->buf->in_file) {
|
||||
b->file_pos = file_pos;
|
||||
file_pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit);
|
||||
|
||||
if (file_pos >= in->buf->file_last) {
|
||||
file_pos = in->buf->file_last;
|
||||
next = 1;
|
||||
}
|
||||
|
||||
b->file_last = file_pos;
|
||||
len = (ngx_uint_t) (file_pos - b->file_pos);
|
||||
|
||||
} else {
|
||||
b->pos = pos;
|
||||
pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit);
|
||||
|
||||
if (pos >= in->buf->last) {
|
||||
pos = in->buf->last;
|
||||
next = 1;
|
||||
}
|
||||
|
||||
b->last = pos;
|
||||
len = (ngx_uint_t) (pos - b->pos);
|
||||
}
|
||||
|
||||
b->tag = (ngx_buf_tag_t) &ngx_http_proxy_v2_body_output_filter;
|
||||
b->shadow = in->buf;
|
||||
b->last_shadow = next;
|
||||
|
||||
b->last_buf = 0;
|
||||
b->last_in_chain = 0;
|
||||
|
||||
*ll = cl;
|
||||
ll = &cl->next;
|
||||
|
||||
f->length_0 = (u_char) ((len >> 16) & 0xff);
|
||||
f->length_1 = (u_char) ((len >> 8) & 0xff);
|
||||
f->length_2 = (u_char) (len & 0xff);
|
||||
f->type = NGX_HTTP_V2_DATA_FRAME;
|
||||
f->flags = 0;
|
||||
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
|
||||
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
|
||||
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
|
||||
f->stream_id_3 = (u_char) (ctx->id & 0xff);
|
||||
|
||||
limit -= len;
|
||||
ctx->send_window -= len;
|
||||
ctx->connection->send_window -= len;
|
||||
|
||||
} while (!next && limit > 0);
|
||||
|
||||
if (!next) {
|
||||
/*
|
||||
* if the buffer wasn't fully sent due to flow control limits,
|
||||
* preserve position for future use
|
||||
*/
|
||||
|
||||
if (in->buf->in_file) {
|
||||
in->buf->file_pos = file_pos;
|
||||
|
||||
} else {
|
||||
in->buf->pos = pos;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
next:
|
||||
|
||||
if (in->buf->last_buf) {
|
||||
last = 1;
|
||||
}
|
||||
|
||||
ln = in;
|
||||
in = in->next;
|
||||
|
||||
ngx_free_chain(r->pool, ln);
|
||||
}
|
||||
|
||||
ctx->in = in;
|
||||
|
||||
if (last) {
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy output last");
|
||||
|
||||
ctx->output_closed = 1;
|
||||
|
||||
if (f) {
|
||||
f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;
|
||||
|
||||
} else {
|
||||
cl = ngx_http_proxy_v2_get_buf(r, ctx);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
b = cl->buf;
|
||||
|
||||
f = (ngx_http_proxy_v2_frame_t *) b->last;
|
||||
b->last += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
f->length_0 = 0;
|
||||
f->length_1 = 0;
|
||||
f->length_2 = 0;
|
||||
f->type = NGX_HTTP_V2_DATA_FRAME;
|
||||
f->flags = NGX_HTTP_V2_END_STREAM_FLAG;
|
||||
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
|
||||
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
|
||||
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
|
||||
f->stream_id_3 = (u_char) (ctx->id & 0xff);
|
||||
|
||||
*ll = cl;
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
cl->buf->last_buf = 1;
|
||||
}
|
||||
|
||||
*ll = NULL;
|
||||
|
||||
#if (NGX_DEBUG)
|
||||
|
||||
for (cl = out; cl; cl = cl->next) {
|
||||
ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
|
||||
"http proxy output out l:%d f:%d %p, pos %p, size: %z "
|
||||
"file: %O, size: %O",
|
||||
cl->buf->last_buf,
|
||||
cl->buf->in_file,
|
||||
cl->buf->start, cl->buf->pos,
|
||||
cl->buf->last - cl->buf->pos,
|
||||
cl->buf->file_pos,
|
||||
cl->buf->file_last - cl->buf->file_pos);
|
||||
}
|
||||
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy output limit: %uz w:%z:%uz",
|
||||
limit, ctx->send_window, ctx->connection->send_window);
|
||||
|
||||
#endif
|
||||
|
||||
rc = ngx_chain_writer(&r->upstream->writer, out);
|
||||
|
||||
ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
|
||||
(ngx_buf_tag_t) &ngx_http_proxy_v2_body_output_filter);
|
||||
|
||||
for (cl = ctx->free; cl; cl = cl->next) {
|
||||
|
||||
/* mark original buffers as sent */
|
||||
|
||||
if (cl->buf->shadow) {
|
||||
if (cl->buf->last_shadow) {
|
||||
b = cl->buf->shadow;
|
||||
b->pos = b->last;
|
||||
}
|
||||
|
||||
cl->buf->shadow = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc == NGX_OK && ctx->in) {
|
||||
rc = NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
ctx->output_blocked = 1;
|
||||
|
||||
} else {
|
||||
ctx->output_blocked = 0;
|
||||
}
|
||||
|
||||
if (ctx->done) {
|
||||
|
||||
/*
|
||||
* We have already got the response and were sending some additional
|
||||
* control frames. Even if there is still something unsent, stop
|
||||
* here anyway.
|
||||
*/
|
||||
|
||||
u = r->upstream;
|
||||
u->length = 0;
|
||||
u->pipe->length = 0;
|
||||
|
||||
if (ctx->in == NULL
|
||||
&& ctx->out == NULL
|
||||
&& ctx->output_closed
|
||||
&& !ctx->output_blocked
|
||||
&& !ctx->goaway
|
||||
&& ctx->state == ngx_http_proxy_v2_st_start)
|
||||
{
|
||||
u->keepalive = 1;
|
||||
}
|
||||
|
||||
ngx_post_event(u->peer.connection->read, &ngx_posted_events);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_process_header(ngx_http_request_t *r)
|
||||
{
|
||||
u_char *pos;
|
||||
ngx_str_t *status_line;
|
||||
ngx_int_t rc, status;
|
||||
ngx_buf_t *b;
|
||||
ngx_table_elt_t *h;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_proxy_v2_ctx_t *ctx;
|
||||
ngx_http_upstream_header_t *hh;
|
||||
ngx_http_upstream_main_conf_t *umcf;
|
||||
|
||||
u = r->upstream;
|
||||
b = &u->buffer;
|
||||
pos = b->pos;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy response: %*xs%s, len: %uz",
|
||||
(size_t) ngx_min(b->last - b->pos, 256),
|
||||
b->pos, b->last - b->pos > 256 ? "..." : "",
|
||||
b->last - b->pos);
|
||||
|
||||
ctx = ngx_http_proxy_v2_get_ctx(r);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
if (ctx->state < ngx_http_proxy_v2_st_payload) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_frame(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
|
||||
/*
|
||||
* there can be a lot of window update frames,
|
||||
* so we reset buffer if it is empty and we haven't
|
||||
* started parsing headers yet
|
||||
*/
|
||||
|
||||
if (!ctx->parsing_headers) {
|
||||
b->pos = pos;
|
||||
b->last = b->pos;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 7540 says that implementations MUST discard frames
|
||||
* that have unknown or unsupported types. However, extension
|
||||
* frames that appear in the middle of a header block are
|
||||
* not permitted. Also, for obvious reasons CONTINUATION frames
|
||||
* cannot appear before headers, and DATA frames are not expected
|
||||
* to appear before all headers are parsed.
|
||||
*/
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_DATA_FRAME
|
||||
|| (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME
|
||||
&& !ctx->parsing_headers)
|
||||
|| (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME
|
||||
&& ctx->parsing_headers))
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent unexpected http2 frame: %d",
|
||||
ctx->type);
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
if (ctx->stream_id && ctx->stream_id != ctx->id) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent frame for unknown stream %ui",
|
||||
ctx->stream_id);
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
}
|
||||
|
||||
/* frame payload */
|
||||
|
||||
if (u->peer.connection) {
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) {
|
||||
rc = ngx_http_proxy_v2_parse_rst_stream(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream rejected request with error %ui",
|
||||
ctx->error);
|
||||
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
rc = ngx_http_proxy_v2_process_control_frame(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->type != NGX_HTTP_V2_HEADERS_FRAME
|
||||
&& ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME)
|
||||
{
|
||||
/* priority, unknown frames */
|
||||
|
||||
rc = ngx_http_proxy_v2_skip_frame(ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* headers */
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_header(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
/* a header line has been parsed successfully */
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header: \"%V: %V\"",
|
||||
&ctx->name, &ctx->value);
|
||||
|
||||
if (ctx->name.len && ctx->name.data[0] == ':') {
|
||||
|
||||
if (ctx->name.len != sizeof(":status") - 1
|
||||
|| ngx_strncmp(ctx->name.data, ":status",
|
||||
sizeof(":status") - 1)
|
||||
!= 0)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid header \"%V: %V\"",
|
||||
&ctx->name, &ctx->value);
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
if (ctx->status) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent duplicate :status header");
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
status_line = &ctx->value;
|
||||
|
||||
if (status_line->len != 3) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid :status \"%V\"",
|
||||
status_line);
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
status = ngx_atoi(status_line->data, 3);
|
||||
|
||||
if (status == NGX_ERROR) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid :status \"%V\"",
|
||||
status_line);
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
if (status < NGX_HTTP_OK && status != NGX_HTTP_EARLY_HINTS)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent unexpected :status \"%V\"",
|
||||
status_line);
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
u->headers_in.status_n = status;
|
||||
|
||||
if (u->state && u->state->status == 0) {
|
||||
u->state->status = status;
|
||||
}
|
||||
|
||||
ctx->status = 1;
|
||||
|
||||
continue;
|
||||
|
||||
} else if (!ctx->status) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent no :status header");
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
h = ngx_list_push(&u->headers_in.headers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
h->key = ctx->name;
|
||||
h->value = ctx->value;
|
||||
h->lowcase_key = h->key.data;
|
||||
h->hash = ngx_hash_key(h->key.data, h->key.len);
|
||||
|
||||
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
|
||||
h->lowcase_key, h->key.len);
|
||||
|
||||
if (hh) {
|
||||
rc = hh->handler(r, h, hh->offset);
|
||||
|
||||
if (rc != NGX_OK) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
|
||||
|
||||
/* a whole header has been parsed successfully */
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header done");
|
||||
|
||||
if (u->headers_in.status_n == NGX_HTTP_EARLY_HINTS) {
|
||||
if (ctx->end_stream) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream prematurely closed stream");
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
ctx->status = 0;
|
||||
return NGX_HTTP_UPSTREAM_EARLY_HINTS;
|
||||
}
|
||||
|
||||
if (ctx->end_stream
|
||||
&& ctx->in == NULL
|
||||
&& ctx->out == NULL
|
||||
&& ctx->output_closed
|
||||
&& !ctx->output_blocked
|
||||
&& !ctx->goaway
|
||||
&& b->last == b->pos)
|
||||
{
|
||||
u->keepalive = 1;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
/* there was error while a header line parsing */
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid header");
|
||||
|
||||
return NGX_HTTP_UPSTREAM_INVALID_HEADER;
|
||||
}
|
||||
|
||||
/* rc == NGX_AGAIN */
|
||||
|
||||
if (ctx->rest == 0) {
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
continue;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_filter_init(void *data)
|
||||
{
|
||||
ngx_http_request_t *r = data;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_proxy_v2_ctx_t *ctx;
|
||||
|
||||
u = r->upstream;
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT
|
||||
|| u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED
|
||||
|| ctx->ctx.head)
|
||||
{
|
||||
ctx->length = 0;
|
||||
|
||||
} else {
|
||||
ctx->length = u->headers_in.content_length_n;
|
||||
}
|
||||
|
||||
if (ctx->end_stream) {
|
||||
|
||||
if (ctx->length > 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream prematurely closed stream");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
u->length = 0;
|
||||
u->pipe->length = 0;
|
||||
ctx->done = 1;
|
||||
|
||||
} else {
|
||||
u->length = 1;
|
||||
u->pipe->length = 1;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_non_buffered_filter(void *data, ssize_t bytes)
|
||||
{
|
||||
ngx_http_request_t *r = data;
|
||||
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *b, *buf;
|
||||
ngx_chain_t *cl, **ll;
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_proxy_v2_ctx_t *ctx;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy filter bytes:%z", bytes);
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
u = r->upstream;
|
||||
b = &u->buffer;
|
||||
|
||||
b->pos = b->last;
|
||||
b->last += bytes;
|
||||
|
||||
for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
rc = ngx_http_proxy_v2_process_frames(r, ctx, b);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
*ll = cl;
|
||||
ll = &cl->next;
|
||||
|
||||
buf = cl->buf;
|
||||
|
||||
buf->flush = 1;
|
||||
buf->memory = 1;
|
||||
|
||||
buf->pos = b->pos;
|
||||
buf->tag = u->output.tag;
|
||||
|
||||
if (b->last - b->pos >= (ssize_t) ctx->rest - ctx->padding) {
|
||||
b->pos += ctx->rest - ctx->padding;
|
||||
buf->last = b->pos;
|
||||
ctx->rest = ctx->padding;
|
||||
|
||||
} else {
|
||||
ctx->rest -= b->last - b->pos;
|
||||
b->pos = b->last;
|
||||
buf->last = b->pos;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy output buf %p", buf->pos);
|
||||
|
||||
if (ctx->length != -1) {
|
||||
|
||||
if (buf->last - buf->pos > ctx->length) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent response body larger "
|
||||
"than indicated content length");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->length -= buf->last - buf->pos;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
u->length = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
/* invalid response */
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_body_filter(ngx_event_pipe_t *p, ngx_buf_t *b)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_buf_t *buf, **prev;
|
||||
ngx_chain_t *cl;
|
||||
ngx_http_request_t *r;
|
||||
ngx_http_proxy_v2_ctx_t *ctx;
|
||||
|
||||
if (b->pos == b->last) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
r = p->input_ctx;
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
|
||||
|
||||
if (ctx == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
buf = NULL;
|
||||
prev = &b->shadow;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy filter bytes:%z", b->last - b->pos);
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
rc = ngx_http_proxy_v2_process_frames(r, ctx, b);
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
/* copy data frame payload for buffering */
|
||||
|
||||
cl = ngx_chain_get_free_buf(p->pool, &p->free);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
buf = cl->buf;
|
||||
|
||||
ngx_memzero(buf, sizeof(ngx_buf_t));
|
||||
|
||||
buf->pos = b->pos;
|
||||
buf->start = b->start;
|
||||
buf->end = b->end;
|
||||
buf->tag = p->tag;
|
||||
buf->temporary = 1;
|
||||
buf->recycled = 1;
|
||||
|
||||
*prev = buf;
|
||||
prev = &buf->shadow;
|
||||
|
||||
if (p->in) {
|
||||
*p->last_in = cl;
|
||||
|
||||
} else {
|
||||
p->in = cl;
|
||||
}
|
||||
|
||||
p->last_in = &cl->next;
|
||||
|
||||
/* STUB */ buf->num = b->num;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy copy buf %p", buf->pos);
|
||||
|
||||
if (b->last - b->pos >= (ssize_t) ctx->rest - ctx->padding) {
|
||||
b->pos += ctx->rest - ctx->padding;
|
||||
buf->last = b->pos;
|
||||
ctx->rest = ctx->padding;
|
||||
|
||||
} else {
|
||||
ctx->rest -= b->last - b->pos;
|
||||
b->pos = b->last;
|
||||
buf->last = b->pos;
|
||||
}
|
||||
|
||||
if (ctx->length != -1) {
|
||||
|
||||
if (buf->last - buf->pos > ctx->length) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent response body larger "
|
||||
"than indicated content length");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->length -= buf->last - buf->pos;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc == NGX_DONE) {
|
||||
p->length = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* invalid response */
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (buf) {
|
||||
buf->shadow = b;
|
||||
buf->last_shadow = 1;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,
|
||||
"input buf %p %z", buf->pos, buf->last - buf->pos);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
/* there is no data record in the buf, add it to free chain */
|
||||
|
||||
if (ngx_event_pipe_add_free_buf(p, b) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_process_control_frame(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_http_upstream_t *u;
|
||||
|
||||
u = r->upstream;
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_goaway(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* If stream_id is lower than one we use, our
|
||||
* request won't be processed and needs to be retried.
|
||||
* If stream_id is greater or equal to the one we use,
|
||||
* we can continue normally (except we can't use this
|
||||
* connection for additional requests). If there is
|
||||
* a real error, the connection will be closed.
|
||||
*/
|
||||
|
||||
if (ctx->stream_id < ctx->id) {
|
||||
|
||||
/* TODO: we can retry non-idempotent requests */
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent goaway with error %ui",
|
||||
ctx->error);
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->goaway = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_window_update(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->in) {
|
||||
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_settings(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->in) {
|
||||
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_PING_FRAME) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_ping(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_post_event(u->peer.connection->write, &ngx_posted_events);
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent unexpected push promise frame");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
return NGX_DECLINED;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_skip_frame(ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest) {
|
||||
ctx->rest -= b->last - b->pos;
|
||||
b->pos = b->last;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
b->pos += ctx->rest;
|
||||
ctx->rest = 0;
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_process_frames(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
ngx_int_t rc;
|
||||
ngx_table_elt_t *h;
|
||||
ngx_http_upstream_t *u;
|
||||
|
||||
u = r->upstream;
|
||||
|
||||
for ( ;; ) {
|
||||
|
||||
if (ctx->state < ngx_http_proxy_v2_st_payload) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_frame(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
|
||||
if (ctx->done) {
|
||||
|
||||
if (ctx->length > 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream prematurely closed stream");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* We have finished parsing the response and the
|
||||
* remaining control frames. If there are unsent
|
||||
* control frames, post a write event to send them.
|
||||
*/
|
||||
|
||||
if (ctx->out) {
|
||||
ngx_post_event(u->peer.connection->write,
|
||||
&ngx_posted_events);
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (ctx->in == NULL
|
||||
&& ctx->output_closed
|
||||
&& !ctx->output_blocked
|
||||
&& !ctx->goaway
|
||||
&& ctx->state == ngx_http_proxy_v2_st_start)
|
||||
{
|
||||
u->keepalive = 1;
|
||||
}
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if ((ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME
|
||||
&& !ctx->parsing_headers)
|
||||
|| (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME
|
||||
&& ctx->parsing_headers))
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent unexpected http2 frame: %d",
|
||||
ctx->type);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_DATA_FRAME) {
|
||||
|
||||
if (ctx->stream_id != ctx->id) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent data frame "
|
||||
"for unknown stream %ui",
|
||||
ctx->stream_id);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->rest > ctx->recv_window) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream violated stream flow control, "
|
||||
"received %uz data frame with window %uz",
|
||||
ctx->rest, ctx->recv_window);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->rest > ctx->connection->recv_window) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream violated connection flow control, "
|
||||
"received %uz data frame with window %uz",
|
||||
ctx->rest, ctx->connection->recv_window);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->recv_window -= ctx->rest;
|
||||
ctx->connection->recv_window -= ctx->rest;
|
||||
|
||||
if (ctx->connection->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4
|
||||
|| ctx->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4)
|
||||
{
|
||||
if (ngx_http_proxy_v2_send_window_update(r, ctx)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_post_event(u->peer.connection->write,
|
||||
&ngx_posted_events);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->stream_id && ctx->stream_id != ctx->id) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent frame for unknown stream %ui",
|
||||
ctx->stream_id);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->stream_id && ctx->done
|
||||
&& ctx->type != NGX_HTTP_V2_RST_STREAM_FRAME
|
||||
&& ctx->type != NGX_HTTP_V2_WINDOW_UPDATE_FRAME)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent frame for closed stream %ui",
|
||||
ctx->stream_id);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->padding = 0;
|
||||
}
|
||||
|
||||
if (ctx->state == ngx_http_proxy_v2_st_padding) {
|
||||
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest) {
|
||||
ctx->rest -= b->last - b->pos;
|
||||
b->pos = b->last;
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
b->pos += ctx->rest;
|
||||
ctx->rest = 0;
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
|
||||
ctx->done = 1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* frame payload */
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_rst_stream(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->error || !ctx->done) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream rejected request with error %ui",
|
||||
ctx->error);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->rst) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent frame for closed stream %ui",
|
||||
ctx->stream_id);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->rst = 1;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = ngx_http_proxy_v2_process_control_frame(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME
|
||||
|| ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME)
|
||||
{
|
||||
for ( ;; ) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_header(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
|
||||
/* a header line has been parsed successfully */
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy trailer: \"%V: %V\"",
|
||||
&ctx->name, &ctx->value);
|
||||
|
||||
if (ctx->name.len && ctx->name.data[0] == ':') {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid "
|
||||
"trailer \"%V: %V\"",
|
||||
&ctx->name, &ctx->value);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
h = ngx_list_push(&u->headers_in.trailers);
|
||||
if (h == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
h->key = ctx->name;
|
||||
h->value = ctx->value;
|
||||
h->lowcase_key = h->key.data;
|
||||
h->hash = ngx_hash_key(h->key.data, h->key.len);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
|
||||
|
||||
/* a whole header has been parsed successfully */
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy trailer done");
|
||||
|
||||
if (ctx->end_stream) {
|
||||
ctx->done = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent trailer without "
|
||||
"end stream flag");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
/* there was error while a header line parsing */
|
||||
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid trailer");
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* rc == NGX_AGAIN */
|
||||
|
||||
if (ctx->rest == 0) {
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
continue;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (ctx->type != NGX_HTTP_V2_DATA_FRAME) {
|
||||
|
||||
/* priority, unknown frames */
|
||||
|
||||
rc = ngx_http_proxy_v2_skip_frame(ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* data frame:
|
||||
*
|
||||
* +---------------+
|
||||
* |Pad Length? (8)|
|
||||
* +---------------+-----------------------------------------------+
|
||||
* | Data (*) ...
|
||||
* +---------------------------------------------------------------+
|
||||
* | Padding (*) ...
|
||||
* +---------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) {
|
||||
|
||||
if (ctx->rest == 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent too short http2 frame");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (b->pos == b->last) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ctx->flags &= ~NGX_HTTP_V2_PADDED_FLAG;
|
||||
ctx->padding = *b->pos++;
|
||||
ctx->rest -= 1;
|
||||
|
||||
if (ctx->padding > ctx->rest) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent http2 frame with too long "
|
||||
"padding: %d in frame %uz",
|
||||
ctx->padding, ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx->padding == ctx->rest) {
|
||||
|
||||
if (ctx->padding) {
|
||||
ctx->state = ngx_http_proxy_v2_st_padding;
|
||||
|
||||
} else {
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
|
||||
ctx->done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (b->pos == b->last) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_parse_frame(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
u_char ch, *p;
|
||||
ngx_http_proxy_v2_state_e state;
|
||||
|
||||
state = ctx->state;
|
||||
|
||||
for (p = b->pos; p < b->last; p++) {
|
||||
ch = *p;
|
||||
|
||||
#if 0
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy frame byte: %02Xd, s:%d", ch, state);
|
||||
#endif
|
||||
|
||||
switch (state) {
|
||||
|
||||
case ngx_http_proxy_v2_st_start:
|
||||
ctx->rest = ch << 16;
|
||||
state = ngx_http_proxy_v2_st_length_2;
|
||||
break;
|
||||
|
||||
case ngx_http_proxy_v2_st_length_2:
|
||||
ctx->rest |= ch << 8;
|
||||
state = ngx_http_proxy_v2_st_length_3;
|
||||
break;
|
||||
|
||||
case ngx_http_proxy_v2_st_length_3:
|
||||
ctx->rest |= ch;
|
||||
|
||||
if (ctx->rest > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent too large http2 frame: %uz",
|
||||
ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
state = ngx_http_proxy_v2_st_type;
|
||||
break;
|
||||
|
||||
case ngx_http_proxy_v2_st_type:
|
||||
ctx->type = ch;
|
||||
state = ngx_http_proxy_v2_st_flags;
|
||||
break;
|
||||
|
||||
case ngx_http_proxy_v2_st_flags:
|
||||
ctx->flags = ch;
|
||||
state = ngx_http_proxy_v2_st_stream_id;
|
||||
break;
|
||||
|
||||
case ngx_http_proxy_v2_st_stream_id:
|
||||
ctx->stream_id = (ch & 0x7f) << 24;
|
||||
state = ngx_http_proxy_v2_st_stream_id_2;
|
||||
break;
|
||||
|
||||
case ngx_http_proxy_v2_st_stream_id_2:
|
||||
ctx->stream_id |= ch << 16;
|
||||
state = ngx_http_proxy_v2_st_stream_id_3;
|
||||
break;
|
||||
|
||||
case ngx_http_proxy_v2_st_stream_id_3:
|
||||
ctx->stream_id |= ch << 8;
|
||||
state = ngx_http_proxy_v2_st_stream_id_4;
|
||||
break;
|
||||
|
||||
case ngx_http_proxy_v2_st_stream_id_4:
|
||||
ctx->stream_id |= ch;
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy frame: %d, len: %uz, f:%d, i:%ui",
|
||||
ctx->type, ctx->rest, ctx->flags, ctx->stream_id);
|
||||
|
||||
b->pos = p + 1;
|
||||
|
||||
ctx->state = ngx_http_proxy_v2_st_payload;
|
||||
ctx->frame_state = 0;
|
||||
|
||||
return NGX_OK;
|
||||
|
||||
/* suppress warning */
|
||||
case ngx_http_proxy_v2_st_payload:
|
||||
case ngx_http_proxy_v2_st_padding:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
b->pos = p;
|
||||
ctx->state = state;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_parse_header(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
u_char ch, *p, *last;
|
||||
size_t min;
|
||||
ngx_int_t rc;
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_padding_length,
|
||||
sw_dependency,
|
||||
sw_dependency_2,
|
||||
sw_dependency_3,
|
||||
sw_dependency_4,
|
||||
sw_weight,
|
||||
sw_fragment,
|
||||
sw_padding
|
||||
} state;
|
||||
|
||||
state = ctx->frame_state;
|
||||
|
||||
if (state == sw_start) {
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy parse header: start");
|
||||
|
||||
if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME) {
|
||||
ctx->parsing_headers = 1;
|
||||
ctx->fragment_state = 0;
|
||||
|
||||
min = (ctx->flags & NGX_HTTP_V2_PADDED_FLAG ? 1 : 0)
|
||||
+ (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG ? 5 : 0);
|
||||
|
||||
if (ctx->rest < min) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent headers frame "
|
||||
"with invalid length: %uz",
|
||||
ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {
|
||||
ctx->end_stream = 1;
|
||||
}
|
||||
|
||||
if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) {
|
||||
state = sw_padding_length;
|
||||
|
||||
} else if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) {
|
||||
state = sw_dependency;
|
||||
|
||||
} else {
|
||||
state = sw_fragment;
|
||||
}
|
||||
|
||||
} else if (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME) {
|
||||
state = sw_fragment;
|
||||
}
|
||||
|
||||
ctx->padding = 0;
|
||||
ctx->frame_state = state;
|
||||
}
|
||||
|
||||
if (state < sw_fragment) {
|
||||
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest) {
|
||||
last = b->last;
|
||||
|
||||
} else {
|
||||
last = b->pos + ctx->rest;
|
||||
}
|
||||
|
||||
for (p = b->pos; p < last; p++) {
|
||||
ch = *p;
|
||||
|
||||
#if 0
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header byte: %02Xd s:%d", ch, state);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* headers frame:
|
||||
*
|
||||
* +---------------+
|
||||
* |Pad Length? (8)|
|
||||
* +-+-------------+----------------------------------------------+
|
||||
* |E| Stream Dependency? (31) |
|
||||
* +-+-------------+----------------------------------------------+
|
||||
* | Weight? (8) |
|
||||
* +-+-------------+----------------------------------------------+
|
||||
* | Header Block Fragment (*) ...
|
||||
* +--------------------------------------------------------------+
|
||||
* | Padding (*) ...
|
||||
* +--------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
switch (state) {
|
||||
|
||||
case sw_padding_length:
|
||||
|
||||
ctx->padding = ch;
|
||||
|
||||
if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) {
|
||||
state = sw_dependency;
|
||||
break;
|
||||
}
|
||||
|
||||
goto fragment;
|
||||
|
||||
case sw_dependency:
|
||||
state = sw_dependency_2;
|
||||
break;
|
||||
|
||||
case sw_dependency_2:
|
||||
state = sw_dependency_3;
|
||||
break;
|
||||
|
||||
case sw_dependency_3:
|
||||
state = sw_dependency_4;
|
||||
break;
|
||||
|
||||
case sw_dependency_4:
|
||||
state = sw_weight;
|
||||
break;
|
||||
|
||||
case sw_weight:
|
||||
goto fragment;
|
||||
|
||||
/* suppress warning */
|
||||
case sw_start:
|
||||
case sw_fragment:
|
||||
case sw_padding:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->rest -= p - b->pos;
|
||||
b->pos = p;
|
||||
|
||||
ctx->frame_state = state;
|
||||
return NGX_AGAIN;
|
||||
|
||||
fragment:
|
||||
|
||||
p++;
|
||||
ctx->rest -= p - b->pos;
|
||||
b->pos = p;
|
||||
|
||||
if (ctx->padding > ctx->rest) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent http2 frame with too long "
|
||||
"padding: %d in frame %uz",
|
||||
ctx->padding, ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
state = sw_fragment;
|
||||
ctx->frame_state = state;
|
||||
}
|
||||
|
||||
if (state == sw_fragment) {
|
||||
|
||||
rc = ngx_http_proxy_v2_parse_fragment(r, ctx, b);
|
||||
|
||||
if (rc == NGX_AGAIN) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
if (rc == NGX_ERROR) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (rc == NGX_OK) {
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
/* rc == NGX_DONE */
|
||||
|
||||
state = sw_padding;
|
||||
ctx->frame_state = state;
|
||||
}
|
||||
|
||||
if (state == sw_padding) {
|
||||
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest) {
|
||||
|
||||
ctx->rest -= b->last - b->pos;
|
||||
b->pos = b->last;
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
b->pos += ctx->rest;
|
||||
ctx->rest = 0;
|
||||
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
if (ctx->flags & NGX_HTTP_V2_END_HEADERS_FLAG) {
|
||||
|
||||
if (ctx->fragment_state) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent truncated http2 header");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->parsing_headers = 0;
|
||||
|
||||
return NGX_HTTP_PARSE_HEADER_DONE;
|
||||
}
|
||||
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
/* unreachable */
|
||||
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_parse_fragment(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
u_char ch, *p, *last;
|
||||
size_t size;
|
||||
ngx_uint_t index, size_update;
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_index,
|
||||
sw_name_length,
|
||||
sw_name_length_2,
|
||||
sw_name_length_3,
|
||||
sw_name_length_4,
|
||||
sw_name,
|
||||
sw_name_bytes,
|
||||
sw_value_length,
|
||||
sw_value_length_2,
|
||||
sw_value_length_3,
|
||||
sw_value_length_4,
|
||||
sw_value,
|
||||
sw_value_bytes
|
||||
} state;
|
||||
|
||||
/* header block fragment */
|
||||
|
||||
#if 0
|
||||
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header fragment %p:%p rest:%uz",
|
||||
b->pos, b->last, ctx->rest);
|
||||
#endif
|
||||
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) {
|
||||
last = b->last;
|
||||
|
||||
} else {
|
||||
last = b->pos + ctx->rest - ctx->padding;
|
||||
}
|
||||
|
||||
state = ctx->fragment_state;
|
||||
|
||||
for (p = b->pos; p < last; p++) {
|
||||
ch = *p;
|
||||
|
||||
#if 0
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header byte: %02Xd s:%d", ch, state);
|
||||
#endif
|
||||
|
||||
switch (state) {
|
||||
|
||||
case sw_start:
|
||||
ctx->index = 0;
|
||||
|
||||
if ((ch & 0x80) == 0x80) {
|
||||
/*
|
||||
* indexed header:
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | 1 | Index (7+) |
|
||||
* +---+---------------------------+
|
||||
*/
|
||||
|
||||
index = ch & ~0x80;
|
||||
|
||||
if (index == 0 || index > 61) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid http2 "
|
||||
"table index: %ui", index);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy indexed header: %ui", index);
|
||||
|
||||
ctx->index = index;
|
||||
ctx->literal = 0;
|
||||
|
||||
goto done;
|
||||
|
||||
} else if ((ch & 0xc0) == 0x40) {
|
||||
/*
|
||||
* literal header with incremental indexing:
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | 0 | 1 | Index (6+) |
|
||||
* +---+---+-----------------------+
|
||||
* | H | Value Length (7+) |
|
||||
* +---+---------------------------+
|
||||
* | Value String (Length octets) |
|
||||
* +-------------------------------+
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | 0 | 1 | 0 |
|
||||
* +---+---+-----------------------+
|
||||
* | H | Name Length (7+) |
|
||||
* +---+---------------------------+
|
||||
* | Name String (Length octets) |
|
||||
* +---+---------------------------+
|
||||
* | H | Value Length (7+) |
|
||||
* +---+---------------------------+
|
||||
* | Value String (Length octets) |
|
||||
* +-------------------------------+
|
||||
*/
|
||||
|
||||
index = ch & ~0xc0;
|
||||
|
||||
if (index > 61) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid http2 "
|
||||
"table index: %ui", index);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy literal header: %ui", index);
|
||||
|
||||
if (index == 0) {
|
||||
state = sw_name_length;
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->index = index;
|
||||
ctx->literal = 1;
|
||||
|
||||
state = sw_value_length;
|
||||
break;
|
||||
|
||||
} else if ((ch & 0xe0) == 0x20) {
|
||||
/*
|
||||
* dynamic table size update:
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | 0 | 0 | 1 | Max size (5+) |
|
||||
* +---+---------------------------+
|
||||
*/
|
||||
|
||||
size_update = ch & ~0xe0;
|
||||
|
||||
if (size_update > 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid http2 "
|
||||
"dynamic table size update: %ui",
|
||||
size_update);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy table size update: %ui",
|
||||
size_update);
|
||||
|
||||
break;
|
||||
|
||||
} else if ((ch & 0xf0) == 0x10) {
|
||||
/*
|
||||
* literal header field never indexed:
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | 0 | 0 | 0 | 1 | Index (4+) |
|
||||
* +---+---+-----------------------+
|
||||
* | H | Value Length (7+) |
|
||||
* +---+---------------------------+
|
||||
* | Value String (Length octets) |
|
||||
* +-------------------------------+
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | 0 | 0 | 0 | 1 | 0 |
|
||||
* +---+---+-----------------------+
|
||||
* | H | Name Length (7+) |
|
||||
* +---+---------------------------+
|
||||
* | Name String (Length octets) |
|
||||
* +---+---------------------------+
|
||||
* | H | Value Length (7+) |
|
||||
* +---+---------------------------+
|
||||
* | Value String (Length octets) |
|
||||
* +-------------------------------+
|
||||
*/
|
||||
|
||||
index = ch & ~0xf0;
|
||||
|
||||
if (index == 0x0f) {
|
||||
ctx->index = index;
|
||||
ctx->literal = 1;
|
||||
state = sw_index;
|
||||
break;
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
state = sw_name_length;
|
||||
break;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy literal header never indexed: %ui",
|
||||
index);
|
||||
|
||||
ctx->index = index;
|
||||
ctx->literal = 1;
|
||||
|
||||
state = sw_value_length;
|
||||
break;
|
||||
|
||||
} else if ((ch & 0xf0) == 0x00) {
|
||||
/*
|
||||
* literal header field without indexing:
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | 0 | 0 | 0 | 0 | Index (4+) |
|
||||
* +---+---+-----------------------+
|
||||
* | H | Value Length (7+) |
|
||||
* +---+---------------------------+
|
||||
* | Value String (Length octets) |
|
||||
* +-------------------------------+
|
||||
*
|
||||
* 0 1 2 3 4 5 6 7
|
||||
* +---+---+---+---+---+---+---+---+
|
||||
* | 0 | 0 | 0 | 0 | 0 |
|
||||
* +---+---+-----------------------+
|
||||
* | H | Name Length (7+) |
|
||||
* +---+---------------------------+
|
||||
* | Name String (Length octets) |
|
||||
* +---+---------------------------+
|
||||
* | H | Value Length (7+) |
|
||||
* +---+---------------------------+
|
||||
* | Value String (Length octets) |
|
||||
* +-------------------------------+
|
||||
*/
|
||||
|
||||
index = ch & ~0xf0;
|
||||
|
||||
if (index == 0x0f) {
|
||||
ctx->index = index;
|
||||
ctx->literal = 1;
|
||||
state = sw_index;
|
||||
break;
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
state = sw_name_length;
|
||||
break;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy literal header without indexing: %ui",
|
||||
index);
|
||||
|
||||
ctx->index = index;
|
||||
ctx->literal = 1;
|
||||
|
||||
state = sw_value_length;
|
||||
break;
|
||||
}
|
||||
|
||||
/* not reached */
|
||||
|
||||
return NGX_ERROR;
|
||||
|
||||
case sw_index:
|
||||
ctx->index = ctx->index + (ch & ~0x80);
|
||||
|
||||
if (ch & 0x80) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent http2 table index "
|
||||
"with continuation flag");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->index > 61) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid http2 "
|
||||
"table index: %ui", ctx->index);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy header index: %ui", ctx->index);
|
||||
|
||||
state = sw_value_length;
|
||||
break;
|
||||
|
||||
case sw_name_length:
|
||||
ctx->field_huffman = ch & 0x80 ? 1 : 0;
|
||||
ctx->field_length = ch & ~0x80;
|
||||
|
||||
if (ctx->field_length == 0x7f) {
|
||||
state = sw_name_length_2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->field_length == 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent zero http2 "
|
||||
"header name length");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
state = sw_name;
|
||||
break;
|
||||
|
||||
case sw_name_length_2:
|
||||
ctx->field_length += ch & ~0x80;
|
||||
|
||||
if (ch & 0x80) {
|
||||
state = sw_name_length_3;
|
||||
break;
|
||||
}
|
||||
|
||||
state = sw_name;
|
||||
break;
|
||||
|
||||
case sw_name_length_3:
|
||||
ctx->field_length += (ch & ~0x80) << 7;
|
||||
|
||||
if (ch & 0x80) {
|
||||
state = sw_name_length_4;
|
||||
break;
|
||||
}
|
||||
|
||||
state = sw_name;
|
||||
break;
|
||||
|
||||
case sw_name_length_4:
|
||||
ctx->field_length += (ch & ~0x80) << 14;
|
||||
|
||||
if (ch & 0x80) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent too large http2 "
|
||||
"header name length");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
state = sw_name;
|
||||
break;
|
||||
|
||||
case sw_name:
|
||||
ctx->name.len = ctx->field_huffman ?
|
||||
ctx->field_length * 8 / 5 : ctx->field_length;
|
||||
|
||||
ctx->name.data = ngx_pnalloc(r->pool, ctx->name.len + 1);
|
||||
if (ctx->name.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->field_end = ctx->name.data;
|
||||
ctx->field_rest = ctx->field_length;
|
||||
ctx->field_state = 0;
|
||||
|
||||
state = sw_name_bytes;
|
||||
|
||||
/* fall through */
|
||||
|
||||
case sw_name_bytes:
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy name: len:%uz h:%d last:%uz, rest:%uz",
|
||||
ctx->field_length,
|
||||
ctx->field_huffman,
|
||||
last - p,
|
||||
ctx->rest - (p - b->pos));
|
||||
|
||||
size = ngx_min(last - p, (ssize_t) ctx->field_rest);
|
||||
ctx->field_rest -= size;
|
||||
|
||||
if (ctx->field_huffman) {
|
||||
if (ngx_http_huff_decode(&ctx->field_state, p, size,
|
||||
&ctx->field_end,
|
||||
ctx->field_rest == 0,
|
||||
r->connection->log)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid encoded header");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->name.len = ctx->field_end - ctx->name.data;
|
||||
ctx->name.data[ctx->name.len] = '\0';
|
||||
|
||||
} else {
|
||||
ctx->field_end = ngx_cpymem(ctx->field_end, p, size);
|
||||
ctx->name.data[ctx->name.len] = '\0';
|
||||
}
|
||||
|
||||
p += size - 1;
|
||||
|
||||
if (ctx->field_rest == 0) {
|
||||
state = sw_value_length;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case sw_value_length:
|
||||
ctx->field_huffman = ch & 0x80 ? 1 : 0;
|
||||
ctx->field_length = ch & ~0x80;
|
||||
|
||||
if (ctx->field_length == 0x7f) {
|
||||
state = sw_value_length_2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->field_length == 0) {
|
||||
ngx_str_set(&ctx->value, "");
|
||||
goto done;
|
||||
}
|
||||
|
||||
state = sw_value;
|
||||
break;
|
||||
|
||||
case sw_value_length_2:
|
||||
ctx->field_length += ch & ~0x80;
|
||||
|
||||
if (ch & 0x80) {
|
||||
state = sw_value_length_3;
|
||||
break;
|
||||
}
|
||||
|
||||
state = sw_value;
|
||||
break;
|
||||
|
||||
case sw_value_length_3:
|
||||
ctx->field_length += (ch & ~0x80) << 7;
|
||||
|
||||
if (ch & 0x80) {
|
||||
state = sw_value_length_4;
|
||||
break;
|
||||
}
|
||||
|
||||
state = sw_value;
|
||||
break;
|
||||
|
||||
case sw_value_length_4:
|
||||
ctx->field_length += (ch & ~0x80) << 14;
|
||||
|
||||
if (ch & 0x80) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent too large http2 "
|
||||
"header value length");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
state = sw_value;
|
||||
break;
|
||||
|
||||
case sw_value:
|
||||
ctx->value.len = ctx->field_huffman ?
|
||||
ctx->field_length * 8 / 5 : ctx->field_length;
|
||||
|
||||
ctx->value.data = ngx_pnalloc(r->pool, ctx->value.len + 1);
|
||||
if (ctx->value.data == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->field_end = ctx->value.data;
|
||||
ctx->field_rest = ctx->field_length;
|
||||
ctx->field_state = 0;
|
||||
|
||||
state = sw_value_bytes;
|
||||
|
||||
/* fall through */
|
||||
|
||||
case sw_value_bytes:
|
||||
|
||||
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy value: len:%uz h:%d last:%uz, rest:%uz",
|
||||
ctx->field_length,
|
||||
ctx->field_huffman,
|
||||
last - p,
|
||||
ctx->rest - (p - b->pos));
|
||||
|
||||
size = ngx_min(last - p, (ssize_t) ctx->field_rest);
|
||||
ctx->field_rest -= size;
|
||||
|
||||
if (ctx->field_huffman) {
|
||||
if (ngx_http_huff_decode(&ctx->field_state, p, size,
|
||||
&ctx->field_end,
|
||||
ctx->field_rest == 0,
|
||||
r->connection->log)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid encoded header");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->value.len = ctx->field_end - ctx->value.data;
|
||||
ctx->value.data[ctx->value.len] = '\0';
|
||||
|
||||
} else {
|
||||
ctx->field_end = ngx_cpymem(ctx->field_end, p, size);
|
||||
ctx->value.data[ctx->value.len] = '\0';
|
||||
}
|
||||
|
||||
p += size - 1;
|
||||
|
||||
if (ctx->field_rest == 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
done:
|
||||
|
||||
p++;
|
||||
ctx->rest -= p - b->pos;
|
||||
ctx->fragment_state = sw_start;
|
||||
b->pos = p;
|
||||
|
||||
if (ctx->index) {
|
||||
ctx->name = *ngx_http_v2_get_static_name(ctx->index);
|
||||
}
|
||||
|
||||
if (ctx->index && !ctx->literal) {
|
||||
ctx->value = *ngx_http_v2_get_static_value(ctx->index);
|
||||
}
|
||||
|
||||
if (!ctx->index) {
|
||||
if (ngx_http_proxy_v2_validate_header_name(r, &ctx->name)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid header: \"%V: %V\"",
|
||||
&ctx->name, &ctx->value);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx->index || ctx->literal) {
|
||||
if (ngx_http_proxy_v2_validate_header_value(r, &ctx->value)
|
||||
!= NGX_OK)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent invalid header: \"%V: %V\"",
|
||||
&ctx->name, &ctx->value);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
ctx->rest -= p - b->pos;
|
||||
ctx->fragment_state = state;
|
||||
b->pos = p;
|
||||
|
||||
if (ctx->rest > ctx->padding) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
return NGX_DONE;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_validate_header_name(ngx_http_request_t *r, ngx_str_t *s)
|
||||
{
|
||||
u_char ch;
|
||||
ngx_uint_t i;
|
||||
|
||||
for (i = 0; i < s->len; i++) {
|
||||
ch = s->data[i];
|
||||
|
||||
if (ch == ':' && i > 0) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ch <= 0x20 || ch == 0x7f) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_validate_header_value(ngx_http_request_t *r, ngx_str_t *s)
|
||||
{
|
||||
u_char ch;
|
||||
ngx_uint_t i;
|
||||
|
||||
for (i = 0; i < s->len; i++) {
|
||||
ch = s->data[i];
|
||||
|
||||
if (ch == '\0' || ch == CR || ch == LF) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_parse_rst_stream(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
u_char ch, *p, *last;
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_error_2,
|
||||
sw_error_3,
|
||||
sw_error_4
|
||||
} state;
|
||||
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest) {
|
||||
last = b->last;
|
||||
|
||||
} else {
|
||||
last = b->pos + ctx->rest;
|
||||
}
|
||||
|
||||
state = ctx->frame_state;
|
||||
|
||||
if (state == sw_start) {
|
||||
if (ctx->rest != 4) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent rst stream frame "
|
||||
"with invalid length: %uz",
|
||||
ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (p = b->pos; p < last; p++) {
|
||||
ch = *p;
|
||||
|
||||
#if 0
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy rst byte: %02Xd s:%d", ch, state);
|
||||
#endif
|
||||
|
||||
switch (state) {
|
||||
|
||||
case sw_start:
|
||||
ctx->error = (ngx_uint_t) ch << 24;
|
||||
state = sw_error_2;
|
||||
break;
|
||||
|
||||
case sw_error_2:
|
||||
ctx->error |= ch << 16;
|
||||
state = sw_error_3;
|
||||
break;
|
||||
|
||||
case sw_error_3:
|
||||
ctx->error |= ch << 8;
|
||||
state = sw_error_4;
|
||||
break;
|
||||
|
||||
case sw_error_4:
|
||||
ctx->error |= ch;
|
||||
state = sw_start;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy error: %ui", ctx->error);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->rest -= p - b->pos;
|
||||
ctx->frame_state = state;
|
||||
b->pos = p;
|
||||
|
||||
if (ctx->rest > 0) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_parse_goaway(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
u_char ch, *p, *last;
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_last_stream_id_2,
|
||||
sw_last_stream_id_3,
|
||||
sw_last_stream_id_4,
|
||||
sw_error,
|
||||
sw_error_2,
|
||||
sw_error_3,
|
||||
sw_error_4,
|
||||
sw_debug
|
||||
} state;
|
||||
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest) {
|
||||
last = b->last;
|
||||
|
||||
} else {
|
||||
last = b->pos + ctx->rest;
|
||||
}
|
||||
|
||||
state = ctx->frame_state;
|
||||
|
||||
if (state == sw_start) {
|
||||
|
||||
if (ctx->stream_id) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent goaway frame "
|
||||
"with non-zero stream id: %ui",
|
||||
ctx->stream_id);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->rest < 8) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent goaway frame "
|
||||
"with invalid length: %uz",
|
||||
ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (p = b->pos; p < last; p++) {
|
||||
ch = *p;
|
||||
|
||||
#if 0
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy goaway byte: %02Xd s:%d", ch, state);
|
||||
#endif
|
||||
|
||||
switch (state) {
|
||||
|
||||
case sw_start:
|
||||
ctx->stream_id = (ch & 0x7f) << 24;
|
||||
state = sw_last_stream_id_2;
|
||||
break;
|
||||
|
||||
case sw_last_stream_id_2:
|
||||
ctx->stream_id |= ch << 16;
|
||||
state = sw_last_stream_id_3;
|
||||
break;
|
||||
|
||||
case sw_last_stream_id_3:
|
||||
ctx->stream_id |= ch << 8;
|
||||
state = sw_last_stream_id_4;
|
||||
break;
|
||||
|
||||
case sw_last_stream_id_4:
|
||||
ctx->stream_id |= ch;
|
||||
state = sw_error;
|
||||
break;
|
||||
|
||||
case sw_error:
|
||||
ctx->error = (ngx_uint_t) ch << 24;
|
||||
state = sw_error_2;
|
||||
break;
|
||||
|
||||
case sw_error_2:
|
||||
ctx->error |= ch << 16;
|
||||
state = sw_error_3;
|
||||
break;
|
||||
|
||||
case sw_error_3:
|
||||
ctx->error |= ch << 8;
|
||||
state = sw_error_4;
|
||||
break;
|
||||
|
||||
case sw_error_4:
|
||||
ctx->error |= ch;
|
||||
state = sw_debug;
|
||||
break;
|
||||
|
||||
case sw_debug:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->rest -= p - b->pos;
|
||||
ctx->frame_state = state;
|
||||
b->pos = p;
|
||||
|
||||
if (ctx->rest > 0) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy goaway: %ui, stream %ui",
|
||||
ctx->error, ctx->stream_id);
|
||||
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_parse_window_update(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
u_char ch, *p, *last;
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_size_2,
|
||||
sw_size_3,
|
||||
sw_size_4
|
||||
} state;
|
||||
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest) {
|
||||
last = b->last;
|
||||
|
||||
} else {
|
||||
last = b->pos + ctx->rest;
|
||||
}
|
||||
|
||||
state = ctx->frame_state;
|
||||
|
||||
if (state == sw_start) {
|
||||
if (ctx->rest != 4) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent window update frame "
|
||||
"with invalid length: %uz",
|
||||
ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (p = b->pos; p < last; p++) {
|
||||
ch = *p;
|
||||
|
||||
#if 0
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy window update byte: %02Xd s:%d", ch, state);
|
||||
#endif
|
||||
|
||||
switch (state) {
|
||||
|
||||
case sw_start:
|
||||
ctx->window_update = (ch & 0x7f) << 24;
|
||||
state = sw_size_2;
|
||||
break;
|
||||
|
||||
case sw_size_2:
|
||||
ctx->window_update |= ch << 16;
|
||||
state = sw_size_3;
|
||||
break;
|
||||
|
||||
case sw_size_3:
|
||||
ctx->window_update |= ch << 8;
|
||||
state = sw_size_4;
|
||||
break;
|
||||
|
||||
case sw_size_4:
|
||||
ctx->window_update |= ch;
|
||||
state = sw_start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->rest -= p - b->pos;
|
||||
ctx->frame_state = state;
|
||||
b->pos = p;
|
||||
|
||||
if (ctx->rest > 0) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy window update: %ui", ctx->window_update);
|
||||
|
||||
if (ctx->stream_id) {
|
||||
|
||||
if (ctx->window_update > (size_t) NGX_HTTP_V2_MAX_WINDOW
|
||||
- ctx->send_window)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent too large window update");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->send_window += ctx->window_update;
|
||||
|
||||
} else {
|
||||
|
||||
if (ctx->window_update > NGX_HTTP_V2_MAX_WINDOW
|
||||
- ctx->connection->send_window)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent too large window update");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->connection->send_window += ctx->window_update;
|
||||
}
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_parse_settings(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
u_char ch, *p, *last;
|
||||
ssize_t window_update;
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_id,
|
||||
sw_id_2,
|
||||
sw_value,
|
||||
sw_value_2,
|
||||
sw_value_3,
|
||||
sw_value_4
|
||||
} state;
|
||||
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest) {
|
||||
last = b->last;
|
||||
|
||||
} else {
|
||||
last = b->pos + ctx->rest;
|
||||
}
|
||||
|
||||
state = ctx->frame_state;
|
||||
|
||||
if (state == sw_start) {
|
||||
|
||||
if (ctx->stream_id) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent settings frame "
|
||||
"with non-zero stream id: %ui",
|
||||
ctx->stream_id);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) {
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy settings ack");
|
||||
|
||||
if (ctx->rest != 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent settings frame "
|
||||
"with ack flag and non-zero length: %uz",
|
||||
ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
if (ctx->rest % 6 != 0) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent settings frame "
|
||||
"with invalid length: %uz",
|
||||
ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->free == NULL && ctx->settings++ > 1000) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent too many settings frames");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (p = b->pos; p < last; p++) {
|
||||
ch = *p;
|
||||
|
||||
#if 0
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy settings byte: %02Xd s:%d", ch, state);
|
||||
#endif
|
||||
|
||||
switch (state) {
|
||||
|
||||
case sw_start:
|
||||
case sw_id:
|
||||
ctx->setting_id = ch << 8;
|
||||
state = sw_id_2;
|
||||
break;
|
||||
|
||||
case sw_id_2:
|
||||
ctx->setting_id |= ch;
|
||||
state = sw_value;
|
||||
break;
|
||||
|
||||
case sw_value:
|
||||
ctx->setting_value = (ngx_uint_t) ch << 24;
|
||||
state = sw_value_2;
|
||||
break;
|
||||
|
||||
case sw_value_2:
|
||||
ctx->setting_value |= ch << 16;
|
||||
state = sw_value_3;
|
||||
break;
|
||||
|
||||
case sw_value_3:
|
||||
ctx->setting_value |= ch << 8;
|
||||
state = sw_value_4;
|
||||
break;
|
||||
|
||||
case sw_value_4:
|
||||
ctx->setting_value |= ch;
|
||||
state = sw_id;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy setting: %ui %ui",
|
||||
ctx->setting_id, ctx->setting_value);
|
||||
|
||||
/*
|
||||
* The following settings are defined by the protocol:
|
||||
*
|
||||
* SETTINGS_HEADER_TABLE_SIZE, SETTINGS_ENABLE_PUSH,
|
||||
* SETTINGS_MAX_CONCURRENT_STREAMS, SETTINGS_INITIAL_WINDOW_SIZE,
|
||||
* SETTINGS_MAX_FRAME_SIZE, SETTINGS_MAX_HEADER_LIST_SIZE
|
||||
*
|
||||
* Only SETTINGS_INITIAL_WINDOW_SIZE seems to be needed in
|
||||
* a simple client.
|
||||
*/
|
||||
|
||||
if (ctx->setting_id == 0x04) {
|
||||
/* SETTINGS_INITIAL_WINDOW_SIZE */
|
||||
|
||||
if (ctx->setting_value > NGX_HTTP_V2_MAX_WINDOW) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent settings frame "
|
||||
"with too large initial window size: %ui",
|
||||
ctx->setting_value);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
window_update = ctx->setting_value
|
||||
- ctx->connection->init_window;
|
||||
ctx->connection->init_window = ctx->setting_value;
|
||||
|
||||
if (ctx->send_window > 0
|
||||
&& window_update > (ssize_t) NGX_HTTP_V2_MAX_WINDOW
|
||||
- ctx->send_window)
|
||||
{
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent settings frame "
|
||||
"with too large initial window size: %ui",
|
||||
ctx->setting_value);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->send_window += window_update;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->rest -= p - b->pos;
|
||||
ctx->frame_state = state;
|
||||
b->pos = p;
|
||||
|
||||
if (ctx->rest > 0) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
return ngx_http_proxy_v2_send_settings_ack(r, ctx);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_parse_ping(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_buf_t *b)
|
||||
{
|
||||
u_char ch, *p, *last;
|
||||
enum {
|
||||
sw_start = 0,
|
||||
sw_data_2,
|
||||
sw_data_3,
|
||||
sw_data_4,
|
||||
sw_data_5,
|
||||
sw_data_6,
|
||||
sw_data_7,
|
||||
sw_data_8
|
||||
} state;
|
||||
|
||||
if (b->last - b->pos < (ssize_t) ctx->rest) {
|
||||
last = b->last;
|
||||
|
||||
} else {
|
||||
last = b->pos + ctx->rest;
|
||||
}
|
||||
|
||||
state = ctx->frame_state;
|
||||
|
||||
if (state == sw_start) {
|
||||
|
||||
if (ctx->stream_id) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent ping frame "
|
||||
"with non-zero stream id: %ui",
|
||||
ctx->stream_id);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->rest != 8) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent ping frame "
|
||||
"with invalid length: %uz",
|
||||
ctx->rest);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent ping frame with ack flag");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
if (ctx->free == NULL && ctx->pings++ > 1000) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"upstream sent too many ping frames");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
for (p = b->pos; p < last; p++) {
|
||||
ch = *p;
|
||||
|
||||
#if 0
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy ping byte: %02Xd s:%d", ch, state);
|
||||
#endif
|
||||
|
||||
if (state < sw_data_8) {
|
||||
ctx->ping_data[state] = ch;
|
||||
state++;
|
||||
|
||||
} else {
|
||||
ctx->ping_data[7] = ch;
|
||||
state = sw_start;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy ping");
|
||||
}
|
||||
}
|
||||
|
||||
ctx->rest -= p - b->pos;
|
||||
ctx->frame_state = state;
|
||||
b->pos = p;
|
||||
|
||||
if (ctx->rest > 0) {
|
||||
return NGX_AGAIN;
|
||||
}
|
||||
|
||||
ctx->state = ngx_http_proxy_v2_st_start;
|
||||
|
||||
return ngx_http_proxy_v2_send_ping_ack(r, ctx);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_send_settings_ack(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx)
|
||||
{
|
||||
ngx_chain_t *cl, **ll;
|
||||
ngx_http_proxy_v2_frame_t *f;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy send settings ack");
|
||||
|
||||
for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
cl = ngx_http_proxy_v2_get_buf(r, ctx);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
f = (ngx_http_proxy_v2_frame_t *) cl->buf->last;
|
||||
cl->buf->last += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
f->length_0 = 0;
|
||||
f->length_1 = 0;
|
||||
f->length_2 = 0;
|
||||
f->type = NGX_HTTP_V2_SETTINGS_FRAME;
|
||||
f->flags = NGX_HTTP_V2_ACK_FLAG;
|
||||
f->stream_id_0 = 0;
|
||||
f->stream_id_1 = 0;
|
||||
f->stream_id_2 = 0;
|
||||
f->stream_id_3 = 0;
|
||||
|
||||
*ll = cl;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_send_ping_ack(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx)
|
||||
{
|
||||
ngx_chain_t *cl, **ll;
|
||||
ngx_http_proxy_v2_frame_t *f;
|
||||
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy send ping ack");
|
||||
|
||||
for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
cl = ngx_http_proxy_v2_get_buf(r, ctx);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
f = (ngx_http_proxy_v2_frame_t *) cl->buf->last;
|
||||
cl->buf->last += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
f->length_0 = 0;
|
||||
f->length_1 = 0;
|
||||
f->length_2 = 8;
|
||||
f->type = NGX_HTTP_V2_PING_FRAME;
|
||||
f->flags = NGX_HTTP_V2_ACK_FLAG;
|
||||
f->stream_id_0 = 0;
|
||||
f->stream_id_1 = 0;
|
||||
f->stream_id_2 = 0;
|
||||
f->stream_id_3 = 0;
|
||||
|
||||
cl->buf->last = ngx_copy(cl->buf->last, ctx->ping_data, 8);
|
||||
|
||||
*ll = cl;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_send_window_update(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx)
|
||||
{
|
||||
size_t n;
|
||||
ngx_chain_t *cl, **ll;
|
||||
ngx_http_proxy_v2_frame_t *f;
|
||||
|
||||
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"http proxy send window update: %uz %uz",
|
||||
ctx->connection->recv_window, ctx->recv_window);
|
||||
|
||||
for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {
|
||||
ll = &cl->next;
|
||||
}
|
||||
|
||||
cl = ngx_http_proxy_v2_get_buf(r, ctx);
|
||||
if (cl == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
f = (ngx_http_proxy_v2_frame_t *) cl->buf->last;
|
||||
cl->buf->last += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
f->length_0 = 0;
|
||||
f->length_1 = 0;
|
||||
f->length_2 = 4;
|
||||
f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME;
|
||||
f->flags = 0;
|
||||
f->stream_id_0 = 0;
|
||||
f->stream_id_1 = 0;
|
||||
f->stream_id_2 = 0;
|
||||
f->stream_id_3 = 0;
|
||||
|
||||
n = NGX_HTTP_V2_MAX_WINDOW - ctx->connection->recv_window;
|
||||
ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW;
|
||||
|
||||
*cl->buf->last++ = (u_char) ((n >> 24) & 0xff);
|
||||
*cl->buf->last++ = (u_char) ((n >> 16) & 0xff);
|
||||
*cl->buf->last++ = (u_char) ((n >> 8) & 0xff);
|
||||
*cl->buf->last++ = (u_char) (n & 0xff);
|
||||
|
||||
f = (ngx_http_proxy_v2_frame_t *) cl->buf->last;
|
||||
cl->buf->last += sizeof(ngx_http_proxy_v2_frame_t);
|
||||
|
||||
f->length_0 = 0;
|
||||
f->length_1 = 0;
|
||||
f->length_2 = 4;
|
||||
f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME;
|
||||
f->flags = 0;
|
||||
f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);
|
||||
f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);
|
||||
f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);
|
||||
f->stream_id_3 = (u_char) (ctx->id & 0xff);
|
||||
|
||||
n = NGX_HTTP_V2_MAX_WINDOW - ctx->recv_window;
|
||||
ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;
|
||||
|
||||
*cl->buf->last++ = (u_char) ((n >> 24) & 0xff);
|
||||
*cl->buf->last++ = (u_char) ((n >> 16) & 0xff);
|
||||
*cl->buf->last++ = (u_char) ((n >> 8) & 0xff);
|
||||
*cl->buf->last++ = (u_char) (n & 0xff);
|
||||
|
||||
*ll = cl;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static ngx_chain_t *
|
||||
ngx_http_proxy_v2_get_buf(ngx_http_request_t *r, ngx_http_proxy_v2_ctx_t *ctx)
|
||||
{
|
||||
u_char *start;
|
||||
ngx_buf_t *b;
|
||||
ngx_chain_t *cl;
|
||||
|
||||
cl = ngx_chain_get_free_buf(r->pool, &ctx->free);
|
||||
if (cl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b = cl->buf;
|
||||
start = b->start;
|
||||
|
||||
if (start == NULL) {
|
||||
|
||||
/*
|
||||
* each buffer is large enough to hold two window update
|
||||
* frames in a row
|
||||
*/
|
||||
|
||||
start = ngx_palloc(r->pool, 2 * sizeof(ngx_http_proxy_v2_frame_t) + 8);
|
||||
if (start == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ngx_memzero(b, sizeof(ngx_buf_t));
|
||||
|
||||
b->start = start;
|
||||
b->pos = start;
|
||||
b->last = start;
|
||||
b->end = start + 2 * sizeof(ngx_http_proxy_v2_frame_t) + 8;
|
||||
|
||||
b->tag = (ngx_buf_tag_t) &ngx_http_proxy_v2_body_output_filter;
|
||||
b->temporary = 1;
|
||||
b->flush = 1;
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
static ngx_http_proxy_v2_ctx_t *
|
||||
ngx_http_proxy_v2_get_ctx(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_http_upstream_t *u;
|
||||
ngx_http_proxy_v2_ctx_t *ctx;
|
||||
|
||||
ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_v2_module);
|
||||
|
||||
if (ctx->connection == NULL) {
|
||||
u = r->upstream;
|
||||
|
||||
if (ngx_http_proxy_v2_get_connection_data(r, ctx, &u->peer) != NGX_OK) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_proxy_v2_get_connection_data(ngx_http_request_t *r,
|
||||
ngx_http_proxy_v2_ctx_t *ctx, ngx_peer_connection_t *pc)
|
||||
{
|
||||
ngx_connection_t *c;
|
||||
ngx_pool_cleanup_t *cln;
|
||||
|
||||
c = pc->connection;
|
||||
|
||||
if (c == NULL) {
|
||||
ctx->connection = ngx_palloc(r->pool, sizeof(ngx_http_proxy_v2_conn_t));
|
||||
if (ctx->connection == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (pc->cached) {
|
||||
|
||||
/*
|
||||
* for cached connections, connection data can be found
|
||||
* in the cleanup handler
|
||||
*/
|
||||
|
||||
for (cln = c->pool->cleanup; cln; cln = cln->next) {
|
||||
if (cln->handler == ngx_http_proxy_v2_cleanup) {
|
||||
ctx->connection = cln->data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->connection == NULL) {
|
||||
ngx_log_error(NGX_LOG_ERR, c->log, 0,
|
||||
"no connection data found for "
|
||||
"keepalive http2 connection");
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ctx->send_window = ctx->connection->init_window;
|
||||
ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;
|
||||
|
||||
ctx->connection->last_stream_id += 2;
|
||||
ctx->id = ctx->connection->last_stream_id;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_proxy_v2_conn_t));
|
||||
if (cln == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
cln->handler = ngx_http_proxy_v2_cleanup;
|
||||
ctx->connection = cln->data;
|
||||
|
||||
done:
|
||||
|
||||
ctx->connection->init_window = NGX_HTTP_V2_DEFAULT_WINDOW;
|
||||
ctx->connection->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;
|
||||
ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW;
|
||||
|
||||
ctx->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;
|
||||
ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;
|
||||
|
||||
ctx->id = 1;
|
||||
ctx->connection->last_stream_id = 1;
|
||||
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_proxy_v2_cleanup(void *data)
|
||||
{
|
||||
#if 0
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
||||
"http proxy cleanup");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_proxy_v2_abort_request(ngx_http_request_t *r)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"abort proxy http2 request");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ngx_http_proxy_v2_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
|
||||
{
|
||||
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
||||
"finalize proxy http2 request");
|
||||
return;
|
||||
}
|
||||
@@ -1283,6 +1283,21 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t *cf)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* set by ngx_pcalloc():
|
||||
*
|
||||
* conf->upstream.bufs.num = 0;
|
||||
* conf->upstream.ignore_headers = 0;
|
||||
* conf->upstream.next_upstream = 0;
|
||||
* conf->upstream.cache_zone = NULL;
|
||||
* conf->upstream.cache_use_stale = 0;
|
||||
* conf->upstream.cache_methods = 0;
|
||||
* conf->upstream.temp_path = NULL;
|
||||
* conf->upstream.hide_headers_hash = { NULL, 0 };
|
||||
* conf->upstream.store_lengths = NULL;
|
||||
* conf->upstream.store_values = NULL;
|
||||
*/
|
||||
|
||||
conf->upstream.store = NGX_CONF_UNSET;
|
||||
conf->upstream.store_access = NGX_CONF_UNSET_UINT;
|
||||
conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
|
||||
@@ -1528,7 +1543,7 @@ ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
|
||||
prev->upstream.temp_path,
|
||||
&ngx_http_scgi_temp_path)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
@@ -1980,6 +1995,11 @@ ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (value[1].len == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty path");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
#if (NGX_HTTP_CACHE)
|
||||
if (scf->upstream.cache > 0) {
|
||||
return "is incompatible with \"scgi_cache\"";
|
||||
|
||||
@@ -165,8 +165,8 @@ ngx_http_slice_header_filter(ngx_http_request_t *r)
|
||||
|
||||
if (cr.start != ctx->start || cr.end != end) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"unexpected range in slice response: %O-%O",
|
||||
cr.start, cr.end);
|
||||
"unexpected range in slice response: %O-%O, "
|
||||
"expected: %O-%O", cr.start, cr.end, ctx->start, end);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
@@ -419,13 +419,13 @@ ngx_http_slice_range_variable(ngx_http_request_t *r,
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
|
||||
|
||||
p = ngx_pnalloc(r->pool, sizeof("bytes=-") - 1 + 2 * NGX_OFF_T_LEN);
|
||||
if (p == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);
|
||||
|
||||
ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size);
|
||||
|
||||
ctx->range.data = p;
|
||||
|
||||
@@ -820,7 +820,7 @@ ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
||||
}
|
||||
|
||||
for (prm = cmd->params; prm->name.len; prm++) {
|
||||
if (prm->mandatory && params[prm->index] == 0) {
|
||||
if (prm->mandatory && params[prm->index] == NULL) {
|
||||
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
||||
"mandatory \"%V\" parameter is absent "
|
||||
"in \"%V\" SSI command",
|
||||
@@ -2942,7 +2942,7 @@ ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ typedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,
|
||||
|
||||
|
||||
#define NGX_DEFAULT_CIPHERS "[EECDH+ECDSA+AESGCM+AES128|EECDH+ECDSA+CHACHA20]:EECDH+ECDSA+AESGCM+AES256:EECDH+ECDSA+AES128+SHA:EECDH+ECDSA+AES256+SHA:[EECDH+aRSA+AESGCM+AES128|EECDH+aRSA+CHACHA20]:EECDH+aRSA+AESGCM+AES256:EECDH+aRSA+AES128+SHA:EECDH+aRSA+AES256+SHA"
|
||||
#define NGX_DEFAULT_ECDH_CURVE "X25519Kyber768Draft00:X25519:P-256:P-384:P-521"
|
||||
#define NGX_DEFAULT_ECDH_CURVE "X25519MLKEM768:X25519Kyber768Draft00:X25519:P-256:P-384:P-521"
|
||||
|
||||
#define NGX_HTTP_ALPN_PROTOS "\x08http/1.1\x08http/1.0\x08http/0.9"
|
||||
|
||||
@@ -43,6 +43,8 @@ static char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,
|
||||
static ngx_int_t ngx_http_ssl_compile_certificates(ngx_conf_t *cf,
|
||||
ngx_http_ssl_srv_conf_t *conf);
|
||||
|
||||
static char *ngx_http_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
void *conf);
|
||||
static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
|
||||
@@ -108,6 +110,20 @@ static ngx_command_t ngx_http_ssl_commands[] = {
|
||||
offsetof(ngx_http_ssl_srv_conf_t, certificate_keys),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_certificate_cache"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE123,
|
||||
ngx_http_ssl_certificate_cache,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_ech_file"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_array_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, ech_files),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_password_file"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_http_ssl_password_file,
|
||||
@@ -115,6 +131,13 @@ static ngx_command_t ngx_http_ssl_commands[] = {
|
||||
0,
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_certificate_compression"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
ngx_conf_set_flag_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, certificate_compression),
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_dhparam"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_str_slot,
|
||||
@@ -221,7 +244,7 @@ static ngx_command_t ngx_http_ssl_commands[] = {
|
||||
NULL },
|
||||
|
||||
{ ngx_string("ssl_ocsp"),
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
|
||||
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
|
||||
ngx_conf_set_enum_slot,
|
||||
NGX_HTTP_SRV_CONF_OFFSET,
|
||||
offsetof(ngx_http_ssl_srv_conf_t, ocsp),
|
||||
@@ -377,6 +400,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
|
||||
{ ngx_string("ssl_curves"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_sigalg"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_sigalg, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_session_id"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
@@ -393,6 +419,13 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
|
||||
{ ngx_string("ssl_alpn_protocol"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_alpn_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_ech_status"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_ech_status, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_ech_outer_server_name"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_ech_outer_server_name,
|
||||
NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_cert"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
@@ -434,6 +467,9 @@ static ngx_http_variable_t ngx_http_ssl_vars[] = {
|
||||
{ ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
{ ngx_string("ssl_client_sigalg"), NULL, ngx_http_ssl_variable,
|
||||
(uintptr_t) ngx_ssl_get_client_sigalg, NGX_HTTP_VAR_CHANGEABLE, 0 },
|
||||
|
||||
ngx_http_null_variable
|
||||
};
|
||||
|
||||
@@ -647,6 +683,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
|
||||
*/
|
||||
|
||||
sscf->prefer_server_ciphers = NGX_CONF_UNSET;
|
||||
sscf->certificate_compression = NGX_CONF_UNSET;
|
||||
sscf->early_data = NGX_CONF_UNSET;
|
||||
sscf->reject_handshake = NGX_CONF_UNSET;
|
||||
sscf->buffer_size = NGX_CONF_UNSET_SIZE;
|
||||
@@ -654,6 +691,8 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
|
||||
sscf->verify_depth = NGX_CONF_UNSET_UINT;
|
||||
sscf->certificates = NGX_CONF_UNSET_PTR;
|
||||
sscf->certificate_keys = NGX_CONF_UNSET_PTR;
|
||||
sscf->certificate_cache = NGX_CONF_UNSET_PTR;
|
||||
sscf->ech_files = NGX_CONF_UNSET_PTR;
|
||||
sscf->passwords = NGX_CONF_UNSET_PTR;
|
||||
sscf->conf_commands = NGX_CONF_UNSET_PTR;
|
||||
sscf->builtin_session_cache = NGX_CONF_UNSET;
|
||||
@@ -688,11 +727,14 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
ngx_conf_merge_value(conf->prefer_server_ciphers,
|
||||
prev->prefer_server_ciphers, 1);
|
||||
|
||||
ngx_conf_merge_value(conf->certificate_compression,
|
||||
prev->certificate_compression, 0);
|
||||
|
||||
ngx_conf_merge_value(conf->early_data, prev->early_data, 1);
|
||||
ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0);
|
||||
|
||||
ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,
|
||||
(NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3));
|
||||
(NGX_CONF_BITMASK_SET|NGX_SSL_DEFAULT_PROTOCOLS));
|
||||
|
||||
ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
|
||||
NGX_SSL_BUFSIZE);
|
||||
@@ -704,6 +746,11 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,
|
||||
NULL);
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->certificate_cache, prev->certificate_cache,
|
||||
NULL);
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->ech_files, prev->ech_files, NULL);
|
||||
|
||||
ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);
|
||||
|
||||
ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
|
||||
@@ -779,6 +826,12 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
cln->data = &conf->ssl;
|
||||
|
||||
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||
{
|
||||
static ngx_ssl_client_hello_arg cb = { ngx_http_ssl_servername };
|
||||
|
||||
if (ngx_ssl_set_client_hello_callback(&conf->ssl, &cb) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,
|
||||
ngx_http_ssl_servername)
|
||||
@@ -789,7 +842,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
"dynamically to an OpenSSL library which has no tlsext support, "
|
||||
"therefore SNI is not available");
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
|
||||
@@ -833,6 +886,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_certificate_compression(cf, &conf->ssl,
|
||||
conf->certificate_compression)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
conf->ssl.buffer_size = conf->buffer_size;
|
||||
@@ -887,6 +947,10 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_ech_files(cf, &conf->ssl, conf->ech_files) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
@@ -925,13 +989,19 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
|
||||
if (conf->stapling) {
|
||||
|
||||
if (conf->certificate_compression) {
|
||||
ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
|
||||
"\"ssl_stapling\" is incompatible with "
|
||||
"\"ssl_certificate_compression\"");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,
|
||||
&conf->stapling_responder, conf->stapling_verify)
|
||||
!= NGX_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ngx_ssl_early_data(cf, &conf->ssl, conf->early_data) != NGX_OK) {
|
||||
@@ -1056,6 +1126,99 @@ found:
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ssl_certificate_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
ngx_http_ssl_srv_conf_t *sscf = conf;
|
||||
|
||||
time_t inactive, valid;
|
||||
ngx_str_t *value, s;
|
||||
ngx_int_t max;
|
||||
ngx_uint_t i;
|
||||
|
||||
if (sscf->certificate_cache != NGX_CONF_UNSET_PTR) {
|
||||
return "is duplicate";
|
||||
}
|
||||
|
||||
value = cf->args->elts;
|
||||
|
||||
max = 0;
|
||||
inactive = 10;
|
||||
valid = 60;
|
||||
|
||||
for (i = 1; i < cf->args->nelts; i++) {
|
||||
|
||||
if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
|
||||
|
||||
max = ngx_atoi(value[i].data + 4, value[i].len - 4);
|
||||
if (max <= 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
|
||||
|
||||
s.len = value[i].len - 9;
|
||||
s.data = value[i].data + 9;
|
||||
|
||||
inactive = ngx_parse_time(&s, 1);
|
||||
if (inactive == (time_t) NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
|
||||
|
||||
s.len = value[i].len - 6;
|
||||
s.data = value[i].data + 6;
|
||||
|
||||
valid = ngx_parse_time(&s, 1);
|
||||
if (valid == (time_t) NGX_ERROR) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ngx_strcmp(value[i].data, "off") == 0) {
|
||||
|
||||
sscf->certificate_cache = NULL;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
failed:
|
||||
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"invalid parameter \"%V\"", &value[i]);
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
if (sscf->certificate_cache == NULL) {
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
if (max == 0) {
|
||||
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
|
||||
"\"ssl_certificate_cache\" must have "
|
||||
"the \"max\" parameter");
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
sscf->certificate_cache = ngx_ssl_cache_init(cf->pool, max, valid,
|
||||
inactive);
|
||||
if (sscf->certificate_cache == NULL) {
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
return NGX_CONF_OK;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef struct {
|
||||
ngx_ssl_t ssl;
|
||||
|
||||
ngx_flag_t prefer_server_ciphers;
|
||||
ngx_flag_t certificate_compression;
|
||||
ngx_flag_t early_data;
|
||||
ngx_flag_t reject_handshake;
|
||||
|
||||
@@ -38,6 +39,8 @@ typedef struct {
|
||||
ngx_array_t *certificate_values;
|
||||
ngx_array_t *certificate_key_values;
|
||||
|
||||
ngx_ssl_cache_t *certificate_cache;
|
||||
|
||||
ngx_str_t dhparam;
|
||||
ngx_str_t ecdh_curve;
|
||||
ngx_str_t client_certificate;
|
||||
@@ -46,6 +49,7 @@ typedef struct {
|
||||
|
||||
ngx_str_t ciphers;
|
||||
|
||||
ngx_array_t *ech_files;
|
||||
ngx_array_t *passwords;
|
||||
ngx_array_t *conf_commands;
|
||||
|
||||
|
||||
@@ -901,7 +901,7 @@ ngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||
if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
|
||||
&prev->types_keys, &prev->types,
|
||||
ngx_http_html_default_types)
|
||||
!= NGX_OK)
|
||||
!= NGX_CONF_OK)
|
||||
{
|
||||
return NGX_CONF_ERROR;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
ngx_http_complex_value_t key;
|
||||
#if (NGX_HTTP_UPSTREAM_ZONE)
|
||||
ngx_uint_t config;
|
||||
#endif
|
||||
ngx_http_upstream_chash_points_t *points;
|
||||
} ngx_http_upstream_hash_srv_conf_t;
|
||||
|
||||
@@ -49,6 +52,8 @@ static ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc,
|
||||
|
||||
static ngx_int_t ngx_http_upstream_init_chash(ngx_conf_t *cf,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
static ngx_int_t ngx_http_upstream_update_chash(ngx_pool_t *pool,
|
||||
ngx_http_upstream_srv_conf_t *us);
|
||||
static int ngx_libc_cdecl
|
||||
ngx_http_upstream_chash_cmp_points(const void *one, const void *two);
|
||||
static ngx_uint_t ngx_http_upstream_find_chash_point(
|
||||
@@ -178,11 +183,18 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
ngx_http_upstream_rr_peers_rlock(hp->rrp.peers);
|
||||
|
||||
if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) {
|
||||
if (hp->tries > 20 || hp->rrp.peers->number < 2 || hp->key.len == 0) {
|
||||
ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
return hp->get_rr_peer(pc, &hp->rrp);
|
||||
}
|
||||
|
||||
#if (NGX_HTTP_UPSTREAM_ZONE)
|
||||
if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) {
|
||||
ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
return hp->get_rr_peer(pc, &hp->rrp);
|
||||
}
|
||||
#endif
|
||||
|
||||
now = ngx_time();
|
||||
|
||||
pc->cached = 0;
|
||||
@@ -262,6 +274,7 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
}
|
||||
|
||||
hp->rrp.current = peer;
|
||||
ngx_http_upstream_rr_peer_ref(hp->rrp.peers, peer);
|
||||
|
||||
pc->sockaddr = peer->sockaddr;
|
||||
pc->socklen = peer->socklen;
|
||||
@@ -284,6 +297,26 @@ ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
us->peer.init = ngx_http_upstream_init_chash_peer;
|
||||
|
||||
#if (NGX_HTTP_UPSTREAM_ZONE)
|
||||
if (us->shm_zone) {
|
||||
return NGX_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ngx_http_upstream_update_chash(cf->pool, us);
|
||||
}
|
||||
|
||||
|
||||
static ngx_int_t
|
||||
ngx_http_upstream_update_chash(ngx_pool_t *pool,
|
||||
ngx_http_upstream_srv_conf_t *us)
|
||||
{
|
||||
u_char *host, *port, c;
|
||||
size_t host_len, port_len, size;
|
||||
@@ -299,25 +332,32 @@ ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
|
||||
u_char byte[4];
|
||||
} prev_hash;
|
||||
|
||||
if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);
|
||||
|
||||
us->peer.init = ngx_http_upstream_init_chash_peer;
|
||||
if (hcf->points) {
|
||||
ngx_free(hcf->points);
|
||||
hcf->points = NULL;
|
||||
}
|
||||
|
||||
peers = us->peer.data;
|
||||
npoints = peers->total_weight * 160;
|
||||
|
||||
size = sizeof(ngx_http_upstream_chash_points_t)
|
||||
+ sizeof(ngx_http_upstream_chash_point_t) * (npoints - 1);
|
||||
- sizeof(ngx_http_upstream_chash_point_t)
|
||||
+ sizeof(ngx_http_upstream_chash_point_t) * npoints;
|
||||
|
||||
points = ngx_palloc(cf->pool, size);
|
||||
points = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log);
|
||||
if (points == NULL) {
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
points->number = 0;
|
||||
|
||||
if (npoints == 0) {
|
||||
hcf->points = points;
|
||||
return NGX_OK;
|
||||
}
|
||||
|
||||
for (peer = peers->peer; peer; peer = peer->next) {
|
||||
server = &peer->server;
|
||||
|
||||
@@ -401,7 +441,6 @@ ngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
|
||||
|
||||
points->number = i + 1;
|
||||
|
||||
hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);
|
||||
hcf->points = points;
|
||||
|
||||
return NGX_OK;
|
||||
@@ -481,7 +520,22 @@ ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,
|
||||
|
||||
ngx_http_upstream_rr_peers_rlock(hp->rrp.peers);
|
||||
|
||||
hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash);
|
||||
#if (NGX_HTTP_UPSTREAM_ZONE)
|
||||
if (hp->rrp.peers->config
|
||||
&& (hcf->points == NULL || hcf->config != *hp->rrp.peers->config))
|
||||
{
|
||||
if (ngx_http_upstream_update_chash(NULL, us) != NGX_OK) {
|
||||
ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
return NGX_ERROR;
|
||||
}
|
||||
|
||||
hcf->config = *hp->rrp.peers->config;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (hcf->points->number) {
|
||||
hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash);
|
||||
}
|
||||
|
||||
ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
|
||||
@@ -517,6 +571,20 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
pc->cached = 0;
|
||||
pc->connection = NULL;
|
||||
|
||||
if (hp->rrp.peers->number == 0) {
|
||||
pc->name = hp->rrp.peers->name;
|
||||
ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
return NGX_BUSY;
|
||||
}
|
||||
|
||||
#if (NGX_HTTP_UPSTREAM_ZONE)
|
||||
if (hp->rrp.peers->config && hp->rrp.config != *hp->rrp.peers->config) {
|
||||
pc->name = hp->rrp.peers->name;
|
||||
ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
|
||||
return NGX_BUSY;
|
||||
}
|
||||
#endif
|
||||
|
||||
now = ngx_time();
|
||||
hcf = hp->conf;
|
||||
|
||||
@@ -597,6 +665,7 @@ ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)
|
||||
found:
|
||||
|
||||
hp->rrp.current = best;
|
||||
ngx_http_upstream_rr_peer_ref(hp->rrp.peers, best);
|
||||
|
||||
pc->sockaddr = best->sockaddr;
|
||||
pc->socklen = best->socklen;
|
||||
@@ -664,6 +733,7 @@ ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
|
||||
}
|
||||
|
||||
uscf->flags = NGX_HTTP_UPSTREAM_CREATE
|
||||
|NGX_HTTP_UPSTREAM_MODIFY
|
||||
|NGX_HTTP_UPSTREAM_WEIGHT
|
||||
|NGX_HTTP_UPSTREAM_MAX_CONNS
|
||||
|NGX_HTTP_UPSTREAM_MAX_FAILS
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user