From 862e84877ff262cd4b8c4b191a8710f94f63fcf7 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 10 Mar 2022 15:09:57 +0100 Subject: [PATCH 01/14] ssl: first pass limit when allocating buffer for certificates With this check, on the first packet of a certificate presenting a length of 16Mbytes, we only allocate up to 65Kb When we get to the point where need more than 65Kb, we realloc to the true size. With this check, it makes it more expensive for an attacket to use this allocation as a way to trigger ressource exhaustion... --- src/app-layer-ssl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index 30ad2f785628..fed5eaca1306 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -1418,6 +1418,12 @@ static uint32_t GetCertsLen(SSLStateConnp *curr_connp, const uint8_t *input, } } +// For certificates whose size is bigger than this, +// we do not allocate all the required memory straight away, +// to avoid DOS by RAM exhaustion, but we will allocate +// this memory once a consequent part of the certificate has been seen. +#define SSL_CERT_MAX_FIRST_ALLOC 65536 // 0x10000 + /** \internal * \brief setup or grow the `trec` space in the connp */ @@ -1431,6 +1437,10 @@ static int EnsureRecordSpace(SSLStateConnp *curr_connp, const uint8_t * const in SCLogDebug("cert_len unknown still, create small buffer to start"); certs_len = 256; } + // Limit in a first time allocation for very large certificates + if (certs_len > SSL_CERT_MAX_FIRST_ALLOC && certs_len > curr_connp->trec_pos + input_len) { + certs_len = SSL_CERT_MAX_FIRST_ALLOC; + } if (curr_connp->trec == NULL) { curr_connp->trec_len = certs_len; From cfcade58ad65b18ddc10f1e7cc29b2efe2a8618c Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 30 Nov 2021 15:06:38 +0100 Subject: [PATCH 02/14] http: move xff logging to alert object Ticket: 4860 instead of root field --- doc/userguide/upgrade.rst | 1 + src/output-json-alert.c | 21 ++++++++++++--------- src/output-json-alert.h | 2 +- src/output-json-drop.c | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index 34aadc961e24..10c9577ec2f1 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -46,6 +46,7 @@ Logging changes - IKEv2 Eve logging changed, the event_type has become ``ike``. The fields ``errors`` and ``notify`` have moved to ``ike.ikev2.errors`` and ``ike.ikev2.notify``. - FTP DATA metadata for alerts are now logged in ``ftp_data`` instead of root. +- Alert ``xff`` field is now logged as ``alert.xff`` for alerts instead of at the root. Other changes ~~~~~~~~~~~~~ diff --git a/src/output-json-alert.c b/src/output-json-alert.c index 0465184fb690..5c71557dabfc 100644 --- a/src/output-json-alert.c +++ b/src/output-json-alert.c @@ -338,8 +338,8 @@ static void AlertJsonMetadata(AlertJsonOutputCtx *json_output_ctx, } } -void AlertJsonHeader(void *ctx, const Packet *p, const PacketAlert *pa, - JsonBuilder *js, uint16_t flags, JsonAddrInfo *addr) +void AlertJsonHeader(void *ctx, const Packet *p, const PacketAlert *pa, JsonBuilder *js, + uint16_t flags, JsonAddrInfo *addr, char *xff_buffer) { AlertJsonOutputCtx *json_output_ctx = (AlertJsonOutputCtx *)ctx; const char *action = "allowed"; @@ -390,6 +390,9 @@ void AlertJsonHeader(void *ctx, const Packet *p, const PacketAlert *pa, if (flags & LOG_JSON_RULE) { jb_set_string(js, "rule", pa->s->sig_str); } + if (xff_buffer && xff_buffer[0]) { + jb_set_string(js, "xff", xff_buffer); + } jb_close(js); } @@ -650,6 +653,7 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p) json_output_ctx->xff_cfg : json_output_ctx->parent_xff_cfg;; int have_xff_ip = 0; char xff_buffer[XFF_MAXLEN]; + xff_buffer[0] = 0; if ((xff_cfg != NULL) && !(xff_cfg->flags & XFF_DISABLED) && p->flow != NULL) { if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP1) { if (pa->flags & PACKET_ALERT_FLAG_TX) { @@ -671,6 +675,10 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p) * logged below. */ have_xff_ip = false; } + if (have_xff_ip && !(xff_cfg->flags & XFF_EXTRADATA)) { + // reset xff_buffer so as not to log it + xff_buffer[0] = 0; + } } JsonBuilder *jb = @@ -680,8 +688,7 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p) /* alert */ - AlertJsonHeader(json_output_ctx, p, pa, jb, json_output_ctx->flags, - &addr); + AlertJsonHeader(json_output_ctx, p, pa, jb, json_output_ctx->flags, &addr, xff_buffer); if (IS_TUNNEL_PKT(p)) { AlertJsonTunnel(p, jb); @@ -759,10 +766,6 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p) EvePacket(p, jb, 0); } - if (have_xff_ip && xff_cfg->flags & XFF_EXTRADATA) { - jb_set_string(jb, "xff", xff_buffer); - } - OutputJsonBuilderBuffer(jb, aft->ctx); jb_free(jb); } @@ -805,7 +808,7 @@ static int AlertJsonDecoderEvent(ThreadVars *tv, JsonAlertLogThread *aft, const /* just the timestamp, no tuple */ jb_set_string(jb, "timestamp", timebuf); - AlertJsonHeader(json_output_ctx, p, pa, jb, json_output_ctx->flags, NULL); + AlertJsonHeader(json_output_ctx, p, pa, jb, json_output_ctx->flags, NULL, NULL); OutputJsonBuilderBuffer(jb, aft->ctx); jb_free(jb); diff --git a/src/output-json-alert.h b/src/output-json-alert.h index 879e39919fff..0a5af4268a09 100644 --- a/src/output-json-alert.h +++ b/src/output-json-alert.h @@ -29,7 +29,7 @@ void JsonAlertLogRegister(void); void AlertJsonHeader(void *ctx, const Packet *p, const PacketAlert *pa, JsonBuilder *js, - uint16_t flags, JsonAddrInfo *addr); + uint16_t flags, JsonAddrInfo *addr, char *xff_buffer); #endif /* __OUTPUT_JSON_ALERT_H__ */ diff --git a/src/output-json-drop.c b/src/output-json-drop.c index 83e16be8284a..b2b5925e0a63 100644 --- a/src/output-json-drop.c +++ b/src/output-json-drop.c @@ -155,7 +155,7 @@ static int DropLogJSON (JsonDropLogThread *aft, const Packet *p) if ((pa->action & (ACTION_REJECT|ACTION_REJECT_DST|ACTION_REJECT_BOTH)) || ((pa->action & ACTION_DROP) && EngineModeIsIPS())) { - AlertJsonHeader(NULL, p, pa, js, 0, &addr); + AlertJsonHeader(NULL, p, pa, js, 0, &addr, NULL); logged = 1; break; } @@ -163,7 +163,7 @@ static int DropLogJSON (JsonDropLogThread *aft, const Packet *p) if (logged == 0) { if (p->alerts.drop.action != 0) { const PacketAlert *pa = &p->alerts.drop; - AlertJsonHeader(NULL, p, pa, js, 0, &addr); + AlertJsonHeader(NULL, p, pa, js, 0, &addr, NULL); } } } From 69c6657127546cf1b8a02751ae328421c8f366f8 Mon Sep 17 00:00:00 2001 From: Juliana Fajardini Date: Wed, 22 Dec 2021 17:57:25 +0000 Subject: [PATCH 03/14] devguide: clarify cargo test usage for modules The documentation was showing an invalid path for running single tests. --- doc/devguide/codebase/unittests-rust.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/devguide/codebase/unittests-rust.rst b/doc/devguide/codebase/unittests-rust.rst index 4b196f25d77a..c11d6e44e8ac 100644 --- a/doc/devguide/codebase/unittests-rust.rst +++ b/doc/devguide/codebase/unittests-rust.rst @@ -80,11 +80,12 @@ From ``nfs > rpc_records.rs``: Once that is done, Rust should recognize the new test. If you want to check a single test, run:: - cargo test module::test_name + cargo test module::file_name::tests::test_name -or even:: +Where ``tests`` refers to ``mod tests``. If you know the test name is unique, you can even run:: cargo test test_name -if you know it's a unique function name. Following the same idea, it is also possible to test specific modules or -submodules. +Following the same idea, it is also possible to test specific modules or submodules. For instance:: + + cargo test nfs::rpc_records From 6f77c722a28a18db717e265e19ffba431efad97e Mon Sep 17 00:00:00 2001 From: Juliana Fajardini Date: Thu, 16 Dec 2021 18:40:41 +0000 Subject: [PATCH 04/14] devguide: move into userguide as last chapter Moved devguide dir into userguide dir. Since the devguide is now incorporated as the last chapter of the userguide, removed build and configuration files from the devguide dir, as these are no longer needed. Task #4909 --- .clang-format | 2 +- .github/workflows/builds.yml | 1 - configure.ac | 2 +- doc/Makefile.am | 2 +- doc/devguide/Makefile.am | 73 ---- doc/devguide/Makefile.sphinx | 204 ------------ doc/devguide/_static/.gitignore | 0 doc/devguide/_static/css/suricata.css | 27 -- doc/devguide/conf.py | 315 ------------------ doc/userguide/Makefile.am | 2 + doc/userguide/README.md | 5 +- doc/userguide/conf.py | 17 +- doc/{ => userguide}/devguide/.gitignore | 1 - doc/{ => userguide}/devguide/README.md | 0 .../devguide/codebase/code-style.rst | 0 .../contributing/code-submission-process.rst | 0 .../devguide/codebase/contributing/index.rst | 0 .../devguide/codebase/fuzz-testing.rst | 0 .../codebase/img/InputCaptureExample.png | Bin .../devguide/codebase/index.rst | 0 .../devguide/codebase/testing.rst | 0 .../devguide/codebase/unittests-c.rst | 0 .../devguide/codebase/unittests-rust.rst | 0 .../extending/app-layer/app-layer-frames.rst | 22 +- .../DnsUnidirectionalTransactions.msc | 0 .../HTTP2BidirectionalTransaction.msc | 0 .../diagrams/TemplateTransaction.msc | 0 .../app-layer/diagrams/TlsHandshake.msc | 0 .../extending/app-layer/img/StreamFrames.png | Bin .../devguide/extending/app-layer/index.rst | 0 .../devguide/extending/app-layer/parser.rst | 0 .../extending/app-layer/transactions.rst | 0 .../devguide/extending/capture/index.rst | 0 .../devguide/extending/decoder/index.rst | 0 .../devguide/extending/detect/index.rst | 0 .../devguide/extending/index.rst | 0 .../devguide/extending/output/index.rst | 0 doc/{ => userguide}/devguide/index.rst | 1 - .../devguide/internals/datastructs/index.rst | 0 .../devguide/internals/engines/index.rst | 0 .../devguide/internals/index.rst | 0 .../devguide/internals/pipeline/index.rst | 0 .../devguide/internals/threading/index.rst | 0 .../devguide/tools/generate-images.sh | 5 +- doc/userguide/index.rst | 1 + 45 files changed, 35 insertions(+), 645 deletions(-) delete mode 100644 doc/devguide/Makefile.am delete mode 100644 doc/devguide/Makefile.sphinx delete mode 100644 doc/devguide/_static/.gitignore delete mode 100644 doc/devguide/_static/css/suricata.css delete mode 100644 doc/devguide/conf.py rename doc/{ => userguide}/devguide/.gitignore (83%) rename doc/{ => userguide}/devguide/README.md (100%) rename doc/{ => userguide}/devguide/codebase/code-style.rst (100%) rename doc/{ => userguide}/devguide/codebase/contributing/code-submission-process.rst (100%) rename doc/{ => userguide}/devguide/codebase/contributing/index.rst (100%) rename doc/{ => userguide}/devguide/codebase/fuzz-testing.rst (100%) rename doc/{ => userguide}/devguide/codebase/img/InputCaptureExample.png (100%) rename doc/{ => userguide}/devguide/codebase/index.rst (100%) rename doc/{ => userguide}/devguide/codebase/testing.rst (100%) rename doc/{ => userguide}/devguide/codebase/unittests-c.rst (100%) rename doc/{ => userguide}/devguide/codebase/unittests-rust.rst (100%) rename doc/{ => userguide}/devguide/extending/app-layer/app-layer-frames.rst (94%) rename doc/{ => userguide}/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.msc (100%) rename doc/{ => userguide}/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.msc (100%) rename doc/{ => userguide}/devguide/extending/app-layer/diagrams/TemplateTransaction.msc (100%) rename doc/{ => userguide}/devguide/extending/app-layer/diagrams/TlsHandshake.msc (100%) rename doc/{ => userguide}/devguide/extending/app-layer/img/StreamFrames.png (100%) rename doc/{ => userguide}/devguide/extending/app-layer/index.rst (100%) rename doc/{ => userguide}/devguide/extending/app-layer/parser.rst (100%) rename doc/{ => userguide}/devguide/extending/app-layer/transactions.rst (100%) rename doc/{ => userguide}/devguide/extending/capture/index.rst (100%) rename doc/{ => userguide}/devguide/extending/decoder/index.rst (100%) rename doc/{ => userguide}/devguide/extending/detect/index.rst (100%) rename doc/{ => userguide}/devguide/extending/index.rst (100%) rename doc/{ => userguide}/devguide/extending/output/index.rst (100%) rename doc/{ => userguide}/devguide/index.rst (91%) rename doc/{ => userguide}/devguide/internals/datastructs/index.rst (100%) rename doc/{ => userguide}/devguide/internals/engines/index.rst (100%) rename doc/{ => userguide}/devguide/internals/index.rst (100%) rename doc/{ => userguide}/devguide/internals/pipeline/index.rst (100%) rename doc/{ => userguide}/devguide/internals/threading/index.rst (100%) rename doc/{ => userguide}/devguide/tools/generate-images.sh (80%) diff --git a/.clang-format b/.clang-format index 69670b574247..e1b4e41f6650 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,5 @@ # Suricata settings as per -# doc/devguide/codebase/code-style.rst +# doc/userguide/devguide/codebase/code-style.rst # # This file is set up for clang 9. For the settings available, see # https://releases.llvm.org/9.0.0/tools/clang/docs/ClangFormatStyleOptions.html diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index b50c25a6faeb..0007322ff4da 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -1157,7 +1157,6 @@ jobs: - run: make dist - name: Checking that documentation was built run: | - test -e doc/devguide/devguide.pdf test -e doc/userguide/userguide.pdf test -e doc/userguide/suricata.1 - name: Extracting suricata-verify diff --git a/configure.ac b/configure.ac index a34b71f4f04f..754237da1219 100644 --- a/configure.ac +++ b/configure.ac @@ -2534,7 +2534,7 @@ AM_CONDITIONAL([BUILD_SHARED_LIBRARY], [test "x$enable_shared" = "xyes"] && [tes AC_CONFIG_FILES(Makefile src/Makefile rust/Makefile rust/Cargo.toml rust/derive/Cargo.toml rust/.cargo/config) AC_CONFIG_FILES(qa/Makefile qa/coccinelle/Makefile) -AC_CONFIG_FILES(rules/Makefile doc/Makefile doc/userguide/Makefile doc/devguide/Makefile) +AC_CONFIG_FILES(rules/Makefile doc/Makefile doc/userguide/Makefile) AC_CONFIG_FILES(contrib/Makefile contrib/file_processor/Makefile contrib/file_processor/Action/Makefile contrib/file_processor/Processor/Makefile) AC_CONFIG_FILES(suricata.yaml etc/Makefile etc/suricata.logrotate etc/suricata.service) AC_CONFIG_FILES(python/Makefile python/suricata/config/defaults.py) diff --git a/doc/Makefile.am b/doc/Makefile.am index c2bd1d456ca6..f3cad3d0713d 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = userguide devguide +SUBDIRS = userguide EXTRA_DIST = \ AUTHORS \ diff --git a/doc/devguide/Makefile.am b/doc/devguide/Makefile.am deleted file mode 100644 index e4fe5ea309ef..000000000000 --- a/doc/devguide/Makefile.am +++ /dev/null @@ -1,73 +0,0 @@ -EXTRA_DIST = \ - conf.py \ - _static \ - index.rst \ - extending/detect/index.rst \ - extending/decoder/index.rst \ - extending/index.rst \ - extending/app-layer/index.rst \ - extending/app-layer/app-layer-frames.rst \ - extending/app-layer/parser.rst \ - extending/app-layer/transactions.rst \ - extending/app-layer/diagrams \ - extending/app-layer/img \ - extending/capture/index.rst \ - extending/output/index.rst \ - internals/engines/index.rst \ - internals/threading/index.rst \ - internals/index.rst \ - internals/pipeline/index.rst \ - internals/datastructs/index.rst \ - codebase/index.rst \ - codebase/code-style.rst \ - codebase/contributing/code-submission-process.rst \ - codebase/contributing/index.rst \ - codebase/fuzz-testing.rst \ - codebase/testing.rst \ - codebase/unittests-c.rst \ - codebase/unittests-rust.rst - -if HAVE_SPHINXBUILD -if HAVE_MSCGEN - -if HAVE_PDFLATEX -EXTRA_DIST += devguide.pdf -endif - -SPHINX_BUILD = sphinx-build -q - -html: - sysconfdir=$(sysconfdir) \ - localstatedir=$(localstatedir) \ - version=$(PACKAGE_VERSION) \ - $(SPHINX_BUILD) -W -b html -d _build/doctrees \ - $(top_srcdir)/doc/devguide _build/html - -_build/latex/Suricata.pdf: - sysconfdir=$(sysconfdir) \ - localstatedir=$(localstatedir) \ - version=$(PACKAGE_VERSION) \ - $(SPHINX_BUILD) -W -b latex -d _build/doctrees \ - $(top_srcdir)/doc/devguide _build/latex -# The Sphinx generated Makefile is GNU Make specific, so just do what -# it does here - yes, multiple passes of pdflatex is required. - cd _build/latex && pdflatex Suricata.tex - cd _build/latex && pdflatex Suricata.tex - cd _build/latex && pdflatex Suricata.tex - cd _build/latex && makeindex -s python.ist Suricata.idx - cd _build/latex && pdflatex Suricata.tex - cd _build/latex && pdflatex Suricata.tex - -devguide.pdf: _build/latex/Suricata.pdf - cp _build/latex/Suricata.pdf devguide.pdf - -pdf: devguide.pdf - -# Remove build artifacts that aren't tracked by autotools. -clean-local: - rm -rf $(top_builddir)/doc/devguide/_build - rm -f $(top_builddir)/doc/devguide/suricata.1 - rm -f $(top_builddir)/doc/devguide/devguide.pdf - -endif # HAVE_MSCGEN -endif # HAVE_SPHINXBUILD diff --git a/doc/devguide/Makefile.sphinx b/doc/devguide/Makefile.sphinx deleted file mode 100644 index bd8cddd8e9e2..000000000000 --- a/doc/devguide/Makefile.sphinx +++ /dev/null @@ -1,204 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -BUILD_IMG = ./tools/generate-images.sh - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text changes linkcheck doctest coverage gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(BUILD_IMG) - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(BUILD_IMG) - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(BUILD_IMG) - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(BUILD_IMG) - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(BUILD_IMG) - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(BUILD_IMG) - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(BUILD_IMG) - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Suricata.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Suricata.qhc" - -applehelp: - $(BUILD_IMG) - $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp - @echo - @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." - @echo "N.B. You won't be able to view it unless you put it in" \ - "~/Library/Documentation/Help or install it in your application" \ - "bundle." - -devhelp: - $(BUILD_IMG) - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/Suricata" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Suricata" - @echo "# devhelp" - -epub: - $(BUILD_IMG) - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(BUILD_IMG) - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(BUILD_IMG) - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(BUILD_IMG) - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(BUILD_IMG) - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -texinfo: - $(BUILD_IMG) - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(BUILD_IMG) - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(BUILD_IMG) - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/doc/devguide/_static/.gitignore b/doc/devguide/_static/.gitignore deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/doc/devguide/_static/css/suricata.css b/doc/devguide/_static/css/suricata.css deleted file mode 100644 index 5fdbbf11181a..000000000000 --- a/doc/devguide/_static/css/suricata.css +++ /dev/null @@ -1,27 +0,0 @@ -.example-rule { - padding: 12px 12px; - font-family: Consolas,"Andale Mono WT","Andale Mono","Lucida Console","Lucida Sans Typewriter","DejaVu Sans Mono","Bitstream Vera Sans Mono","Liberation Mono","Nimbus Mono L",Monaco,"Courier New",Courier,monospace; - font-size: 12px; - line-height: 1.5; - display: block; - overflow: auto; - color: #404040; - - border: 1px solid #e1e4e5; - background: #fff; - margin: 1px 0 24px 0; -} -.example-rule-emphasis { - color: #f00; - font-weight: bold; -} - -.example-rule-action { - color: #f00; -} -.example-rule-header { - color: #090; -} -.example-rule-options { - color: #00f; -} diff --git a/doc/devguide/conf.py b/doc/devguide/conf.py deleted file mode 100644 index 5e5f8d1f6ce2..000000000000 --- a/doc/devguide/conf.py +++ /dev/null @@ -1,315 +0,0 @@ -# -*- coding: utf-8 -*- -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os -import shlex -import re -import subprocess - -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Suricata' -copyright = u'2020, OISF' -author = u'OISF' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. - -# Attempt to extract the version configure.ac. -try: - version = os.environ.get('version', None) - if not version: - version = re.search( - "AC_INIT\(\[suricata\],\s*\[(.*)?\]\)", - open("../../configure.ac").read()).groups()[0] - if not version: - version = "unknown" -except: - version = "unknown" -release = version - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [ - '_build', -] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -if not on_rtd: - # Attempt to use the read the docs theme. - try: - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - except: - html_theme = 'default' -else: - html_context = { - 'css_files': [ - 'https://media.readthedocs.org/css/sphinx_rtd_theme.css', - 'https://media.readthedocs.org/css/readthedocs-doc-embed.css', - '_static/css/suricata.css', - ], - } - -def setup(app): - # Generate images. - subprocess.check_call("./tools/generate-images.sh") - if not on_rtd: - if hasattr(app, 'add_css_file'): - app.add_css_file('css/suricata.css') - else: - app.add_stylesheet('css/suricata.css') - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -#html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'suricatadevdoc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', - -# Latex figure (float) alignment -#'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'Suricata.tex', u'Suricata Developer Guide', - u'OISF', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'Suricata', u'Suricata Developer Guide', - author, 'Suricata', 'High performance NSM tool', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False - -rst_epilog = """ -.. |sysconfdir| replace:: %(sysconfdir)s -.. |localstatedir| replace:: %(localstatedir)s -""" % { - "sysconfdir": os.getenv("sysconfdir", "/etc"), - "localstatedir": os.getenv("localstatedir", "/var"), -} diff --git a/doc/userguide/Makefile.am b/doc/userguide/Makefile.am index b6c9888ba7fc..e535462a8a84 100644 --- a/doc/userguide/Makefile.am +++ b/doc/userguide/Makefile.am @@ -6,6 +6,7 @@ EXTRA_DIST = \ command-line-options.rst \ conf.py \ configuration \ + devguide \ file-extraction \ index.rst \ upgrade \ @@ -88,5 +89,6 @@ clean-local: rm -rf $(top_builddir)/doc/userguide/_build rm -f $(top_builddir)/doc/userguide/suricata*.1 rm -f $(top_builddir)/doc/userguide/userguide.pdf + rm -f $(top_builddir)/doc/userguide/devguide/extending/app-layer/diagrams/*.png endif # HAVE_SPHINXBUILD diff --git a/doc/userguide/README.md b/doc/userguide/README.md index 65776b225e15..b625625c17da 100644 --- a/doc/userguide/README.md +++ b/doc/userguide/README.md @@ -1,7 +1,8 @@ # Suricata User Guide -This directory contains the Suricata Guide. The -[Sphinx Document Generator](http://sphinx-doc.org) is used to build the +This directory contains the Suricata Guide. The Suricata Developer's guide +is included as a chapter of the Guide. +The [Sphinx Document Generator](http://sphinx-doc.org) is used to build the documentation. For a primer os reStructuredText see the [reStructuredText Primer](http://sphinx-doc.org/rest.html). diff --git a/doc/userguide/conf.py b/doc/userguide/conf.py index baf4726e6238..1cb1287d2aee 100644 --- a/doc/userguide/conf.py +++ b/doc/userguide/conf.py @@ -16,6 +16,7 @@ import os import shlex import re +import subprocess on_rtd = os.environ.get('READTHEDOCS', None) == 'True' @@ -50,7 +51,7 @@ # General information about the project. project = u'Suricata' -copyright = u'2016-2019, OISF' +copyright = u'2016-2022, OISF' author = u'OISF' # The version info for the project you're documenting, acts as replacement for @@ -134,11 +135,6 @@ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] except: html_theme = 'default' - def setup(app): - if hasattr(app, 'add_css_file'): - app.add_css_file('css/suricata.css') - else: - app.add_stylesheet('css/suricata.css') else: html_context = { 'css_files': [ @@ -148,6 +144,15 @@ def setup(app): ], } +def setup(app): + # Generate images. + subprocess.check_call("./devguide/tools/generate-images.sh") + if not on_rtd: + if hasattr(app, 'add_css_file'): + app.add_css_file('css/suricata.css') + else: + app.add_stylesheet('css/suricata.css') + # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. diff --git a/doc/devguide/.gitignore b/doc/userguide/devguide/.gitignore similarity index 83% rename from doc/devguide/.gitignore rename to doc/userguide/devguide/.gitignore index faba9ed00cb4..507d52cdc509 100644 --- a/doc/devguide/.gitignore +++ b/doc/userguide/devguide/.gitignore @@ -1,2 +1 @@ -_build extending/app-layer/diagrams/*.png diff --git a/doc/devguide/README.md b/doc/userguide/devguide/README.md similarity index 100% rename from doc/devguide/README.md rename to doc/userguide/devguide/README.md diff --git a/doc/devguide/codebase/code-style.rst b/doc/userguide/devguide/codebase/code-style.rst similarity index 100% rename from doc/devguide/codebase/code-style.rst rename to doc/userguide/devguide/codebase/code-style.rst diff --git a/doc/devguide/codebase/contributing/code-submission-process.rst b/doc/userguide/devguide/codebase/contributing/code-submission-process.rst similarity index 100% rename from doc/devguide/codebase/contributing/code-submission-process.rst rename to doc/userguide/devguide/codebase/contributing/code-submission-process.rst diff --git a/doc/devguide/codebase/contributing/index.rst b/doc/userguide/devguide/codebase/contributing/index.rst similarity index 100% rename from doc/devguide/codebase/contributing/index.rst rename to doc/userguide/devguide/codebase/contributing/index.rst diff --git a/doc/devguide/codebase/fuzz-testing.rst b/doc/userguide/devguide/codebase/fuzz-testing.rst similarity index 100% rename from doc/devguide/codebase/fuzz-testing.rst rename to doc/userguide/devguide/codebase/fuzz-testing.rst diff --git a/doc/devguide/codebase/img/InputCaptureExample.png b/doc/userguide/devguide/codebase/img/InputCaptureExample.png similarity index 100% rename from doc/devguide/codebase/img/InputCaptureExample.png rename to doc/userguide/devguide/codebase/img/InputCaptureExample.png diff --git a/doc/devguide/codebase/index.rst b/doc/userguide/devguide/codebase/index.rst similarity index 100% rename from doc/devguide/codebase/index.rst rename to doc/userguide/devguide/codebase/index.rst diff --git a/doc/devguide/codebase/testing.rst b/doc/userguide/devguide/codebase/testing.rst similarity index 100% rename from doc/devguide/codebase/testing.rst rename to doc/userguide/devguide/codebase/testing.rst diff --git a/doc/devguide/codebase/unittests-c.rst b/doc/userguide/devguide/codebase/unittests-c.rst similarity index 100% rename from doc/devguide/codebase/unittests-c.rst rename to doc/userguide/devguide/codebase/unittests-c.rst diff --git a/doc/devguide/codebase/unittests-rust.rst b/doc/userguide/devguide/codebase/unittests-rust.rst similarity index 100% rename from doc/devguide/codebase/unittests-rust.rst rename to doc/userguide/devguide/codebase/unittests-rust.rst diff --git a/doc/devguide/extending/app-layer/app-layer-frames.rst b/doc/userguide/devguide/extending/app-layer/app-layer-frames.rst similarity index 94% rename from doc/devguide/extending/app-layer/app-layer-frames.rst rename to doc/userguide/devguide/extending/app-layer/app-layer-frames.rst index 115532162c95..ec16ea91c9b2 100644 --- a/doc/devguide/extending/app-layer/app-layer-frames.rst +++ b/doc/userguide/devguide/extending/app-layer/app-layer-frames.rst @@ -75,7 +75,7 @@ This section shows how Frame support is added in Rust, using examples from the ` **Define the frame types**. The frame types are defined as an enum. In Rust, make sure to derive from the ``AppLayerFrameType``: -.. literalinclude:: ../../../../rust/src/sip/sip.rs +.. literalinclude:: ../../../../../rust/src/sip/sip.rs :caption: rust/src/sip/sip.rs :language: rust :start-after: // app-layer-frame-documentation tag start: FrameType enum @@ -83,7 +83,7 @@ This section shows how Frame support is added in Rust, using examples from the ` **Frame registering**. Some understanding of the parser will be needed in order to find where the frames should be registered. It makes sense that it will happen when the input stream is being parsed into records. See when some pdu and request frames are created for SIP: -.. literalinclude:: ../../../../rust/src/sip/sip.rs +.. literalinclude:: ../../../../../rust/src/sip/sip.rs :caption: rust/src/sip/sip.rs :language: rust :start-after: // app-layer-frame-documentation tag start: parse_request @@ -96,7 +96,7 @@ This section shows how Frame support is added in Rust, using examples from the ` **Use the Frame API or build upon them as needed**. These are the frame registration functions highlighted above: -.. literalinclude:: ../../../../rust/src/sip/sip.rs +.. literalinclude:: ../../../../../rust/src/sip/sip.rs :caption: rust/src/sip/sip.rs :language: rust :start-after: // app-layer-frame-documentation tag start: function to add frames @@ -104,7 +104,7 @@ This section shows how Frame support is added in Rust, using examples from the ` **Register relevant frame callbacks.** As these are inferred from the ``#[derive(AppLayerFrameType)]`` statement, all that is needed is: -.. literalinclude:: ../../../../rust/src/sip/sip.rs +.. literalinclude:: ../../../../../rust/src/sip/sip.rs :caption: rust/src/sip/sip.rs :language: rust :start-at: get_frame_id_by_name @@ -117,7 +117,7 @@ This section shows how Frame support is added in Rust, using examples from the ` The telnet parser has examples of using the Frame API directly for registering telnet frames, and also illustrates how that is done when length is not yet known: -.. literalinclude:: ../../../../rust/src/telnet/telnet.rs +.. literalinclude:: ../../../../../rust/src/telnet/telnet.rs :caption: rust/src/telnet/telnet.rs :language: rust :start-after: // app-layer-frame-documentation tag start: parse_request @@ -127,7 +127,7 @@ The telnet parser has examples of using the Frame API directly for registering t We then update length later on (note especially lines 3 and 10): -.. literalinclude:: ../../../../rust/src/telnet/telnet.rs +.. literalinclude:: ../../../../../rust/src/telnet/telnet.rs :caption: rust/src/telnet/telnet.rs :language: rust :start-after: // app-layer-frame-documentation tag start: update frame_len @@ -145,7 +145,7 @@ The Frame API calls parameters represent: ``StreamSlice`` contains the input data to the parser, alongside other Stream-related data important in parsing context. Definition is found in *applayer.rs*: -.. literalinclude:: ../../../../rust/src/applayer.rs +.. literalinclude:: ../../../../../rust/src/applayer.rs :caption: rust/src/applayer.rs :language: rust :start-at: pub struct StreamSlice @@ -159,7 +159,7 @@ Implementing Frame support in C involves a bit more manual work, as one cannot m Defining the frame types with the enum means: -.. literalinclude:: ../../../../src/app-layer-htp.c +.. literalinclude:: ../../../../../src/app-layer-htp.c :caption: src/app-layer-htp.c :start-after: /* app-layer-frame-documentation tag start: HttpFrameTypes :end-before: /* app-layer-frame-documentation tag end: HttpFrameTypes @@ -167,7 +167,7 @@ Defining the frame types with the enum means: The HTTP parser uses the Frame registration functions from the C API (``app-layer-frames.c``) directly for registering request Frames. Here we also don't know the length yet. The ``0`` indicates flow direction: ``toserver``, and ``1`` would be used for ``toclient``: -.. literalinclude:: ../../../../src/app-layer-htp.c +.. literalinclude:: ../../../../../src/app-layer-htp.c :caption: src/app-layer-htp.c :start-after: /* app-layer-frame-documentation tag start: frame registration http request :end-before: /* app-layer-frame-documentation tag end: frame registration http request @@ -175,7 +175,7 @@ The HTTP parser uses the Frame registration functions from the C API (``app-laye Updating ``frame->len`` later: -.. literalinclude:: ../../../../src/app-layer-htp.c +.. literalinclude:: ../../../../../src/app-layer-htp.c :caption: src/app-layer-htp.c :start-after: /* app-layer-frame-documentation tag start: updating frame->len :end-before: /* app-layer-frame-documentation tag end: updating frame->len @@ -183,7 +183,7 @@ Updating ``frame->len`` later: Register relevant callbacks (note that the actual functions will also have to be written, for C): -.. literalinclude:: ../../../../src/app-layer-htp.c +.. literalinclude:: ../../../../../src/app-layer-htp.c :caption: src/app-layer-htp.c :language: c :start-after: /* app-layer-frame-documentation tag start: registering relevant callbacks diff --git a/doc/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.msc b/doc/userguide/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.msc similarity index 100% rename from doc/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.msc rename to doc/userguide/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.msc diff --git a/doc/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.msc b/doc/userguide/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.msc similarity index 100% rename from doc/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.msc rename to doc/userguide/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.msc diff --git a/doc/devguide/extending/app-layer/diagrams/TemplateTransaction.msc b/doc/userguide/devguide/extending/app-layer/diagrams/TemplateTransaction.msc similarity index 100% rename from doc/devguide/extending/app-layer/diagrams/TemplateTransaction.msc rename to doc/userguide/devguide/extending/app-layer/diagrams/TemplateTransaction.msc diff --git a/doc/devguide/extending/app-layer/diagrams/TlsHandshake.msc b/doc/userguide/devguide/extending/app-layer/diagrams/TlsHandshake.msc similarity index 100% rename from doc/devguide/extending/app-layer/diagrams/TlsHandshake.msc rename to doc/userguide/devguide/extending/app-layer/diagrams/TlsHandshake.msc diff --git a/doc/devguide/extending/app-layer/img/StreamFrames.png b/doc/userguide/devguide/extending/app-layer/img/StreamFrames.png similarity index 100% rename from doc/devguide/extending/app-layer/img/StreamFrames.png rename to doc/userguide/devguide/extending/app-layer/img/StreamFrames.png diff --git a/doc/devguide/extending/app-layer/index.rst b/doc/userguide/devguide/extending/app-layer/index.rst similarity index 100% rename from doc/devguide/extending/app-layer/index.rst rename to doc/userguide/devguide/extending/app-layer/index.rst diff --git a/doc/devguide/extending/app-layer/parser.rst b/doc/userguide/devguide/extending/app-layer/parser.rst similarity index 100% rename from doc/devguide/extending/app-layer/parser.rst rename to doc/userguide/devguide/extending/app-layer/parser.rst diff --git a/doc/devguide/extending/app-layer/transactions.rst b/doc/userguide/devguide/extending/app-layer/transactions.rst similarity index 100% rename from doc/devguide/extending/app-layer/transactions.rst rename to doc/userguide/devguide/extending/app-layer/transactions.rst diff --git a/doc/devguide/extending/capture/index.rst b/doc/userguide/devguide/extending/capture/index.rst similarity index 100% rename from doc/devguide/extending/capture/index.rst rename to doc/userguide/devguide/extending/capture/index.rst diff --git a/doc/devguide/extending/decoder/index.rst b/doc/userguide/devguide/extending/decoder/index.rst similarity index 100% rename from doc/devguide/extending/decoder/index.rst rename to doc/userguide/devguide/extending/decoder/index.rst diff --git a/doc/devguide/extending/detect/index.rst b/doc/userguide/devguide/extending/detect/index.rst similarity index 100% rename from doc/devguide/extending/detect/index.rst rename to doc/userguide/devguide/extending/detect/index.rst diff --git a/doc/devguide/extending/index.rst b/doc/userguide/devguide/extending/index.rst similarity index 100% rename from doc/devguide/extending/index.rst rename to doc/userguide/devguide/extending/index.rst diff --git a/doc/devguide/extending/output/index.rst b/doc/userguide/devguide/extending/output/index.rst similarity index 100% rename from doc/devguide/extending/output/index.rst rename to doc/userguide/devguide/extending/output/index.rst diff --git a/doc/devguide/index.rst b/doc/userguide/devguide/index.rst similarity index 91% rename from doc/devguide/index.rst rename to doc/userguide/devguide/index.rst index 48007de2c3c7..b6b5fbc1c7d3 100644 --- a/doc/devguide/index.rst +++ b/doc/userguide/devguide/index.rst @@ -2,7 +2,6 @@ Suricata Developer Guide ======================== .. toctree:: - :numbered: :maxdepth: 2 codebase/index.rst diff --git a/doc/devguide/internals/datastructs/index.rst b/doc/userguide/devguide/internals/datastructs/index.rst similarity index 100% rename from doc/devguide/internals/datastructs/index.rst rename to doc/userguide/devguide/internals/datastructs/index.rst diff --git a/doc/devguide/internals/engines/index.rst b/doc/userguide/devguide/internals/engines/index.rst similarity index 100% rename from doc/devguide/internals/engines/index.rst rename to doc/userguide/devguide/internals/engines/index.rst diff --git a/doc/devguide/internals/index.rst b/doc/userguide/devguide/internals/index.rst similarity index 100% rename from doc/devguide/internals/index.rst rename to doc/userguide/devguide/internals/index.rst diff --git a/doc/devguide/internals/pipeline/index.rst b/doc/userguide/devguide/internals/pipeline/index.rst similarity index 100% rename from doc/devguide/internals/pipeline/index.rst rename to doc/userguide/devguide/internals/pipeline/index.rst diff --git a/doc/devguide/internals/threading/index.rst b/doc/userguide/devguide/internals/threading/index.rst similarity index 100% rename from doc/devguide/internals/threading/index.rst rename to doc/userguide/devguide/internals/threading/index.rst diff --git a/doc/devguide/tools/generate-images.sh b/doc/userguide/devguide/tools/generate-images.sh similarity index 80% rename from doc/devguide/tools/generate-images.sh rename to doc/userguide/devguide/tools/generate-images.sh index 52d83e19f1e3..5db3f953cfde 100755 --- a/doc/devguide/tools/generate-images.sh +++ b/doc/userguide/devguide/tools/generate-images.sh @@ -3,9 +3,12 @@ # Script to generate Sequence Diagram images with mscgen # +parent_path=$(cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P) + set -e -cd extending/app-layer/diagrams +cd "$parent_path" +cd ../extending/app-layer/diagrams for FILE in *.msc ; do # call mscgen and convert each file in images dir diff --git a/doc/userguide/index.rst b/doc/userguide/index.rst index 5e4821988d13..0f1a069cd3a4 100644 --- a/doc/userguide/index.rst +++ b/doc/userguide/index.rst @@ -29,3 +29,4 @@ Suricata User Guide manpages/index.rst acknowledgements licenses/index.rst + devguide/index.rst From 5d63613c4b73722464b00e4282243005723375b8 Mon Sep 17 00:00:00 2001 From: Juliana Fajardini Date: Fri, 18 Feb 2022 16:28:27 +0000 Subject: [PATCH 05/14] devguide: add watermark to sequence diagrams Make it more evident that the sequence diagrams in the transactions page are generated with Mscgen --- .../app-layer/diagrams/DnsUnidirectionalTransactions.msc | 3 +++ .../app-layer/diagrams/HTTP2BidirectionalTransaction.msc | 2 ++ .../extending/app-layer/diagrams/TemplateTransaction.msc | 3 +++ .../devguide/extending/app-layer/diagrams/TlsHandshake.msc | 3 +++ 4 files changed, 11 insertions(+) diff --git a/doc/userguide/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.msc b/doc/userguide/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.msc index f5bd588f685d..43dd653aa570 100644 --- a/doc/userguide/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.msc +++ b/doc/userguide/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.msc @@ -13,4 +13,7 @@ msc { |||; b =>> a [ label = "DNS Response" ]; --- [ label = "Transaction 2 Completed" ]; + + |||; + ||| [label="[ generated with Mscgen ]", textcolor="gray"]; } diff --git a/doc/userguide/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.msc b/doc/userguide/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.msc index 0ba93e6511ba..3c3484f72276 100644 --- a/doc/userguide/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.msc +++ b/doc/userguide/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.msc @@ -13,6 +13,8 @@ msc { b =>> a [ label = "Response" ]; |||; --- [ label = "Transaction Completed" ]; + |||; + ||| [label="[ generated with Mscgen ]", textcolor="gray"]; } # Reference: https://tools.ietf.org/html/rfc7540#section-8.1 diff --git a/doc/userguide/devguide/extending/app-layer/diagrams/TemplateTransaction.msc b/doc/userguide/devguide/extending/app-layer/diagrams/TemplateTransaction.msc index 73a82d15347e..3a5a308a095b 100644 --- a/doc/userguide/devguide/extending/app-layer/diagrams/TemplateTransaction.msc +++ b/doc/userguide/devguide/extending/app-layer/diagrams/TemplateTransaction.msc @@ -12,4 +12,7 @@ msc { b =>> a [ label = "Response ('3:Bye')" ]; |||; --- [ label = "Transaction Completed" ]; + + |||; + ||| [label="[ generated with Mscgen ]", textcolor="gray"]; } diff --git a/doc/userguide/devguide/extending/app-layer/diagrams/TlsHandshake.msc b/doc/userguide/devguide/extending/app-layer/diagrams/TlsHandshake.msc index e21ee9be6b3d..34a025d1d00f 100644 --- a/doc/userguide/devguide/extending/app-layer/diagrams/TlsHandshake.msc +++ b/doc/userguide/devguide/extending/app-layer/diagrams/TlsHandshake.msc @@ -28,4 +28,7 @@ msc { # TLS_STATE_FINISHED = 3 a abox b [ label = "TLS_STATE_FINISHED" ]; --- [ label = "Transaction Completed" ]; + + |||; + ||| [label="[ generated with Mscgen ]", textcolor="gray"]; } From 67af1504b36064f4380933c489dc62d552348e43 Mon Sep 17 00:00:00 2001 From: Juliana Fajardini Date: Mon, 14 Feb 2022 18:29:00 +0000 Subject: [PATCH 06/14] devguide: drop use of mscgen script in builds/make Currently, it seems easier to upload the diagram images to git than to try to make the image generation script work with out of the tree builds and other corner cases. This means, however, that one must activelly remember to update msc diagram files, run the script and re-add new png files, if those ever need to be updated. To raise awareness to that, a watermark was added to the diagram images. Also removed configuration steps that added mscgen as dependency (locally and for workflow builds and readthedocs). --- .github/workflows/builds.yml | 1 - .readthedocs.yaml | 4 ---- Makefile.am | 3 ++- configure.ac | 12 ------------ doc/userguide/Makefile.am | 1 - doc/userguide/conf.py | 14 +++++--------- doc/userguide/devguide/.gitignore | 1 - .../diagrams/DnsUnidirectionalTransactions.png | Bin 0 -> 12620 bytes .../diagrams/HTTP2BidirectionalTransaction.png | Bin 0 -> 9344 bytes .../app-layer/diagrams/TemplateTransaction.png | Bin 0 -> 11707 bytes .../app-layer/diagrams/TlsHandshake.png | Bin 0 -> 27884 bytes .../tools => scripts}/generate-images.sh | 2 +- 12 files changed, 8 insertions(+), 30 deletions(-) delete mode 100644 doc/userguide/devguide/.gitignore create mode 100644 doc/userguide/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.png create mode 100644 doc/userguide/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.png create mode 100644 doc/userguide/devguide/extending/app-layer/diagrams/TemplateTransaction.png create mode 100644 doc/userguide/devguide/extending/app-layer/diagrams/TlsHandshake.png rename {doc/userguide/devguide/tools => scripts}/generate-images.sh (90%) diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 0007322ff4da..b393e21ac537 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -1115,7 +1115,6 @@ jobs: libjansson-dev \ libpython2.7 \ make \ - mscgen \ parallel \ python3-yaml \ rustc \ diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f3fa6e66c544..bc23deb5f082 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -7,7 +7,3 @@ python: # Use an empty install section to avoid RTD from picking up a non-python # requirements.txt file. install: [] - -build: - apt_packages: - - mscgen diff --git a/Makefile.am b/Makefile.am index cb101f708b17..b061fa475eb1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,7 +6,8 @@ EXTRA_DIST = ChangeLog COPYING LICENSE suricata.yaml.in \ threshold.config \ $(SURICATA_UPDATE_DIR) \ lua \ - acsite.m4 + acsite.m4 \ + scripts/generate-images.sh SUBDIRS = $(HTP_DIR) rust src qa rules doc contrib etc python ebpf \ $(SURICATA_UPDATE_DIR) diff --git a/configure.ac b/configure.ac index 754237da1219..89ee81adb4ca 100644 --- a/configure.ac +++ b/configure.ac @@ -2168,18 +2168,6 @@ fi AC_DEFINE([CLS],[64],[L1 cache line size]) fi -# mscgen for devguide images - AC_PATH_PROG([HAVE_MSCGEN], mscgen, "no") - if test "$HAVE_MSCGEN" = "no"; then - enable_mscgen=no - echo "WARNING! mscgen package not installed." - echo " Devguide images won't be generated!" - echo " Get mscgen package:" - echo " https://www.mcternan.me.uk/mscgen/" - echo " or install it from your distribution" - fi - AM_CONDITIONAL([HAVE_MSCGEN], [test "x$enable_mscgen" != "xno" ]) - # sphinx for documentation AC_PATH_PROG(HAVE_SPHINXBUILD, sphinx-build, "no") if test "$HAVE_SPHINXBUILD" = "no"; then diff --git a/doc/userguide/Makefile.am b/doc/userguide/Makefile.am index e535462a8a84..8da69b757e79 100644 --- a/doc/userguide/Makefile.am +++ b/doc/userguide/Makefile.am @@ -89,6 +89,5 @@ clean-local: rm -rf $(top_builddir)/doc/userguide/_build rm -f $(top_builddir)/doc/userguide/suricata*.1 rm -f $(top_builddir)/doc/userguide/userguide.pdf - rm -f $(top_builddir)/doc/userguide/devguide/extending/app-layer/diagrams/*.png endif # HAVE_SPHINXBUILD diff --git a/doc/userguide/conf.py b/doc/userguide/conf.py index 1cb1287d2aee..36570c1654f4 100644 --- a/doc/userguide/conf.py +++ b/doc/userguide/conf.py @@ -135,6 +135,11 @@ html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] except: html_theme = 'default' + def setup(app): + if hasattr(app, 'add_css_file'): + app.add_css_file('css/suricata.css') + else: + app.add_stylesheet('css/suricata.css') else: html_context = { 'css_files': [ @@ -144,15 +149,6 @@ ], } -def setup(app): - # Generate images. - subprocess.check_call("./devguide/tools/generate-images.sh") - if not on_rtd: - if hasattr(app, 'add_css_file'): - app.add_css_file('css/suricata.css') - else: - app.add_stylesheet('css/suricata.css') - # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. diff --git a/doc/userguide/devguide/.gitignore b/doc/userguide/devguide/.gitignore deleted file mode 100644 index 507d52cdc509..000000000000 --- a/doc/userguide/devguide/.gitignore +++ /dev/null @@ -1 +0,0 @@ -extending/app-layer/diagrams/*.png diff --git a/doc/userguide/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.png b/doc/userguide/devguide/extending/app-layer/diagrams/DnsUnidirectionalTransactions.png new file mode 100644 index 0000000000000000000000000000000000000000..611ae3167fdb6bc476286c93ae266253fa2f4ee4 GIT binary patch literal 12620 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 literal 0 HcmV?d00001 diff --git a/doc/userguide/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.png b/doc/userguide/devguide/extending/app-layer/diagrams/HTTP2BidirectionalTransaction.png new file mode 100644 index 0000000000000000000000000000000000000000..02de5dc6dde07012e56ed8f04becf0b1f19d4e92 GIT binary patch literal 9344 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%0Dpjz&gb3vW%98@|qzGXOR)jeH} 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 literal 0 HcmV?d00001 diff --git a/doc/userguide/devguide/tools/generate-images.sh b/scripts/generate-images.sh similarity index 90% rename from doc/userguide/devguide/tools/generate-images.sh rename to scripts/generate-images.sh index 5db3f953cfde..b50b75e0c45b 100755 --- a/doc/userguide/devguide/tools/generate-images.sh +++ b/scripts/generate-images.sh @@ -8,7 +8,7 @@ parent_path=$(cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P) set -e cd "$parent_path" -cd ../extending/app-layer/diagrams +cd ../doc/userguide/devguide/extending/app-layer/diagrams for FILE in *.msc ; do # call mscgen and convert each file in images dir From e0c8dba7ac6eaa757daf80245688f9f2b2496eff Mon Sep 17 00:00:00 2001 From: Juliana Fajardini Date: Wed, 9 Feb 2022 19:46:24 +0000 Subject: [PATCH 07/14] userguide: dynamically determine copyright date This uses the date of doc generation to determine the copyright date for the trailing date. Based on Jeff Lucovsky solution. --- doc/userguide/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/userguide/conf.py b/doc/userguide/conf.py index 36570c1654f4..1474dd87afb8 100644 --- a/doc/userguide/conf.py +++ b/doc/userguide/conf.py @@ -17,6 +17,7 @@ import shlex import re import subprocess +import datetime on_rtd = os.environ.get('READTHEDOCS', None) == 'True' @@ -51,7 +52,8 @@ # General information about the project. project = u'Suricata' -copyright = u'2016-2022, OISF' +end_year = datetime.datetime.now().date().year +copyright = u'2016-{}, OISF'.format(end_year) author = u'OISF' # The version info for the project you're documenting, acts as replacement for From 55843aee8e372a2ff1aad70e782bdb91e66195a4 Mon Sep 17 00:00:00 2001 From: Juliana Fajardini Date: Tue, 15 Feb 2022 13:06:50 +0000 Subject: [PATCH 08/14] devguide: update readme Use it to explain how to go about the sequence diagram images (generation, updating, what is mscgen etc). Also remove portion that referred to Sphinx builds, as these don't make sense now. --- doc/userguide/devguide/README.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/doc/userguide/devguide/README.md b/doc/userguide/devguide/README.md index e5086ced2cd3..c5d80eb91e8c 100644 --- a/doc/userguide/devguide/README.md +++ b/doc/userguide/devguide/README.md @@ -1,14 +1,9 @@ # Suricata Developer Guide -This directory contains the Suricata Developer Guide. The -[Sphinx Document Generator](http://sphinx-doc.org) is used to build the -documentation. For a primer os reStructuredText see the -[reStructuredText Primer](http://sphinx-doc.org/rest.html). +This directory contains the Suricata Developer's Guide. It is built as part of the Suricata Userguide. -## Verifying Changes +The Sequence Diagrams seen in the Transactions documentation are generated with Mscgen. Mscgen is a small program to parse Message Sequence Charts that can be represented as text and can then converted to image. -There are a number of output formats to choose from when making the source documentation locally (e.g. html, pdf, man). +If you need to update the diagrams, please edit the ``.msc`` files present in the diagrams directory (extending/app-layer/diagrams). Once those have been changed, in the ``scripts`` directory (in the main Suricata dir) there's a scrip that will generate images for all files: ``generate-images.sh`` (you'll have to install Mscgen for that to work). -The documentation source can be built with `make -f Makefile.sphinx html`. Substitute the 'html' word for desired output format. - -There are different application dependencies based on the output desired. +More info about Mscgen can be found at: https://www.mcternan.me.uk/mscgen/ From 54d34c96f5119116a5db734f7657e7b0e13ac9fe Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 24 Mar 2022 16:53:31 +0100 Subject: [PATCH 09/14] files: open/log debug validation bugon Meant to find more cases where there is a mismatch. --- src/output-file.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/output-file.c b/src/output-file.c index c2a70275401a..832e2e992304 100644 --- a/src/output-file.c +++ b/src/output-file.c @@ -96,11 +96,14 @@ int OutputRegisterFileLogger(LoggerId id, const char *name, FileLogger LogFunc, static void CloseFile(const Packet *p, Flow *f, File *file) { + DEBUG_VALIDATE_BUG_ON((file->flags & FILE_LOGGED) != 0); void *txv = AppLayerParserGetTx(p->proto, f->alproto, f->alstate, file->txid); if (txv) { AppLayerTxData *txd = AppLayerParserGetTxData(p->proto, f->alproto, txv); - if (txd) + if (txd) { txd->files_logged++; + DEBUG_VALIDATE_BUG_ON(txd->files_logged > txd->files_opened); + } } file->flags |= FILE_LOGGED; } From b9cd502249c5c8ee8f6de33b36afef9daef3512a Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Fri, 25 Mar 2022 10:36:03 +0100 Subject: [PATCH 10/14] smb: convert 'close' parser to function --- rust/src/smb/smb1_records.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rust/src/smb/smb1_records.rs b/rust/src/smb/smb1_records.rs index 1d3b175b285c..1a2388a8b06c 100644 --- a/rust/src/smb/smb1_records.rs +++ b/rust/src/smb/smb1_records.rs @@ -772,14 +772,14 @@ pub struct SmbRequestCloseRecord<'a> { pub fid: &'a[u8], } -named!(pub parse_smb1_close_request_record, - do_parse!( - take!(1) - >> fid: take!(2) - >> (SmbRequestCloseRecord { - fid:fid, - })) -); +pub fn parse_smb1_close_request_record(i: &[u8]) -> IResult<&[u8], SmbRequestCloseRecord> { + let (i, _) = take(1_usize)(i)?; + let (i, fid) = take(2_usize)(i)?; + let record = SmbRequestCloseRecord { + fid, + }; + Ok((i, record)) +} #[derive(Debug,PartialEq)] pub struct SmbVersion<> { From b336882008d3640973fa71be6f36f3de33d3cd25 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Fri, 25 Mar 2022 11:17:23 +0100 Subject: [PATCH 11/14] smb1: apply close to direction Instead of closing files in both direction when receiving a close request, close only toserver files for the request and close toclient on receiving a response. --- rust/src/smb/smb1.rs | 58 ++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index f503401009f3..269802c0beec 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2021 Open Information Security Foundation +/* Copyright (C) 2018-2022 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -144,39 +144,19 @@ pub fn smb1_check_tx(cmd: u8) -> bool { } } -fn smb1_close_file(state: &mut SMBState, fid: &Vec) +fn smb1_close_file(state: &mut SMBState, fid: &Vec, direction: Direction) { - // we can have created 2 txs for a FID: one for reads - // and one for writes. So close both. - match state.get_file_tx_by_fuid(fid, Direction::ToServer) { - Some((tx, files, flags)) => { - SCLogDebug!("found tx {}", tx.id); - if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { - if !tx.request_done { - SCLogDebug!("closing file tx {} FID {:?}", tx.id, fid); - tdf.file_tracker.close(files, flags); - tx.request_done = true; - tx.response_done = true; - SCLogDebug!("tx {} is done", tx.id); - } - } - }, - None => { }, - } - match state.get_file_tx_by_fuid(fid, Direction::ToClient) { - Some((tx, files, flags)) => { - SCLogDebug!("found tx {}", tx.id); - if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { - if !tx.request_done { - SCLogDebug!("closing file tx {} FID {:?}", tx.id, fid); - tdf.file_tracker.close(files, flags); - tx.request_done = true; - tx.response_done = true; - SCLogDebug!("tx {} is done", tx.id); - } + if let Some((tx, files, flags)) = state.get_file_tx_by_fuid(fid, direction) { + SCLogDebug!("found tx {}", tx.id); + if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { + if !tx.request_done { + SCLogDebug!("closing file tx {} FID {:?}", tx.id, fid); + tdf.file_tracker.close(files, flags); + tx.request_done = true; + tx.response_done = true; + SCLogDebug!("tx {} is done", tx.id); } - }, - None => { }, + } } } @@ -551,8 +531,10 @@ fn smb1_request_record_one<'b>(state: &mut SMBState, r: &SmbRecord<'b>, command: Ok((_, cd)) => { let mut fid = cd.fid.to_vec(); fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); + state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), fid.to_vec()); + SCLogDebug!("closing FID {:?}/{:?}", cd.fid, fid); - smb1_close_file(state, &fid); + smb1_close_file(state, &fid, Direction::ToServer); }, _ => { events.push(SMBEvent::MalformedData); @@ -785,6 +767,14 @@ fn smb1_response_record_one<'b>(state: &mut SMBState, r: &SmbRecord<'b>, command false } }, + SMB1_COMMAND_CLOSE => { + let fid = state.ssn2vec_map.remove(&SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID)); + if let Some(fid) = fid { + SCLogDebug!("closing FID {:?}", fid); + smb1_close_file(state, &fid, Direction::ToClient); + } + false + }, SMB1_COMMAND_TRANS => { smb1_trans_response_record(state, r); true @@ -1021,7 +1011,7 @@ pub fn smb1_write_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>, an if command == SMB1_COMMAND_WRITE_AND_CLOSE { SCLogDebug!("closing FID {:?}", file_fid); - smb1_close_file(state, &file_fid); + smb1_close_file(state, &file_fid, Direction::ToServer); } }, _ => { From 6d30f4442c400e7b27726267b94b7bf5404881c4 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Fri, 25 Mar 2022 14:38:40 +0100 Subject: [PATCH 12/14] http2: fix file accounting for ranged files Increment files_opened for tx that 'gets' reassembled ranged file --- rust/src/core.rs | 2 +- rust/src/http2/http2.rs | 2 +- rust/src/http2/range.rs | 19 +++++++++++-------- src/app-layer-htp-file.c | 9 ++++++++- src/app-layer-htp-file.h | 2 +- src/rust-context.h | 2 +- 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/rust/src/core.rs b/rust/src/core.rs index 245cfd900f05..7a037d1ff11f 100644 --- a/rust/src/core.rs +++ b/rust/src/core.rs @@ -146,7 +146,7 @@ pub type SCHTPFileCloseHandleRange = extern "C" fn ( flags: u16, c: *mut HttpRangeContainerBlock, data: *const u8, - data_len: u32); + data_len: u32) -> bool; pub type SCFileOpenFileWithId = extern "C" fn ( file_container: &FileContainer, sbcfg: &StreamingBufferConfig, diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs index bf80379c2f16..0d18a2f455d9 100644 --- a/rust/src/http2/http2.rs +++ b/rust/src/http2/http2.rs @@ -134,7 +134,7 @@ pub struct HTTP2Transaction { decoder: decompression::HTTP2Decoder, pub file_range: *mut HttpRangeContainerBlock, - tx_data: AppLayerTxData, + pub tx_data: AppLayerTxData, pub ft_tc: FileTransferTracker, ft_ts: FileTransferTracker, diff --git a/rust/src/http2/range.rs b/rust/src/http2/range.rs index f86e2188f9b2..34217ed40bd2 100644 --- a/rust/src/http2/range.rs +++ b/rust/src/http2/range.rs @@ -167,20 +167,23 @@ pub fn http2_range_append(fr: *mut HttpRangeContainerBlock, data: &[u8]) { pub fn http2_range_close( tx: &mut HTTP2Transaction, files: &mut FileContainer, flags: u16, data: &[u8], ) { - match unsafe { SC } { - None => panic!("BUG no suricata_config"), - Some(c) => { - (c.HTPFileCloseHandleRange)( + let added = if let Some(c) = unsafe { SC } { + let added = (c.HTPFileCloseHandleRange)( files, flags, tx.file_range, data.as_ptr(), data.len() as u32, - ); - (c.HttpRangeFreeBlock)(tx.file_range); - } - } + ); + (c.HttpRangeFreeBlock)(tx.file_range); + added + } else { + false + }; tx.file_range = std::ptr::null_mut(); + if added { + tx.tx_data.incr_files_opened(); + } } // Defined in app-layer-htp-range.h diff --git a/src/app-layer-htp-file.c b/src/app-layer-htp-file.c index bdbcd2932294..bc49a25ca98d 100644 --- a/src/app-layer-htp-file.c +++ b/src/app-layer-htp-file.c @@ -344,9 +344,14 @@ int HTPFileStoreChunk(HtpState *s, const uint8_t *data, uint32_t data_len, SCReturnInt(retval); } -void HTPFileCloseHandleRange(FileContainer *files, const uint16_t flags, HttpRangeContainerBlock *c, +/** \brief close range, add reassembled file if possible + * \retval true if reassembled file was added + * \retval false if no reassembled file was added + */ +bool HTPFileCloseHandleRange(FileContainer *files, const uint16_t flags, HttpRangeContainerBlock *c, const uint8_t *data, uint32_t data_len) { + bool added = false; if (HttpRangeAppendData(c, data, data_len) < 0) { SCLogDebug("Failed to append data"); } @@ -361,10 +366,12 @@ void HTPFileCloseHandleRange(FileContainer *files, const uint16_t flags, HttpRan if (ranged && files) { /* HtpState owns the constructed file now */ FileContainerAdd(files, ranged); + added = true; } DEBUG_VALIDATE_BUG_ON(ranged && !files); THashDataUnlock(c->container->hdata); } + return added; } /** diff --git a/src/app-layer-htp-file.h b/src/app-layer-htp-file.h index 66522a3f2f08..c105652eeaea 100644 --- a/src/app-layer-htp-file.h +++ b/src/app-layer-htp-file.h @@ -30,7 +30,7 @@ int HTPFileOpen(HtpState *, HtpTxUserData *, const uint8_t *, uint16_t, const ui int HTPParseContentRange(bstr *rawvalue, HTTPContentRange *range); int HTPFileOpenWithRange(HtpState *, HtpTxUserData *, const uint8_t *, uint16_t, const uint8_t *, uint32_t, uint64_t, bstr *rawvalue, HtpTxUserData *htud); -void HTPFileCloseHandleRange( +bool HTPFileCloseHandleRange( FileContainer *, const uint16_t, HttpRangeContainerBlock *, const uint8_t *, uint32_t); int HTPFileStoreChunk(HtpState *, const uint8_t *, uint32_t, uint8_t); int HTPFileClose(HtpState *, const uint8_t *, uint32_t, uint8_t, uint8_t); diff --git a/src/rust-context.h b/src/rust-context.h index 20d8ce7ef3d8..4c43c98d9b47 100644 --- a/src/rust-context.h +++ b/src/rust-context.h @@ -40,7 +40,7 @@ typedef struct SuricataContext_ { void (*AppLayerParserTriggerRawStreamReassembly)(Flow *, int direction); void (*HttpRangeFreeBlock)(HttpRangeContainerBlock *); - void (*HTPFileCloseHandleRange)( + bool (*HTPFileCloseHandleRange)( FileContainer *, const uint16_t, HttpRangeContainerBlock *, const uint8_t *, uint32_t); int (*FileOpenFileWithId)(FileContainer *, const StreamingBufferConfig *, From 9537d119b94ae9e09de1389880806f9935c16639 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 24 Mar 2022 16:44:10 +0100 Subject: [PATCH 13/14] http: fix reassembled range file accounting --- src/app-layer-htp-file.c | 7 +++++-- src/app-layer-htp-file.h | 2 +- src/app-layer-htp.c | 17 ++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/app-layer-htp-file.c b/src/app-layer-htp-file.c index bc49a25ca98d..5d4ef9add899 100644 --- a/src/app-layer-htp-file.c +++ b/src/app-layer-htp-file.c @@ -390,7 +390,7 @@ bool HTPFileCloseHandleRange(FileContainer *files, const uint16_t flags, HttpRan * \retval -1 error * \retval -2 not storing files on this flow/tx */ -int HTPFileClose(HtpState *s, const uint8_t *data, uint32_t data_len, +int HTPFileClose(HtpState *s, HtpTxUserData *htud, const uint8_t *data, uint32_t data_len, uint8_t flags, uint8_t direction) { SCEnter(); @@ -422,7 +422,10 @@ int HTPFileClose(HtpState *s, const uint8_t *data, uint32_t data_len, } if (s->file_range != NULL) { - HTPFileCloseHandleRange(files, flags, s->file_range, data, data_len); + bool added = HTPFileCloseHandleRange(files, flags, s->file_range, data, data_len); + if (added) { + htud->tx_data.files_opened++; + } HttpRangeFreeBlock(s->file_range); s->file_range = NULL; } diff --git a/src/app-layer-htp-file.h b/src/app-layer-htp-file.h index c105652eeaea..aa3f132ba047 100644 --- a/src/app-layer-htp-file.h +++ b/src/app-layer-htp-file.h @@ -33,7 +33,7 @@ int HTPFileOpenWithRange(HtpState *, HtpTxUserData *, const uint8_t *, uint16_t, bool HTPFileCloseHandleRange( FileContainer *, const uint16_t, HttpRangeContainerBlock *, const uint8_t *, uint32_t); int HTPFileStoreChunk(HtpState *, const uint8_t *, uint32_t, uint8_t); -int HTPFileClose(HtpState *, const uint8_t *, uint32_t, uint8_t, uint8_t); +int HTPFileClose(HtpState *, HtpTxUserData *, const uint8_t *, uint32_t, uint8_t, uint8_t); void HTPFileParserRegisterTests(void); diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index 2b7b80ef288f..fcdd7eba1d9c 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -1414,9 +1414,8 @@ static int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud, printf("FILEDATA (final chunk) END: \n"); #endif if (!(htud->tsflags & HTP_DONTSTORE)) { - if (HTPFileClose(hstate, filedata, filedata_len, flags, - STREAM_TOSERVER) == -1) - { + if (HTPFileClose(hstate, htud, filedata, filedata_len, flags, STREAM_TOSERVER) == + -1) { goto end; } } @@ -1538,7 +1537,7 @@ static int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud, } else if (result == -2) { htud->tsflags |= HTP_DONTSTORE; } else { - if (HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER) == -1) { + if (HTPFileClose(hstate, htud, NULL, 0, 0, STREAM_TOSERVER) == -1) { goto end; } } @@ -1605,7 +1604,7 @@ static int HtpRequestBodyHandleMultipart(HtpState *hstate, HtpTxUserData *htud, } else if (result == -2) { htud->tsflags |= HTP_DONTSTORE; } else { - if (HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER) == -1) { + if (HTPFileClose(hstate, htud, NULL, 0, 0, STREAM_TOSERVER) == -1) { goto end; } } @@ -1891,7 +1890,7 @@ static int HTPCallbackRequestBodyData(htp_tx_data_t *d) } else { if (tx_ud->tsflags & HTP_FILENAME_SET) { SCLogDebug("closing file that was being stored"); - (void)HTPFileClose(hstate, NULL, 0, FILE_TRUNCATED, STREAM_TOSERVER); + (void)HTPFileClose(hstate, tx_ud, NULL, 0, FILE_TRUNCATED, STREAM_TOSERVER); tx_ud->tsflags &= ~HTP_FILENAME_SET; } } @@ -1981,7 +1980,7 @@ static int HTPCallbackResponseBodyData(htp_tx_data_t *d) } else { if (tx_ud->tcflags & HTP_FILENAME_SET) { SCLogDebug("closing file that was being stored"); - (void)HTPFileClose(hstate, NULL, 0, FILE_TRUNCATED, STREAM_TOCLIENT); + (void)HTPFileClose(hstate, tx_ud, NULL, 0, FILE_TRUNCATED, STREAM_TOCLIENT); tx_ud->tcflags &= ~HTP_FILENAME_SET; } } @@ -2202,7 +2201,7 @@ static int HTPCallbackRequestComplete(htp_tx_t *tx) if (htud != NULL) { if (htud->tsflags & HTP_FILENAME_SET) { SCLogDebug("closing file that was being stored"); - (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOSERVER); + (void)HTPFileClose(hstate, htud, NULL, 0, 0, STREAM_TOSERVER); htud->tsflags &= ~HTP_FILENAME_SET; if (abs_right_edge < (uint64_t)UINT32_MAX) { StreamTcpReassemblySetMinInspectDepth( @@ -2257,7 +2256,7 @@ static int HTPCallbackResponseComplete(htp_tx_t *tx) if (htud != NULL) { if (htud->tcflags & HTP_FILENAME_SET) { SCLogDebug("closing file that was being stored"); - (void)HTPFileClose(hstate, NULL, 0, 0, STREAM_TOCLIENT); + (void)HTPFileClose(hstate, htud, NULL, 0, 0, STREAM_TOCLIENT); htud->tcflags &= ~HTP_FILENAME_SET; } } From 8ef066318d7c4dd9b6686dbebd621c790828c384 Mon Sep 17 00:00:00 2001 From: Arne Welzel Date: Sat, 12 Feb 2022 17:49:07 +0100 Subject: [PATCH 14/14] flow-manager: fix off-by-one in flow_hash row allocation The current code doesn't cover all rows when more than one flow manager is used. It leaves a single row between ftd->max and ftd->min of the next manager orphaned. As an example: hash_size=1000 flowmgr_number=3 range=333 instance ftd->min ftd->max 0 0 333 1 334 666 2 667 1000 Rows not covered: 333, 666 --- src/flow-manager.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/flow-manager.c b/src/flow-manager.c index 9f7f04fcecb9..1139e4326faa 100644 --- a/src/flow-manager.c +++ b/src/flow-manager.c @@ -667,14 +667,13 @@ static TmEcode FlowManagerThreadInit(ThreadVars *t, const void *initdata, void * /* set the min and max value used for hash row walking * each thread has it's own section of the flow hash */ uint32_t range = flow_config.hash_size / flowmgr_number; - if (ftd->instance == 0) - ftd->max = range; - else if ((ftd->instance + 1) == flowmgr_number) { - ftd->min = (range * ftd->instance) + 1; + + ftd->min = ftd->instance * range; + ftd->max = (ftd->instance + 1) * range; + + /* last flow-manager takes on hash_size % flowmgr_number extra rows */ + if ((ftd->instance + 1) == flowmgr_number) { ftd->max = flow_config.hash_size; - } else { - ftd->min = (range * ftd->instance) + 1; - ftd->max = (range * (ftd->instance + 1)); } BUG_ON(ftd->min > flow_config.hash_size || ftd->max > flow_config.hash_size);