diff options
Diffstat (limited to '')
| -rw-r--r-- | src/event/ngx_event_openssl.c | 519 | ||||
| -rw-r--r-- | src/event/ngx_event_openssl.h | 55 |
2 files changed, 337 insertions, 237 deletions
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c index b7e072571..246047869 100644 --- a/src/event/ngx_event_openssl.c +++ b/src/event/ngx_event_openssl.c @@ -14,9 +14,12 @@ typedef struct { } ngx_openssl_conf_t; +static void ngx_ssl_handshake_handler(ngx_event_t *ev); static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n); static void ngx_ssl_write_handler(ngx_event_t *wev); static void ngx_ssl_read_handler(ngx_event_t *rev); +static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, + ngx_err_t err, char *text); static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); static char *ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf); @@ -63,7 +66,19 @@ ngx_module_t ngx_openssl_module = { NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING -}; +}; + + +static long ngx_ssl_protocols[] = { + SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1, + SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1, + SSL_OP_NO_SSLv2|SSL_OP_NO_TLSv1, + SSL_OP_NO_TLSv1, + SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3, + SSL_OP_NO_SSLv3, + SSL_OP_NO_SSLv2, + 0, +}; ngx_int_t @@ -81,118 +96,265 @@ ngx_ssl_init(ngx_log_t *log) ngx_int_t -ngx_ssl_create_connection(ngx_ssl_ctx_t *ssl_ctx, ngx_connection_t *c, - ngx_uint_t flags) +ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols) +{ + ssl->ctx = SSL_CTX_new(SSLv23_server_method()); + + if (ssl->ctx == NULL) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_new() failed"); + return NGX_ERROR; + } + + SSL_CTX_set_options(ssl->ctx, SSL_OP_ALL); + + if (ngx_ssl_protocols[protocols >> 1] != 0) { + SSL_CTX_set_options(ssl->ctx, ngx_ssl_protocols[protocols >> 1]); + } + + SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + SSL_CTX_set_read_ahead(ssl->ctx, 1); + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_certificate(ngx_ssl_t *ssl, u_char *cert, u_char *key) +{ + if (SSL_CTX_use_certificate_chain_file(ssl->ctx, (char *) cert) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_use_certificate_chain_file(\"%s\") failed", + cert); + return NGX_ERROR; + } + + if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key, SSL_FILETYPE_PEM) + == 0) + { + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, + "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key); + return NGX_ERROR; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl) +{ + ssl->rsa512_key = RSA_generate_key(512, RSA_F4, NULL, NULL); + + if (ssl->rsa512_key) { + SSL_CTX_set_tmp_rsa(ssl->ctx, ssl->rsa512_key); + return NGX_OK; + } + + ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "RSA_generate_key(512) failed"); + + return NGX_ERROR; +} + + +ngx_int_t +ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags) { - ngx_ssl_t *ssl; + ngx_ssl_connection_t *sc; - ssl = ngx_pcalloc(c->pool, sizeof(ngx_ssl_t)); - if (ssl == NULL) { + sc = ngx_pcalloc(c->pool, sizeof(ngx_ssl_connection_t)); + if (sc == NULL) { return NGX_ERROR; } if (flags & NGX_SSL_BUFFER) { - ssl->buffer = 1; + sc->buffer = 1; - ssl->buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE); - if (ssl->buf == NULL) { + sc->buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE); + if (sc->buf == NULL) { return NGX_ERROR; } } - ssl->connection = SSL_new(ssl_ctx); + sc->connection = SSL_new(ssl->ctx); - if (ssl->connection == NULL) { + if (sc->connection == NULL) { ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed"); return NGX_ERROR; } - if (SSL_set_fd(ssl->connection, c->fd) == 0) { + if (SSL_set_fd(sc->connection, c->fd) == 0) { ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed"); return NGX_ERROR; } - SSL_set_accept_state(ssl->connection); + SSL_set_accept_state(sc->connection); - c->ssl = ssl; + c->ssl = sc; return NGX_OK; } -ssize_t -ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size) +ngx_int_t +ngx_ssl_handshake(ngx_connection_t *c) { - int n, bytes; + int n, sslerr; + ngx_err_t err; - if (c->ssl->last == NGX_ERROR) { - return NGX_ERROR; + n = SSL_do_handshake(c->ssl->connection); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); + + if (n == 1) { + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + return NGX_ERROR; + } + +#if (NGX_DEBUG) + { + char buf[129], *s, *d; + SSL_CIPHER *cipher; + + cipher = SSL_get_current_cipher(c->ssl->connection); + + if (cipher) { + SSL_CIPHER_description(cipher, &buf[1], 128); + + for (s = &buf[1], d = buf; *s; s++) { + if (*s == ' ' && *d == ' ') { + continue; + } + + if (*s == LF || *s == CR) { + continue; + } + + *++d = *s; + } + + if (*d != ' ') { + d++; + } + + *d = '\0'; + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL: %s, cipher: \"%s\"", + SSL_get_version(c->ssl->connection), &buf[1]); + + if (SSL_session_reused(c->ssl->connection)) { + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL reused session"); + } + + } else { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "SSL no shared ciphers"); + } + } +#endif + + c->ssl->handshaked = 1; + + c->recv = ngx_ssl_recv; + c->send = ngx_ssl_write; + c->send_chain = ngx_ssl_send_chain; + + return NGX_OK; } - bytes = 0; + sslerr = SSL_get_error(c->ssl->connection, n); - /* - * SSL_read() may return data in parts, so try to read - * until SSL_read() would return no data - */ + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr); - for ( ;; ) { + if (sslerr == SSL_ERROR_WANT_READ) { + c->read->ready = 0; + c->read->handler = ngx_ssl_handshake_handler; - n = SSL_read(c->ssl->connection, buf, size); + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + return NGX_ERROR; + } - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n); + return NGX_AGAIN; + } - if (n > 0) { + if (sslerr == SSL_ERROR_WANT_WRITE) { + c->write->ready = 0; + c->write->handler = ngx_ssl_handshake_handler; - bytes += n; + if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) { + return NGX_ERROR; + } -#if (NGX_DEBUG) + return NGX_AGAIN; + } - if (!c->ssl->handshaked && SSL_is_init_finished(c->ssl->connection)) - { - char buf[129], *s, *d; - SSL_CIPHER *cipher; + err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; - c->ssl->handshaked = 1; + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = 1; - cipher = SSL_get_current_cipher(c->ssl->connection); + if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, err, + "client closed connection in SSL handshake"); - if (cipher) { - SSL_CIPHER_description(cipher, &buf[1], 128); + return NGX_ERROR; + } - for (s = &buf[1], d = buf; *s; s++) { - if (*s == ' ' && *d == ' ') { - continue; - } + ngx_ssl_connection_error(c, sslerr, err, "SSL_do_handshake() failed"); - if (*s == LF || *s == CR) { - continue; - } + return NGX_ERROR; +} - *++d = *s; - } - if (*d != ' ') { - d++; - } +static void +ngx_ssl_handshake_handler(ngx_event_t *ev) +{ + ngx_connection_t *c; - *d = '\0'; + c = ev->data; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL cipher: \"%s\"", &buf[1]); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "ssl handshake handler: %d", ev->write); - if (SSL_session_reused(c->ssl->connection)) { - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL reused session"); - } + if (ngx_ssl_handshake(c) == NGX_AGAIN) { + return; + } - } else { - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL no shared ciphers"); - } - } -#endif + c->ssl->handler(c); +} + + +ssize_t +ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size) +{ + int n, bytes; + + if (c->ssl->last == NGX_ERROR) { + return NGX_ERROR; + } + + bytes = 0; + /* + * SSL_read() may return data in parts, so try to read + * until SSL_read() would return no data + */ + + for ( ;; ) { + + n = SSL_read(c->ssl->connection, buf, size); + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n); + + if (n > 0) { + bytes += n; } c->ssl->last = ngx_ssl_handle_recv(c, n); @@ -221,10 +383,8 @@ ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size) static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n) { - int sslerr; - char *handshake; - ngx_err_t err; - ngx_uint_t level; + int sslerr; + ngx_err_t err; if (n > 0) { @@ -250,13 +410,6 @@ ngx_ssl_handle_recv(ngx_connection_t *c, int n) return NGX_OK; } - if (!SSL_is_init_finished(c->ssl->connection)) { - handshake = " in SSL handshake"; - - } else { - handshake = ""; - } - sslerr = SSL_get_error(c->ssl->connection, n); err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0; @@ -270,9 +423,8 @@ ngx_ssl_handle_recv(ngx_connection_t *c, int n) if (sslerr == SSL_ERROR_WANT_WRITE) { - ngx_log_error(NGX_LOG_INFO, c->log, err, - "client does SSL %shandshake", - SSL_is_init_finished(c->ssl->connection) ? "re" : ""); + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client started SSL renegotiation"); c->write->ready = 0; @@ -292,44 +444,16 @@ ngx_ssl_handle_recv(ngx_connection_t *c, int n) return NGX_AGAIN; } - c->ssl->no_rcv_shut = 1; - c->ssl->no_send_shut = 1; + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = 1; if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) { - ngx_log_error(NGX_LOG_INFO, c->log, err, - "client closed connection%s", handshake); + ngx_log_error(NGX_LOG_INFO, c->log, err, "client closed connection"); return NGX_ERROR; } - level = NGX_LOG_CRIT; - - if (sslerr == SSL_ERROR_SYSCALL) { - - if (err == NGX_ECONNRESET - || err == NGX_EPIPE - || err == NGX_ENOTCONN - || err == NGX_ECONNREFUSED - || err == NGX_EHOSTUNREACH) - { - switch (c->log_error) { - - case NGX_ERROR_IGNORE_ECONNRESET: - case NGX_ERROR_INFO: - level = NGX_LOG_INFO; - break; - - case NGX_ERROR_ERR: - level = NGX_LOG_ERR; - break; - - default: - break; - } - } - } - - ngx_ssl_error(level, c->log, err, "SSL_read() failed%s", handshake); + ngx_ssl_connection_error(c, sslerr, err, "SSL_read() failed"); return NGX_ERROR; } @@ -341,6 +465,7 @@ ngx_ssl_write_handler(ngx_event_t *wev) ngx_connection_t *c; c = wev->data; + c->read->handler(c->read); } @@ -482,9 +607,8 @@ ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size) { - int n, sslerr; - ngx_err_t err; - ngx_uint_t level; + int n, sslerr; + ngx_err_t err; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size); @@ -494,52 +618,6 @@ ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size) if (n > 0) { -#if (NGX_DEBUG) - - if (!c->ssl->handshaked && SSL_is_init_finished(c->ssl->connection)) { - char buf[129], *s, *d; - SSL_CIPHER *cipher; - - c->ssl->handshaked = 1; - - cipher = SSL_get_current_cipher(c->ssl->connection); - - if (cipher) { - SSL_CIPHER_description(cipher, &buf[1], 128); - - for (s = &buf[1], d = buf; *s; s++) { - if (*s == ' ' && *d == ' ') { - continue; - } - - if (*s == LF || *s == CR) { - continue; - } - - *++d = *s; - } - - if (*d != ' ') { - d++; - } - - *d = '\0'; - - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL cipher: \"%s\"", &buf[1]); - - if (SSL_session_reused(c->ssl->connection)) { - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL reused session"); - } - - } else { - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, - "SSL no shared ciphers"); - } - } -#endif - if (c->ssl->saved_read_handler) { c->read->handler = c->ssl->saved_read_handler; @@ -575,9 +653,8 @@ ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size) if (sslerr == SSL_ERROR_WANT_READ) { - ngx_log_error(NGX_LOG_INFO, c->log, err, - "client does SSL %shandshake", - SSL_is_init_finished(c->ssl->connection) ? "re" : ""); + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client started SSL renegotiation"); c->read->ready = 0; @@ -598,37 +675,10 @@ ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size) return NGX_AGAIN; } - c->ssl->no_rcv_shut = 1; - c->ssl->no_send_shut = 1; - - level = NGX_LOG_CRIT; - - if (sslerr == SSL_ERROR_SYSCALL) { - - if (err == NGX_ECONNRESET - || err == NGX_EPIPE - || err == NGX_ENOTCONN - || err == NGX_ECONNREFUSED - || err == NGX_EHOSTUNREACH) - { - switch (c->log_error) { - - case NGX_ERROR_IGNORE_ECONNRESET: - case NGX_ERROR_INFO: - level = NGX_LOG_INFO; - break; - - case NGX_ERROR_ERR: - level = NGX_LOG_ERR; - break; - - default: - break; - } - } - } + c->ssl->no_wait_shutdown = 1; + c->ssl->no_send_shutdown = 1; - ngx_ssl_error(level, c->log, err, "SSL_write() failed"); + ngx_ssl_connection_error(c, sslerr, err, "SSL_write() failed"); return NGX_ERROR; } @@ -640,6 +690,7 @@ ngx_ssl_read_handler(ngx_event_t *rev) ngx_connection_t *c; c = rev->data; + c->write->handler(c->write); } @@ -650,31 +701,23 @@ ngx_ssl_shutdown(ngx_connection_t *c) int n, sslerr, mode; ngx_uint_t again; - if (!c->ssl->shutdown_set) { - - /* it seems that SSL_set_shutdown() could be called once only */ - - if (c->read->timedout) { - mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN; - - } else { - mode = 0; + if (c->read->timedout) { + mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN; - if (c->ssl->no_rcv_shut) { - mode = SSL_RECEIVED_SHUTDOWN; - } + } else { + mode = SSL_get_shutdown(c->ssl->connection); - if (c->ssl->no_send_shut) { - mode |= SSL_SENT_SHUTDOWN; - } + if (c->ssl->no_wait_shutdown) { + mode |= SSL_RECEIVED_SHUTDOWN; } - if (mode) { - SSL_set_shutdown(c->ssl->connection, mode); - c->ssl->shutdown_set = 1; + if (c->ssl->no_send_shutdown) { + mode |= SSL_SENT_SHUTDOWN; } } + SSL_set_shutdown(c->ssl->connection, mode); + again = 0; #if (NGX_SUPPRESS_WARN) sslerr = 0; @@ -736,11 +779,49 @@ ngx_ssl_shutdown(ngx_connection_t *c) } +static void +ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err, + char *text) +{ + ngx_uint_t level; + + level = NGX_LOG_CRIT; + + if (sslerr == SSL_ERROR_SYSCALL) { + + if (err == NGX_ECONNRESET + || err == NGX_EPIPE + || err == NGX_ENOTCONN + || err == NGX_ECONNREFUSED + || err == NGX_EHOSTUNREACH) + { + switch (c->log_error) { + + case NGX_ERROR_IGNORE_ECONNRESET: + case NGX_ERROR_INFO: + level = NGX_LOG_INFO; + break; + + case NGX_ERROR_ERR: + level = NGX_LOG_ERR; + break; + + default: + break; + } + } + } + + ngx_ssl_error(level, c->log, err, text); +} + + void ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...) { - u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; + u_long n; va_list args; + u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; last = errstr + NGX_MAX_CONF_ERRSTR; @@ -748,9 +829,18 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...) p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args); va_end(args); - p = ngx_cpystrn(p, (u_char *) " (SSL: ", last - p); + p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p); + + while (p < last && (n = ERR_get_error())) { - ERR_error_string_n(ERR_get_error(), (char *) p, last - p); + *p++ = ' '; + + ERR_error_string_n(n, (char *) p, last - p); + + while (p < last && *p) { + p++; + } + } ngx_log_error(level, log, err, "%s)", errstr); } @@ -759,9 +849,10 @@ ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...) void ngx_ssl_cleanup_ctx(void *data) { - SSL_CTX *ctx = data; + ngx_ssl_t *ssl = data; - SSL_CTX_free(ctx); + RSA_free(ssl->rsa512_key); + SSL_CTX_free(ssl->ctx); } diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h index fa7f27e61..cd3898c73 100644 --- a/src/event/ngx_event_openssl.h +++ b/src/event/ngx_event_openssl.h @@ -16,44 +16,53 @@ #if OPENSSL_VERSION_NUMBER >= 0x00907000 #include <openssl/engine.h> -#define NGX_SSL_ENGINE 1 +#define NGX_SSL_ENGINE 1 #endif -#define NGX_SSL_NAME "OpenSSL" +#define NGX_SSL_NAME "OpenSSL" typedef struct { - SSL *connection; - ngx_int_t last; - ngx_buf_t *buf; - ngx_event_handler_pt saved_read_handler; - ngx_event_handler_pt saved_write_handler; - - unsigned buffer:1; - unsigned no_rcv_shut:1; - unsigned no_send_shut:1; - unsigned shutdown_set:1; - -#if (NGX_DEBUG) - unsigned handshaked:1; -#endif + SSL_CTX *ctx; + RSA *rsa512_key; + ngx_log_t *log; } ngx_ssl_t; -typedef SSL_CTX ngx_ssl_ctx_t; +typedef struct { + SSL *connection; + ngx_int_t last; + ngx_buf_t *buf; + ngx_connection_handler_pt handler; -#define NGX_SSL_BUFFER 1 + ngx_event_handler_pt saved_read_handler; + ngx_event_handler_pt saved_write_handler; -#define NGX_SSL_BUFSIZE 16384 + unsigned handshaked:1; + unsigned buffer:1; + unsigned no_wait_shutdown:1; + unsigned no_send_shutdown:1; +} ngx_ssl_connection_t; -ngx_int_t ngx_ssl_init(ngx_log_t *log); -ngx_int_t ngx_ssl_create_connection(ngx_ssl_ctx_t *ctx, ngx_connection_t *c, - ngx_uint_t flags); +#define NGX_SSL_SSLv2 2 +#define NGX_SSL_SSLv3 4 +#define NGX_SSL_TLSv1 8 + -#define ngx_ssl_handshake(c) NGX_OK +#define NGX_SSL_BUFFER 1 +#define NGX_SSL_BUFSIZE 16384 + + +ngx_int_t ngx_ssl_init(ngx_log_t *log); +ngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols); +ngx_int_t ngx_ssl_certificate(ngx_ssl_t *ssl, u_char *cert, u_char *key); +ngx_int_t ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl); +ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, + ngx_uint_t flags); +ngx_int_t ngx_ssl_handshake(ngx_connection_t *c); ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size); ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size); ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, |
