changeset 8458:e0f92f68e018 quic

QUIC: Introduced ngx_quic_finalize_connection(). The function finalizes QUIC connection with an application protocol error code and sends a CONNECTION_CLOSE frame with type=0x1d. Also, renamed NGX_QUIC_FT_CONNECTION_CLOSE2 to NGX_QUIC_FT_CONNECTION_CLOSE_APP.
author Roman Arutyunyan <arut@nginx.com>
date Thu, 02 Jul 2020 16:33:59 +0300
parents a7f64438aa3c
children 1ed698947172
files src/event/ngx_event_quic.c src/event/ngx_event_quic.h src/event/ngx_event_quic_transport.c src/event/ngx_event_quic_transport.h
diffstat 4 files changed, 95 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -110,9 +110,11 @@ struct ngx_quic_connection_s {
     uint64_t                          max_streams;
 
     ngx_uint_t                        error;
+    enum ssl_encryption_level_t       error_level;
     ngx_uint_t                        error_ftype;
     const char                       *error_reason;
 
+    unsigned                          error_app:1;
     unsigned                          send_timer_set:1;
     unsigned                          closing:1;
     unsigned                          draining:1;
@@ -181,9 +183,7 @@ static ngx_int_t ngx_quic_app_input(ngx_
 static ngx_int_t ngx_quic_payload_handler(ngx_connection_t *c,
     ngx_quic_header_t *pkt);
 static ngx_int_t ngx_quic_send_ack(ngx_connection_t *c, ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_send_cc(ngx_connection_t *c,
-    enum ssl_encryption_level_t level, ngx_uint_t err, ngx_uint_t frame_type,
-    const char *reason);
+static ngx_int_t ngx_quic_send_cc(ngx_connection_t *c);
 static ngx_int_t ngx_quic_send_new_token(ngx_connection_t *c);
 
 static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
@@ -540,7 +540,8 @@ static int
 ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
     uint8_t alert)
 {
-    ngx_connection_t  *c;
+    ngx_connection_t       *c;
+    ngx_quic_connection_t  *qc;
 
     c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
 
@@ -548,13 +549,18 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_
                    "quic ngx_quic_send_alert(), lvl=%d, alert=%d",
                    (int) level, (int) alert);
 
-    if (c->quic == NULL) {
+    qc = c->quic;
+    if (qc == NULL) {
         return 1;
     }
 
-    if (ngx_quic_send_cc(c, level, NGX_QUIC_ERR_CRYPTO(alert), 0, "TLS alert")
-        != NGX_OK)
-    {
+    qc->error_level = level;
+    qc->error = NGX_QUIC_ERR_CRYPTO(alert);
+    qc->error_reason = "TLS alert";
+    qc->error_app = 0;
+    qc->error_ftype = 0;
+
+    if (ngx_quic_send_cc(c) != NGX_OK) {
         return 0;
     }
 
@@ -1262,10 +1268,9 @@ ngx_quic_close_connection(ngx_connection
 static ngx_int_t
 ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc)
 {
-    ngx_uint_t                    i, err;
-    ngx_quic_send_ctx_t          *ctx;
-    ngx_quic_connection_t        *qc;
-    enum ssl_encryption_level_t   level;
+    ngx_uint_t              i;
+    ngx_quic_send_ctx_t    *ctx;
+    ngx_quic_connection_t  *qc;
 
     qc = c->quic;
 
@@ -1311,27 +1316,28 @@ ngx_quic_close_quic(ngx_connection_t *c,
 
                 ngx_add_timer(&qc->close, 3 * NGX_QUIC_HARDCODED_PTO);
 
-                err = NGX_QUIC_ERR_NO_ERROR;
+                qc->error = NGX_QUIC_ERR_NO_ERROR;
 
             } else {
-                err = qc->error ? qc->error : NGX_QUIC_ERR_INTERNAL_ERROR;
-
-                ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                               "quic immediate close due to error: %ui %s",
-                               qc->error,
+                if (qc->error == 0 && !qc->error_app) {
+                    qc->error = NGX_QUIC_ERR_INTERNAL_ERROR;
+                }
+
+                ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                               "quic immediate close due to %serror: %ui %s",
+                               qc->error_app ? "app " : "", qc->error,
                                qc->error_reason ? qc->error_reason : "");
             }
 
-            level = c->ssl ? SSL_quic_read_level(c->ssl->connection)
-                           : ssl_encryption_initial;
-
-            (void) ngx_quic_send_cc(c, level, err, qc->error_ftype,
-                                    qc->error_reason);
-
-            if (level == ssl_encryption_handshake) {
+            qc->error_level = c->ssl ? SSL_quic_read_level(c->ssl->connection)
+                                     : ssl_encryption_initial;
+
+            (void) ngx_quic_send_cc(c);
+
+            if (qc->error_level == ssl_encryption_handshake) {
                 /* for clients that might not have handshake keys */
-                (void) ngx_quic_send_cc(c, ssl_encryption_initial, err,
-                                        qc->error_ftype, qc->error_reason);
+                qc->error_level = ssl_encryption_initial;
+                (void) ngx_quic_send_cc(c);
             }
         }
 
@@ -1382,6 +1388,22 @@ ngx_quic_close_quic(ngx_connection_t *c,
 }
 
 
+void
+ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t err,
+    const char *reason)
+{
+    ngx_quic_connection_t  *qc;
+
+    qc = c->quic;
+    qc->error = err;
+    qc->error_reason = reason;
+    qc->error_app = 1;
+    qc->error_ftype = 0;
+
+    ngx_quic_close_connection(c, NGX_ERROR);
+}
+
+
 static void
 ngx_quic_close_timer_handler(ngx_event_t *ev)
 {
@@ -1887,8 +1909,14 @@ ngx_quic_payload_handler(ngx_connection_
          *  a packet containing a CONNECTION_CLOSE frame and to identify
          *  packets as belonging to the connection.
          */
-        return ngx_quic_send_cc(c, pkt->level, NGX_QUIC_ERR_NO_ERROR, 0,
-                                "connection is closing, packet discarded");
+
+        qc->error_level = pkt->level;
+        qc->error = NGX_QUIC_ERR_NO_ERROR;
+        qc->error_reason = "connection is closing, packet discarded";
+        qc->error_ftype = 0;
+        qc->error_app = 0;
+
+        return ngx_quic_send_cc(c);
     }
 
     p = pkt->payload.data;
@@ -1926,7 +1954,7 @@ ngx_quic_payload_handler(ngx_connection_
             continue;
 
         case NGX_QUIC_FT_CONNECTION_CLOSE:
-        case NGX_QUIC_FT_CONNECTION_CLOSE2:
+        case NGX_QUIC_FT_CONNECTION_CLOSE_APP:
             do_close = 1;
             continue;
         }
@@ -2098,8 +2126,7 @@ ngx_quic_send_ack(ngx_connection_t *c, n
 
 
 static ngx_int_t
-ngx_quic_send_cc(ngx_connection_t *c, enum ssl_encryption_level_t level,
-    ngx_uint_t err, ngx_uint_t frame_type, const char *reason)
+ngx_quic_send_cc(ngx_connection_t *c)
 {
     ngx_quic_frame_t       *frame;
     ngx_quic_connection_t  *qc;
@@ -2122,19 +2149,21 @@ ngx_quic_send_cc(ngx_connection_t *c, en
         return NGX_ERROR;
     }
 
-    frame->level = level;
+    frame->level = qc->error_level;
     frame->type = NGX_QUIC_FT_CONNECTION_CLOSE;
-    frame->u.close.error_code = err;
-    frame->u.close.frame_type = frame_type;
-
-    if (reason) {
-        frame->u.close.reason.len = ngx_strlen(reason);
-        frame->u.close.reason.data = (u_char *) reason;
+    frame->u.close.error_code = qc->error;
+    frame->u.close.frame_type = qc->error_ftype;
+    frame->u.close.app = qc->error_app;
+
+    if (qc->error_reason) {
+        frame->u.close.reason.len = ngx_strlen(qc->error_reason);
+        frame->u.close.reason.data = (u_char *) qc->error_reason;
     }
 
     ngx_snprintf(frame->info, sizeof(frame->info) - 1,
-                 "cc from send_cc err=%ui level=%d ft=%ui reason \"%s\"",
-                 err, level, frame_type, reason ? reason : "-");
+                 "CONNECTION_CLOSE%s err:%ui level:%d ft:%ui reason:\"%s\"",
+                 qc->error_app ? "_APP" : "", qc->error, qc->error_level,
+                 qc->error_ftype, qc->error_reason ? qc->error_reason : "-");
 
     ngx_quic_queue_frame(c->quic, frame);
 
--- a/src/event/ngx_event_quic.h
+++ b/src/event/ngx_event_quic.h
@@ -103,6 +103,8 @@ struct ngx_quic_stream_s {
 void ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
     ngx_connection_handler_pt handler);
 ngx_connection_t *ngx_quic_create_uni_stream(ngx_connection_t *c);
+void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t err,
+    const char *reason);
 
 
 /********************************* DEBUG *************************************/
--- a/src/event/ngx_event_quic_transport.c
+++ b/src/event/ngx_event_quic_transport.c
@@ -726,10 +726,8 @@ ngx_quic_parse_frame(ngx_quic_header_t *
                        f->u.ncid.seqnum, f->u.ncid.retire, f->u.ncid.len);
         break;
 
-    case NGX_QUIC_FT_CONNECTION_CLOSE2:
-        /* fall through */
-
     case NGX_QUIC_FT_CONNECTION_CLOSE:
+    case NGX_QUIC_FT_CONNECTION_CLOSE_APP:
 
         p = ngx_quic_parse_int(p, end, &f->u.close.error_code);
         if (p == NULL) {
@@ -767,7 +765,7 @@ ngx_quic_parse_frame(ngx_quic_header_t *
         } else {
 
             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
-                          "quic frame in: CONNECTION_CLOSE2:"
+                          "quic frame in: CONNECTION_CLOSE_APP:"
                           " code:0x%xi reason:'%V'",
                            f->u.close.error_code, &f->u.close.reason);
         }
@@ -1174,6 +1172,7 @@ ngx_quic_create_frame(u_char *p, ngx_qui
         return ngx_quic_create_stream(p, &f->u.stream);
 
     case NGX_QUIC_FT_CONNECTION_CLOSE:
+    case NGX_QUIC_FT_CONNECTION_CLOSE_APP:
         f->need_ack = 0;
         return ngx_quic_create_close(p, &f->u.close);
 
@@ -1742,13 +1741,21 @@ ngx_quic_create_transport_params(u_char 
 static size_t
 ngx_quic_create_close(u_char *p, ngx_quic_close_frame_t *cl)
 {
-    size_t   len;
-    u_char  *start;
+    size_t       len;
+    u_char      *start;
+    ngx_uint_t   type;
+
+    type = cl->app ? NGX_QUIC_FT_CONNECTION_CLOSE_APP
+                   : NGX_QUIC_FT_CONNECTION_CLOSE;
 
     if (p == NULL) {
-        len = ngx_quic_varint_len(NGX_QUIC_FT_CONNECTION_CLOSE);
+        len = ngx_quic_varint_len(type);
         len += ngx_quic_varint_len(cl->error_code);
-        len += ngx_quic_varint_len(cl->frame_type);
+
+        if (!cl->app) {
+            len += ngx_quic_varint_len(cl->frame_type);
+        }
+
         len += ngx_quic_varint_len(cl->reason.len);
         len += cl->reason.len;
 
@@ -1757,9 +1764,13 @@ ngx_quic_create_close(u_char *p, ngx_qui
 
     start = p;
 
-    ngx_quic_build_int(&p, NGX_QUIC_FT_CONNECTION_CLOSE);
+    ngx_quic_build_int(&p, type);
     ngx_quic_build_int(&p, cl->error_code);
-    ngx_quic_build_int(&p, cl->frame_type);
+
+    if (!cl->app) {
+        ngx_quic_build_int(&p, cl->frame_type);
+    }
+
     ngx_quic_build_int(&p, cl->reason.len);
     p = ngx_cpymem(p, cl->reason.data, cl->reason.len);
 
--- a/src/event/ngx_event_quic_transport.h
+++ b/src/event/ngx_event_quic_transport.h
@@ -71,7 +71,7 @@
 #define NGX_QUIC_FT_PATH_CHALLENGE                       0x1A
 #define NGX_QUIC_FT_PATH_RESPONSE                        0x1B
 #define NGX_QUIC_FT_CONNECTION_CLOSE                     0x1C
-#define NGX_QUIC_FT_CONNECTION_CLOSE2                    0x1D
+#define NGX_QUIC_FT_CONNECTION_CLOSE_APP                 0x1D
 #define NGX_QUIC_FT_HANDSHAKE_DONE                       0x1E
 
 /* 22.4.  QUIC Transport Error Codes Registry */
@@ -185,6 +185,7 @@ typedef struct {
     uint64_t                                    error_code;
     uint64_t                                    frame_type;
     ngx_str_t                                   reason;
+    ngx_uint_t                                  app;  /* unsigned  app:1; */
 } ngx_quic_close_frame_t;