From f2baa0686f9688c9896a967757ba13af2ab14497 Mon Sep 17 00:00:00 2001 From: Cem Keylan Date: Thu, 1 Apr 2021 14:52:14 +0300 Subject: bearssl: move to extra --- core/bearssl/build | 27 ----- core/bearssl/checksums | 4 - ...-return-in-client-single-EC-choose-functi.patch | 25 ----- ...ns-to-retrieve-certificate-validity-perio.patch | 60 ----------- ...003-brssl-client-add-option-to-ignore-EOF.patch | 114 --------------------- core/bearssl/sources | 4 - core/bearssl/version | 1 - extra/bearssl/build | 27 +++++ extra/bearssl/checksums | 4 + ...-return-in-client-single-EC-choose-functi.patch | 25 +++++ ...ns-to-retrieve-certificate-validity-perio.patch | 60 +++++++++++ ...003-brssl-client-add-option-to-ignore-EOF.patch | 114 +++++++++++++++++++++ extra/bearssl/sources | 4 + extra/bearssl/version | 1 + 14 files changed, 235 insertions(+), 235 deletions(-) delete mode 100755 core/bearssl/build delete mode 100644 core/bearssl/checksums delete mode 100644 core/bearssl/patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch delete mode 100644 core/bearssl/patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch delete mode 100644 core/bearssl/patches/0003-brssl-client-add-option-to-ignore-EOF.patch delete mode 100644 core/bearssl/sources delete mode 100644 core/bearssl/version create mode 100755 extra/bearssl/build create mode 100644 extra/bearssl/checksums create mode 100644 extra/bearssl/patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch create mode 100644 extra/bearssl/patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch create mode 100644 extra/bearssl/patches/0003-brssl-client-add-option-to-ignore-EOF.patch create mode 100644 extra/bearssl/sources create mode 100644 extra/bearssl/version diff --git a/core/bearssl/build b/core/bearssl/build deleted file mode 100755 index c0e5e7f2..00000000 --- a/core/bearssl/build +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -e - -for patch in *.patch; do - patch -p1 < "$patch" -done - -kinstall() { - mkdir -p "${3%/*}"; cp "$2" "$3" - chmod "$1" "$3" -} - -make "CC=${CC:=cc}" "AR=${AR:=ar}" LD=$CC - -# Build static binary for bearssl, word splitting on CFLAGS is intentional. -# shellcheck disable=2086 -"$CC" -static $CFLAGS \ - -I ./inc \ - -include tools/brssl.h \ - tools/*.c \ - build/libbearssl.a \ - -o brssl - -kinstall 755 brssl "$1/usr/bin/brssl" -kinstall 644 build/libbearssl.a "$1/usr/lib/libbearssl.a" -kinstall 755 build/libbearssl.so "$1/usr/lib/libbearssl.so" - -mv inc "$1/usr/include" diff --git a/core/bearssl/checksums b/core/bearssl/checksums deleted file mode 100644 index 86e8e8a6..00000000 --- a/core/bearssl/checksums +++ /dev/null @@ -1,4 +0,0 @@ -6705bba1714961b41a728dfc5debbe348d2966c117649392f8c8139efc83ff14 bearssl-0.6.tar.gz -ad783bbbbb58bbdad66af299c5a0ea5389474a7d7256391673fe94e88f11fbef 0001-Add-missing-return-in-client-single-EC-choose-functi.patch -414fd90fc27353ae3ca2478b68891715088de8b6cf6b81927ed8337df63f47e4 0002-Add-functions-to-retrieve-certificate-validity-perio.patch -a738717ddfb68c95813f869a1f2cc6a6cd60cdb9b548c854896d4992dce6b3f5 0003-brssl-client-add-option-to-ignore-EOF.patch diff --git a/core/bearssl/patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch b/core/bearssl/patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch deleted file mode 100644 index 421bbc7f..00000000 --- a/core/bearssl/patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch +++ /dev/null @@ -1,25 +0,0 @@ -From a5c3ea02385205858128e414873a0150cd8bceda Mon Sep 17 00:00:00 2001 -From: Michael Forney -Date: Fri, 31 Jan 2020 15:11:32 -0800 -Subject: [PATCH] Add missing return in client single EC choose function - -Otherwise, static ECDH is never selected. ---- - src/ssl/ssl_ccert_single_ec.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/src/ssl/ssl_ccert_single_ec.c b/src/ssl/ssl_ccert_single_ec.c -index 93ebcde..2e1e54f 100644 ---- a/src/ssl/ssl_ccert_single_ec.c -+++ b/src/ssl/ssl_ccert_single_ec.c -@@ -69,6 +69,7 @@ cc_choose(const br_ssl_client_certificate_class **pctx, - choices->hash_id = -1; - choices->chain = zc->chain; - choices->chain_len = zc->chain_len; -+ return; - } - } - --- -2.25.0 - diff --git a/core/bearssl/patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch b/core/bearssl/patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch deleted file mode 100644 index 8377da4d..00000000 --- a/core/bearssl/patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 31fdee5b9d8fc63c850222768dcd097e43da0116 Mon Sep 17 00:00:00 2001 -From: Michael Forney -Date: Thu, 26 Mar 2020 14:17:19 -0700 -Subject: [PATCH] Add functions to retrieve certificate validity period from - br_x509_decoder. - ---- - inc/bearssl_x509.h | 36 ++++++++++++++++++++++++++++++++++++ - 1 file changed, 36 insertions(+) - -diff --git a/inc/bearssl_x509.h b/inc/bearssl_x509.h -index 49d2fba..9d43e15 100644 ---- a/inc/bearssl_x509.h -+++ b/inc/bearssl_x509.h -@@ -1045,6 +1045,42 @@ br_x509_decoder_last_error(br_x509_decoder_context *ctx) - return 0; - } - -+/** -+ * \brief Get the time when the certificate becomes valid. -+ * -+ * The time is represented the same as in `br_x509_minimal_set_time()`. -+ * These values should not be read before decoding completed successfully. -+ * -+ * \param ctx X.509 decoder context. -+ * \param days receives the days since January 1st, 0 AD. -+ * \param seconds receives the seconds since midnight (0 to 86400). -+ */ -+static inline void -+br_x509_decoder_get_notbefore(br_x509_decoder_context *ctx, -+ uint32_t *days, uint32_t *seconds) -+{ -+ *days = ctx->notbefore_days; -+ *seconds = ctx->notbefore_seconds; -+} -+ -+/** -+ * \brief Get the time when the certificate is no longer valid. -+ * -+ * The time is represented the same as in `br_x509_minimal_set_time()`. -+ * These values should not be read before decoding completed successfully. -+ * -+ * \param ctx X.509 decoder context. -+ * \param days receives the days since January 1st, 0 AD. -+ * \param seconds receives the seconds since midnight (0 to 86400). -+ */ -+static inline void -+br_x509_decoder_get_notafter(br_x509_decoder_context *ctx, -+ uint32_t *days, uint32_t *seconds) -+{ -+ *days = ctx->notafter_days; -+ *seconds = ctx->notafter_seconds; -+} -+ - /** - * \brief Get the "isCA" flag from an X.509 decoder context. - * --- -2.26.0 - diff --git a/core/bearssl/patches/0003-brssl-client-add-option-to-ignore-EOF.patch b/core/bearssl/patches/0003-brssl-client-add-option-to-ignore-EOF.patch deleted file mode 100644 index 684710f8..00000000 --- a/core/bearssl/patches/0003-brssl-client-add-option-to-ignore-EOF.patch +++ /dev/null @@ -1,114 +0,0 @@ -From 694cf4248db1664936ce43e33db0b4c5dc35bad7 Mon Sep 17 00:00:00 2001 -From: Cem Keylan -Date: Wed, 17 Feb 2021 22:39:35 +0300 -Subject: [PATCH] brssl client: add option to ignore EOF. - -I have added a -igneof option to the bearssl client, so that I can -patch busybox to use bearssl instead of openssl. I did not add the -option to the server, because I have personally never used it, and -don't have a use case. ---- - tools/brssl.h | 1 + - tools/client.c | 9 ++++++++- - tools/sslio.c | 10 +++++++--- - 3 files changed, 16 insertions(+), 4 deletions(-) - -diff --git a/tools/brssl.h b/tools/brssl.h -index a23ba00..15876eb 100644 ---- a/tools/brssl.h -+++ b/tools/brssl.h -@@ -514,6 +514,7 @@ int run_ssl_engine(br_ssl_engine_context *eng, - - #define RUN_ENGINE_VERBOSE 0x0001 /* enable verbose messages */ - #define RUN_ENGINE_TRACE 0x0002 /* hex dump of records */ -+#define RUN_ENGINE_IGNEOF 0x0004 /* do not exit after EOF */ - - /* - * Do the "client" command. Returned value is 0 on success, -1 on failure. -diff --git a/tools/client.c b/tools/client.c -index 9838857..3388b09 100644 ---- a/tools/client.c -+++ b/tools/client.c -@@ -467,6 +467,8 @@ usage_client(void) - fprintf(stderr, - " -trace activate extra debug messages (dump of all packets)\n"); - fprintf(stderr, -+" -igneof do not exit after stdin is closed\n"); -+ fprintf(stderr, - " -sni name use this specific name for SNI\n"); - fprintf(stderr, - " -nosni do not send any SNI\n"); -@@ -511,6 +513,7 @@ do_client(int argc, char *argv[]) - int retcode; - int verbose; - int trace; -+ int igneof; - int i, bidi; - const char *server_name; - char *host; -@@ -543,6 +546,7 @@ do_client(int argc, char *argv[]) - retcode = 0; - verbose = 1; - trace = 0; -+ igneof = 0; - server_name = NULL; - host = NULL; - port = NULL; -@@ -584,6 +588,8 @@ do_client(int argc, char *argv[]) - verbose = 0; - } else if (eqstr(arg, "-trace")) { - trace = 1; -+ } else if (eqstr(arg, "-igneof")) { -+ igneof = 1; - } else if (eqstr(arg, "-sni")) { - if (++ i >= argc) { - fprintf(stderr, -@@ -1077,7 +1083,8 @@ do_client(int argc, char *argv[]) - */ - if (run_ssl_engine(&cc.eng, fd, - (verbose ? RUN_ENGINE_VERBOSE : 0) -- | (trace ? RUN_ENGINE_TRACE : 0)) != 0) -+ | (trace ? RUN_ENGINE_TRACE : 0) -+ | (igneof ? RUN_ENGINE_IGNEOF : 0)) != 0) - { - goto client_exit_error; - } else { -diff --git a/tools/sslio.c b/tools/sslio.c -index ef7dd3f..fc6e0f0 100644 ---- a/tools/sslio.c -+++ b/tools/sslio.c -@@ -250,6 +250,7 @@ run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags) - int retcode; - int verbose; - int trace; -+ int igneof; - #ifdef _WIN32 - WSAEVENT fd_event; - int can_send, can_recv; -@@ -261,6 +262,7 @@ run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags) - retcode = 0; - verbose = (flags & RUN_ENGINE_VERBOSE) != 0; - trace = (flags & RUN_ENGINE_TRACE) != 0; -+ igneof = (flags & RUN_ENGINE_IGNEOF) != 0; - - /* - * Print algorithm details. -@@ -730,10 +732,12 @@ run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags) - rlen = read(0, buf, len); - #endif - if (rlen <= 0) { -- if (verbose) { -- fprintf(stderr, "stdin closed...\n"); -+ if (!igneof) { -+ if (verbose) { -+ fprintf(stderr, "stdin closed...\n"); -+ } -+ br_ssl_engine_close(cc); - } -- br_ssl_engine_close(cc); - } else if (!run_command(cc, buf, rlen)) { - br_ssl_engine_sendapp_ack(cc, rlen); - } --- -2.30.1 - diff --git a/core/bearssl/sources b/core/bearssl/sources deleted file mode 100644 index b03853d9..00000000 --- a/core/bearssl/sources +++ /dev/null @@ -1,4 +0,0 @@ -https://bearssl.org/bearssl-0.6.tar.gz -patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch -patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch -patches/0003-brssl-client-add-option-to-ignore-EOF.patch diff --git a/core/bearssl/version b/core/bearssl/version deleted file mode 100644 index fe09a3c0..00000000 --- a/core/bearssl/version +++ /dev/null @@ -1 +0,0 @@ -0.6 3 diff --git a/extra/bearssl/build b/extra/bearssl/build new file mode 100755 index 00000000..c0e5e7f2 --- /dev/null +++ b/extra/bearssl/build @@ -0,0 +1,27 @@ +#!/bin/sh -e + +for patch in *.patch; do + patch -p1 < "$patch" +done + +kinstall() { + mkdir -p "${3%/*}"; cp "$2" "$3" + chmod "$1" "$3" +} + +make "CC=${CC:=cc}" "AR=${AR:=ar}" LD=$CC + +# Build static binary for bearssl, word splitting on CFLAGS is intentional. +# shellcheck disable=2086 +"$CC" -static $CFLAGS \ + -I ./inc \ + -include tools/brssl.h \ + tools/*.c \ + build/libbearssl.a \ + -o brssl + +kinstall 755 brssl "$1/usr/bin/brssl" +kinstall 644 build/libbearssl.a "$1/usr/lib/libbearssl.a" +kinstall 755 build/libbearssl.so "$1/usr/lib/libbearssl.so" + +mv inc "$1/usr/include" diff --git a/extra/bearssl/checksums b/extra/bearssl/checksums new file mode 100644 index 00000000..86e8e8a6 --- /dev/null +++ b/extra/bearssl/checksums @@ -0,0 +1,4 @@ +6705bba1714961b41a728dfc5debbe348d2966c117649392f8c8139efc83ff14 bearssl-0.6.tar.gz +ad783bbbbb58bbdad66af299c5a0ea5389474a7d7256391673fe94e88f11fbef 0001-Add-missing-return-in-client-single-EC-choose-functi.patch +414fd90fc27353ae3ca2478b68891715088de8b6cf6b81927ed8337df63f47e4 0002-Add-functions-to-retrieve-certificate-validity-perio.patch +a738717ddfb68c95813f869a1f2cc6a6cd60cdb9b548c854896d4992dce6b3f5 0003-brssl-client-add-option-to-ignore-EOF.patch diff --git a/extra/bearssl/patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch b/extra/bearssl/patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch new file mode 100644 index 00000000..421bbc7f --- /dev/null +++ b/extra/bearssl/patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch @@ -0,0 +1,25 @@ +From a5c3ea02385205858128e414873a0150cd8bceda Mon Sep 17 00:00:00 2001 +From: Michael Forney +Date: Fri, 31 Jan 2020 15:11:32 -0800 +Subject: [PATCH] Add missing return in client single EC choose function + +Otherwise, static ECDH is never selected. +--- + src/ssl/ssl_ccert_single_ec.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/ssl/ssl_ccert_single_ec.c b/src/ssl/ssl_ccert_single_ec.c +index 93ebcde..2e1e54f 100644 +--- a/src/ssl/ssl_ccert_single_ec.c ++++ b/src/ssl/ssl_ccert_single_ec.c +@@ -69,6 +69,7 @@ cc_choose(const br_ssl_client_certificate_class **pctx, + choices->hash_id = -1; + choices->chain = zc->chain; + choices->chain_len = zc->chain_len; ++ return; + } + } + +-- +2.25.0 + diff --git a/extra/bearssl/patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch b/extra/bearssl/patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch new file mode 100644 index 00000000..8377da4d --- /dev/null +++ b/extra/bearssl/patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch @@ -0,0 +1,60 @@ +From 31fdee5b9d8fc63c850222768dcd097e43da0116 Mon Sep 17 00:00:00 2001 +From: Michael Forney +Date: Thu, 26 Mar 2020 14:17:19 -0700 +Subject: [PATCH] Add functions to retrieve certificate validity period from + br_x509_decoder. + +--- + inc/bearssl_x509.h | 36 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/inc/bearssl_x509.h b/inc/bearssl_x509.h +index 49d2fba..9d43e15 100644 +--- a/inc/bearssl_x509.h ++++ b/inc/bearssl_x509.h +@@ -1045,6 +1045,42 @@ br_x509_decoder_last_error(br_x509_decoder_context *ctx) + return 0; + } + ++/** ++ * \brief Get the time when the certificate becomes valid. ++ * ++ * The time is represented the same as in `br_x509_minimal_set_time()`. ++ * These values should not be read before decoding completed successfully. ++ * ++ * \param ctx X.509 decoder context. ++ * \param days receives the days since January 1st, 0 AD. ++ * \param seconds receives the seconds since midnight (0 to 86400). ++ */ ++static inline void ++br_x509_decoder_get_notbefore(br_x509_decoder_context *ctx, ++ uint32_t *days, uint32_t *seconds) ++{ ++ *days = ctx->notbefore_days; ++ *seconds = ctx->notbefore_seconds; ++} ++ ++/** ++ * \brief Get the time when the certificate is no longer valid. ++ * ++ * The time is represented the same as in `br_x509_minimal_set_time()`. ++ * These values should not be read before decoding completed successfully. ++ * ++ * \param ctx X.509 decoder context. ++ * \param days receives the days since January 1st, 0 AD. ++ * \param seconds receives the seconds since midnight (0 to 86400). ++ */ ++static inline void ++br_x509_decoder_get_notafter(br_x509_decoder_context *ctx, ++ uint32_t *days, uint32_t *seconds) ++{ ++ *days = ctx->notafter_days; ++ *seconds = ctx->notafter_seconds; ++} ++ + /** + * \brief Get the "isCA" flag from an X.509 decoder context. + * +-- +2.26.0 + diff --git a/extra/bearssl/patches/0003-brssl-client-add-option-to-ignore-EOF.patch b/extra/bearssl/patches/0003-brssl-client-add-option-to-ignore-EOF.patch new file mode 100644 index 00000000..684710f8 --- /dev/null +++ b/extra/bearssl/patches/0003-brssl-client-add-option-to-ignore-EOF.patch @@ -0,0 +1,114 @@ +From 694cf4248db1664936ce43e33db0b4c5dc35bad7 Mon Sep 17 00:00:00 2001 +From: Cem Keylan +Date: Wed, 17 Feb 2021 22:39:35 +0300 +Subject: [PATCH] brssl client: add option to ignore EOF. + +I have added a -igneof option to the bearssl client, so that I can +patch busybox to use bearssl instead of openssl. I did not add the +option to the server, because I have personally never used it, and +don't have a use case. +--- + tools/brssl.h | 1 + + tools/client.c | 9 ++++++++- + tools/sslio.c | 10 +++++++--- + 3 files changed, 16 insertions(+), 4 deletions(-) + +diff --git a/tools/brssl.h b/tools/brssl.h +index a23ba00..15876eb 100644 +--- a/tools/brssl.h ++++ b/tools/brssl.h +@@ -514,6 +514,7 @@ int run_ssl_engine(br_ssl_engine_context *eng, + + #define RUN_ENGINE_VERBOSE 0x0001 /* enable verbose messages */ + #define RUN_ENGINE_TRACE 0x0002 /* hex dump of records */ ++#define RUN_ENGINE_IGNEOF 0x0004 /* do not exit after EOF */ + + /* + * Do the "client" command. Returned value is 0 on success, -1 on failure. +diff --git a/tools/client.c b/tools/client.c +index 9838857..3388b09 100644 +--- a/tools/client.c ++++ b/tools/client.c +@@ -467,6 +467,8 @@ usage_client(void) + fprintf(stderr, + " -trace activate extra debug messages (dump of all packets)\n"); + fprintf(stderr, ++" -igneof do not exit after stdin is closed\n"); ++ fprintf(stderr, + " -sni name use this specific name for SNI\n"); + fprintf(stderr, + " -nosni do not send any SNI\n"); +@@ -511,6 +513,7 @@ do_client(int argc, char *argv[]) + int retcode; + int verbose; + int trace; ++ int igneof; + int i, bidi; + const char *server_name; + char *host; +@@ -543,6 +546,7 @@ do_client(int argc, char *argv[]) + retcode = 0; + verbose = 1; + trace = 0; ++ igneof = 0; + server_name = NULL; + host = NULL; + port = NULL; +@@ -584,6 +588,8 @@ do_client(int argc, char *argv[]) + verbose = 0; + } else if (eqstr(arg, "-trace")) { + trace = 1; ++ } else if (eqstr(arg, "-igneof")) { ++ igneof = 1; + } else if (eqstr(arg, "-sni")) { + if (++ i >= argc) { + fprintf(stderr, +@@ -1077,7 +1083,8 @@ do_client(int argc, char *argv[]) + */ + if (run_ssl_engine(&cc.eng, fd, + (verbose ? RUN_ENGINE_VERBOSE : 0) +- | (trace ? RUN_ENGINE_TRACE : 0)) != 0) ++ | (trace ? RUN_ENGINE_TRACE : 0) ++ | (igneof ? RUN_ENGINE_IGNEOF : 0)) != 0) + { + goto client_exit_error; + } else { +diff --git a/tools/sslio.c b/tools/sslio.c +index ef7dd3f..fc6e0f0 100644 +--- a/tools/sslio.c ++++ b/tools/sslio.c +@@ -250,6 +250,7 @@ run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags) + int retcode; + int verbose; + int trace; ++ int igneof; + #ifdef _WIN32 + WSAEVENT fd_event; + int can_send, can_recv; +@@ -261,6 +262,7 @@ run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags) + retcode = 0; + verbose = (flags & RUN_ENGINE_VERBOSE) != 0; + trace = (flags & RUN_ENGINE_TRACE) != 0; ++ igneof = (flags & RUN_ENGINE_IGNEOF) != 0; + + /* + * Print algorithm details. +@@ -730,10 +732,12 @@ run_ssl_engine(br_ssl_engine_context *cc, unsigned long fd, unsigned flags) + rlen = read(0, buf, len); + #endif + if (rlen <= 0) { +- if (verbose) { +- fprintf(stderr, "stdin closed...\n"); ++ if (!igneof) { ++ if (verbose) { ++ fprintf(stderr, "stdin closed...\n"); ++ } ++ br_ssl_engine_close(cc); + } +- br_ssl_engine_close(cc); + } else if (!run_command(cc, buf, rlen)) { + br_ssl_engine_sendapp_ack(cc, rlen); + } +-- +2.30.1 + diff --git a/extra/bearssl/sources b/extra/bearssl/sources new file mode 100644 index 00000000..b03853d9 --- /dev/null +++ b/extra/bearssl/sources @@ -0,0 +1,4 @@ +https://bearssl.org/bearssl-0.6.tar.gz +patches/0001-Add-missing-return-in-client-single-EC-choose-functi.patch +patches/0002-Add-functions-to-retrieve-certificate-validity-perio.patch +patches/0003-brssl-client-add-option-to-ignore-EOF.patch diff --git a/extra/bearssl/version b/extra/bearssl/version new file mode 100644 index 00000000..fe09a3c0 --- /dev/null +++ b/extra/bearssl/version @@ -0,0 +1 @@ +0.6 3 -- cgit v1.2.3