--- /dev/null
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: evilsocket
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
--- /dev/null
+---
+name: 🐞 Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+Please, check the FAQ and Known Problems pages before creating the bug report:
+https://github.com/evilsocket/opensnitch/wiki/FAQs
+https://github.com/evilsocket/opensnitch/wiki/Known-problems
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+Include the following information:
+ - OpenSnitch version.
+ - OS: [e.g. Debian GNU/Linux, ArchLinux, Slackware, ...]
+ - Version [e.g. Buster, 10.3, 20.04]
+ - Window Manager: [e.g. GNOME Shell, KDE, enlightenment, i3wm, ...]
+ - Kernel version: echo $(uname -a)
+
+**To Reproduce**
+Describe in detail as much as you can what happened.
+
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Post error logs:**
+If it's a crash of the GUI:
+ - Launch it from a terminal and reproduce the issue.
+ - Post the errors logged to the terminal.
+
+If the daemon doesn't start:
+ - Post last 15 lines of the log file `/var/log/opensnitchd.log`
+ - Or launch it from a terminal as root (`# /usr/bin/opensnitchd -rules-path /etc/opensnitchd/rules`) and post the errors logged to the terminal.
+
+If the deb or rpm packages fail to install:
+ - Install them from a terminal (`$ sudo dpkg -i opensnitch*` / `$ sudo yum install opensnitch*`), and post the errors logged to stdout.
+
+**Expected behavior (optional)**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem. It may help to understand the issue much better.
+
+**Additional context**
+Add any other context about the problem here.
--- /dev/null
+contact_links:
+ - name: 🙋 Question
+ url: https://github.com/evilsocket/opensnitch/discussions/new
+ about: Ask your question here
--- /dev/null
+---
+name: 💡 Feature request
+about: Suggest an idea
+title: '[Feature Request] <title>'
+labels: feature
+assignees: ''
+
+---
+
+<!--
+Note: Please, use the search box to see if this feature has already been requested.
+-->
+
+### Summary:
+<!-- A concise description of the new feature. -->
--- /dev/null
+name: Build status
+on: [push, pull_request]
+jobs:
+
+ Builddeb:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ image: ["debian:bookworm", "debian:sid"]
+ container:
+ image: ${{ matrix.image }}
+ options: --cpus=2
+ steps:
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJson(github) }}
+ run: echo "$GITHUB_CONTEXT"
+
+ - name: Check out git code
+ uses: actions/checkout@v2
+
+ - name: Install pre-dependencies
+ env:
+ DEBIAN_FRONTEND: noninteractive
+ run: |
+ set -e
+ set -x
+ apt --quiet update
+ # Install stuff needed to check out the linuxcnc repo and turn it into a debian source package.
+ apt --yes --quiet install --no-install-suggests eatmydata
+ eatmydata apt --yes --quiet install --no-install-suggests git devscripts
+
+ - name: Install build dependencies
+ env:
+ DEBIAN_FRONTEND: noninteractive
+ run: |
+ set -e
+ set -x
+ eatmydata apt --yes --quiet build-dep --indep-only .
+
+ - name: Build source client
+ env:
+ DEBIAN_FRONTEND: noninteractive
+ run: |
+ set -e
+ set -x
+ # Workaround for missing source tarball
+ echo 1.0 > debian/source/format
+ yes y | eatmydata debuild -us -uc
+
+ - name: Test install debian packages
+ env:
+ DEBIAN_FRONTEND: noninteractive
+ run: |
+ set -e
+ set -x
+ eatmydata apt --yes --quiet install ../*.deb
--- /dev/null
+name: Build eBPF
+on:
+
+ # Trigger this workflow only when ebpf modules changes.
+ push:
+ paths:
+ - 'ebpf_prog/*'
+ - '.github/workflows/ebpf.yml'
+ pull_request:
+ paths:
+ - 'ebpf_prog/*'
+ - '.github/workflows/ebpf.yml'
+
+ # Allow to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+
+ build:
+ name: Build eBPF object
+ runs-on: ubuntu-latest
+ steps:
+
+ - name: Check out git code
+ uses: actions/checkout@v2
+
+ - name: Get and prepare dependencies
+ run: |
+ set -e
+ set -x
+ sudo apt install eatmydata
+ sudo eatmydata apt install wget tar patch clang llvm libelf-dev libzip-dev flex bison libssl-dev bc rsync python3 binutils
+ eatmydata wget --no-verbose https://github.com/torvalds/linux/archive/v5.8.tar.gz
+ eatmydata tar -xf v5.8.tar.gz
+
+ - name: Build eBPF module
+ run: |
+ set -e
+ set -x
+ eatmydata patch linux-5.8/tools/lib/bpf/bpf_helpers.h < ebpf_prog/file.patch
+ eatmydata cp ebpf_prog/opensnitch.c ebpf_prog/Makefile linux-5.8/samples/bpf
+ cd linux-5.8 && yes "" | eatmydata make oldconfig
+ eatmydata make prepare
+ eatmydata make headers_install
+ cd samples/bpf
+ eatmydata make
+ eatmydata objdump -h opensnitch.o
+ eatmydata llvm-strip -g opensnitch.o
--- /dev/null
+name: Build status
+on:
+ # Trigger this workflow only when daemon code changes.
+ push:
+ paths:
+ - 'daemon/*'
+ - '.github/workflows/go.yml'
+ pull_request:
+ paths:
+ - 'daemon/*'
+ - '.github/workflows/go.yml'
+
+ # Allow to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+
+ build:
+ name: Build Go code
+ runs-on: ubuntu-latest
+ steps:
+
+ - name: Set up Go 1.15.15
+ uses: actions/setup-go@v1
+ with:
+ go-version: 1.15.15
+ id: go
+
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v2
+
+ - name: Get dependencies
+ run: |
+ sudo apt --yes --quiet install --no-install-suggests eatmydata
+ sudo eatmydata apt install git libnetfilter-queue-dev libmnl-dev libpcap-dev protobuf-compiler
+ export GOPATH=~/go
+ export PATH=$PATH:$GOPATH/bin
+ eatmydata go get github.com/golang/protobuf/protoc-gen-go
+ eatmydata go install google.golang.org/protobuf/cmd/protoc-gen-go
+ eatmydata go get google.golang.org/grpc/cmd/protoc-gen-go-grpc
+ cd proto
+ eatmydata make ../daemon/ui/protocol/ui.pb.go
+
+ - name: Build
+ run: |
+ cd daemon
+ eatmydata go mod tidy
+ eatmydata go mod vendor
+ eatmydata go build -v .
--- /dev/null
+*.sock
+*.pyc
+*.profile
+rules
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
--- /dev/null
+all: protocol opensnitch_daemon gui
+
+install:
+ @$(MAKE) -C daemon install
+ @$(MAKE) -C ui install
+
+protocol:
+ @$(MAKE) -C proto
+
+opensnitch_daemon:
+ @$(MAKE) -C daemon
+
+gui:
+ @$(MAKE) -C ui
+
+clean:
+ @$(MAKE) -C daemon clean
+ @$(MAKE) -C proto clean
+ @$(MAKE) -C ui clean
+
+run:
+ cd ui && pip3 install --upgrade . && cd ..
+ opensnitch-ui --socket unix:///tmp/osui.sock &
+ ./daemon/opensnitchd -rules-path /etc/opensnitchd/rules -ui-socket unix:///tmp/osui.sock -cpu-profile cpu.profile -mem-profile mem.profile
+
+test:
+ clear
+ $(MAKE) clean
+ clear
+ mkdir -p rules
+ $(MAKE)
+ clear
+ $(MAKE) run
+
+adblocker:
+ clear
+ $(MAKE) clean
+ clear
+ $(MAKE)
+ clear
+ python make_ads_rules.py
+ clear
+ cd ui && pip3 install --upgrade . && cd ..
+ opensnitch-ui --socket unix:///tmp/osui.sock &
+ ./daemon/opensnitchd -rules-path /etc/opensnitchd/rules -ui-socket unix:///tmp/osui.sock
+
+
--- /dev/null
+<p align="center">
+ <img alt="opensnitch" src="https://raw.githubusercontent.com/evilsocket/opensnitch/master/ui/opensnitch/res/icon.png" height="160" />
+ <p align="center">
+ <img src="https://github.com/evilsocket/opensnitch/workflows/Build%20status/badge.svg" />
+ <a href="https://github.com/evilsocket/opensnitch/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/evilsocket/opensnitch.svg?style=flat-square"></a>
+ <a href="https://github.com/evilsocket/opensnitch/blob/master/LICENSE.md"><img alt="Software License" src="https://img.shields.io/badge/license-GPL3-brightgreen.svg?style=flat-square"></a>
+ <a href="https://goreportcard.com/report/github.com/evilsocket/opensnitch/daemon"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/evilsocket/opensnitch/daemon?style=flat-square"></a>
+ <a href="https://repology.org/project/opensnitch/versions"><img src="https://repology.org/badge/tiny-repos/opensnitch.svg" alt="Packaging status"></a>
+ </p>
+</p>
+
+**OpenSnitch** is a GNU/Linux application firewall.
+
+<p align="center">
+ <img src="https://user-images.githubusercontent.com/2742953/85205382-6ba9cb00-b31b-11ea-8e9a-bd4b8b05a236.png" alt="OpenSnitch"/>
+</p>
+
+### Installation and configuration
+
+Please, refer to [the documentation](https://github.com/evilsocket/opensnitch/wiki) for detailed information.
+
+### Contributors
+
+[See the list](https://github.com/evilsocket/opensnitch/graphs/contributors)
--- /dev/null
+opensnitchd
+vendor
--- /dev/null
+[[constraint]]
+ name = "github.com/fsnotify/fsnotify"
+ version = "1.4.7"
+
+[[constraint]]
+ name = "github.com/google/gopacket"
+ version = "~1.1.14"
+
+[[constraint]]
+ name = "google.golang.org/grpc"
+ version = "~1.11.2"
+
+[[constraint]]
+ name = "github.com/evilsocket/ftrace"
+ version = "~1.2.0"
+
+[prune]
+ go-tests = true
+ unused-packages = true
--- /dev/null
+#SRC contains all *.go *.c *.h files in daemon/ and its subfolders
+SRC := $(shell find . -type f -name '*.go' -o -name '*.h' -o -name '*.c')
+
+all: opensnitchd
+
+install:
+ @mkdir -p /etc/opensnitchd/rules
+ @cp opensnitchd /usr/local/bin/
+ @cp opensnitchd.service /etc/systemd/system/
+ @cp default-config.json /etc/opensnitchd/
+ @cp system-fw.json /etc/opensnitchd/
+ @systemctl daemon-reload
+
+opensnitchd: $(SRC)
+ @go get
+ @go build -o opensnitchd .
+
+clean:
+ @rm -rf opensnitchd
+
+
--- /dev/null
+package conman
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "os"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/dns"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/evilsocket/opensnitch/daemon/netfilter"
+ "github.com/evilsocket/opensnitch/daemon/netlink"
+ "github.com/evilsocket/opensnitch/daemon/netstat"
+ "github.com/evilsocket/opensnitch/daemon/procmon"
+ "github.com/evilsocket/opensnitch/daemon/procmon/ebpf"
+ "github.com/evilsocket/opensnitch/daemon/ui/protocol"
+
+ "github.com/google/gopacket/layers"
+)
+
+// Connection represents an outgoing connection.
+type Connection struct {
+ Protocol string
+ SrcIP net.IP
+ SrcPort uint
+ DstIP net.IP
+ DstPort uint
+ DstHost string
+ Entry *netstat.Entry
+ Process *procmon.Process
+
+ pkt *netfilter.Packet
+}
+
+var showUnknownCons = false
+
+// Parse extracts the IP layers from a network packet to determine what
+// process generated a connection.
+func Parse(nfp netfilter.Packet, interceptUnknown bool) *Connection {
+ showUnknownCons = interceptUnknown
+
+ if nfp.IsIPv4() {
+ con, err := NewConnection(&nfp)
+ if err != nil {
+ log.Debug("%s", err)
+ return nil
+ } else if con == nil {
+ return nil
+ }
+ return con
+ }
+
+ if core.IPv6Enabled == false {
+ return nil
+ }
+ con, err := NewConnection6(&nfp)
+ if err != nil {
+ log.Debug("%s", err)
+ return nil
+ } else if con == nil {
+ return nil
+ }
+ return con
+
+}
+
+func newConnectionImpl(nfp *netfilter.Packet, c *Connection, protoType string) (cr *Connection, err error) {
+ // no errors but not enough info neither
+ if c.parseDirection(protoType) == false {
+ return nil, nil
+ }
+ log.Debug("new connection %s => %d:%v -> %v:%d uid: %d", c.Protocol, c.SrcPort, c.SrcIP, c.DstIP, c.DstPort, nfp.UID)
+
+ c.Entry = &netstat.Entry{
+ Proto: c.Protocol,
+ SrcIP: c.SrcIP,
+ SrcPort: c.SrcPort,
+ DstIP: c.DstIP,
+ DstPort: c.DstPort,
+ UserId: -1,
+ INode: -1,
+ }
+
+ pid := -1
+ uid := -1
+ if procmon.MethodIsEbpf() {
+ pid, uid, err = ebpf.GetPid(c.Protocol, c.SrcPort, c.SrcIP, c.DstIP, c.DstPort)
+ if err != nil {
+ log.Warning("ebpf warning: %v", err)
+ return nil, nil
+ }
+ }
+ // sometimes when using eBPF the connection is not found, but falling back to legacy
+ // methods helps to find it and avoid "unknown/kernel pop-ups". TODO: investigate
+ if pid < 0 {
+ // 0. lookup uid and inode via netlink. Can return several inodes.
+ // 1. lookup uid and inode using /proc/net/(udp|tcp|udplite)
+ // 2. lookup pid by inode
+ // 3. if this is coming from us, just accept
+ // 4. lookup process info by pid
+ var inodeList []int
+ uid, inodeList = netlink.GetSocketInfo(c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort)
+ if len(inodeList) == 0 {
+ if c.Entry = netstat.FindEntry(c.Protocol, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort); c.Entry == nil {
+ return nil, fmt.Errorf("Could not find netstat entry for: %s", c)
+ }
+ if c.Entry.INode > 0 {
+ log.Debug("connection found in netstat: %v", c.Entry)
+ inodeList = append([]int{c.Entry.INode}, inodeList...)
+ }
+ }
+ if len(inodeList) == 0 {
+ log.Debug("<== no inodes found, applying default action.")
+ }
+
+ for n, inode := range inodeList {
+ pid = procmon.GetPIDFromINode(inode, fmt.Sprint(inode, c.SrcIP, c.SrcPort, c.DstIP, c.DstPort))
+ if pid != -1 {
+ log.Debug("[%d] PID found %d [%d]", n, pid, inode)
+ c.Entry.INode = inode
+ break
+ }
+ }
+ }
+
+ if nfp.UID != 0xffffffff {
+ c.Entry.UserId = int(nfp.UID)
+ } else {
+ c.Entry.UserId = uid
+ }
+
+ if pid == os.Getpid() {
+ // return a Process object with our PID, to be able to exclude our own connections
+ // (to the UI on a local socket for example)
+ c.Process = procmon.NewProcess(pid, "")
+ return c, nil
+ }
+
+ if c.Process = procmon.FindProcess(pid, showUnknownCons); c.Process == nil {
+ return nil, fmt.Errorf("Could not find process by its pid %d for: %s", pid, c)
+ }
+
+ return c, nil
+}
+
+// NewConnection creates a new Connection object, and returns the details of it.
+func NewConnection(nfp *netfilter.Packet) (c *Connection, err error) {
+ ipv4 := nfp.Packet.Layer(layers.LayerTypeIPv4)
+ if ipv4 == nil {
+ return nil, errors.New("Error getting IPv4 layer")
+ }
+ ip, ok := ipv4.(*layers.IPv4)
+ if !ok {
+ return nil, errors.New("Error getting IPv4 layer data")
+ }
+ c = &Connection{
+ SrcIP: ip.SrcIP,
+ DstIP: ip.DstIP,
+ DstHost: dns.HostOr(ip.DstIP, ""),
+ pkt: nfp,
+ }
+ return newConnectionImpl(nfp, c, "")
+}
+
+// NewConnection6 creates a IPv6 new Connection object, and returns the details of it.
+func NewConnection6(nfp *netfilter.Packet) (c *Connection, err error) {
+ ipv6 := nfp.Packet.Layer(layers.LayerTypeIPv6)
+ if ipv6 == nil {
+ return nil, errors.New("Error getting IPv6 layer")
+ }
+ ip, ok := ipv6.(*layers.IPv6)
+ if !ok {
+ return nil, errors.New("Error getting IPv6 layer data")
+ }
+ c = &Connection{
+ SrcIP: ip.SrcIP,
+ DstIP: ip.DstIP,
+ DstHost: dns.HostOr(ip.DstIP, ""),
+ pkt: nfp,
+ }
+ return newConnectionImpl(nfp, c, "6")
+}
+
+func (c *Connection) parseDirection(protoType string) bool {
+ ret := false
+ if tcpLayer := c.pkt.Packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
+ if tcp, ok := tcpLayer.(*layers.TCP); ok == true && tcp != nil {
+ c.Protocol = "tcp" + protoType
+ c.DstPort = uint(tcp.DstPort)
+ c.SrcPort = uint(tcp.SrcPort)
+ ret = true
+
+ if tcp.DstPort == 53 {
+ c.getDomains(c.pkt, c)
+ }
+ }
+ } else if udpLayer := c.pkt.Packet.Layer(layers.LayerTypeUDP); udpLayer != nil {
+ if udp, ok := udpLayer.(*layers.UDP); ok == true && udp != nil {
+ c.Protocol = "udp" + protoType
+ c.DstPort = uint(udp.DstPort)
+ c.SrcPort = uint(udp.SrcPort)
+ ret = true
+
+ if udp.DstPort == 53 {
+ c.getDomains(c.pkt, c)
+ }
+ }
+ } else if udpliteLayer := c.pkt.Packet.Layer(layers.LayerTypeUDPLite); udpliteLayer != nil {
+ if udplite, ok := udpliteLayer.(*layers.UDPLite); ok == true && udplite != nil {
+ c.Protocol = "udplite" + protoType
+ c.DstPort = uint(udplite.DstPort)
+ c.SrcPort = uint(udplite.SrcPort)
+ ret = true
+ }
+ }
+
+ return ret
+}
+
+func (c *Connection) getDomains(nfp *netfilter.Packet, con *Connection) {
+ domains := dns.GetQuestions(nfp)
+ if len(domains) > 0 {
+ for _, dns := range domains {
+ con.DstHost = dns
+ }
+ }
+}
+
+// To returns the destination host of a connection.
+func (c *Connection) To() string {
+ if c.DstHost == "" {
+ return c.DstIP.String()
+ }
+ return c.DstHost
+}
+
+func (c *Connection) String() string {
+ if c.Entry == nil {
+ return fmt.Sprintf("%s ->(%s)-> %s:%d", c.SrcIP, c.Protocol, c.To(), c.DstPort)
+ }
+
+ if c.Process == nil {
+ return fmt.Sprintf("%s (uid:%d) ->(%s)-> %s:%d", c.SrcIP, c.Entry.UserId, c.Protocol, c.To(), c.DstPort)
+ }
+
+ return fmt.Sprintf("%s (%d) -> %s:%d (proto:%s uid:%d)", c.Process.Path, c.Process.ID, c.To(), c.DstPort, c.Protocol, c.Entry.UserId)
+}
+
+// Serialize returns a connection serialized.
+func (c *Connection) Serialize() *protocol.Connection {
+ return &protocol.Connection{
+ Protocol: c.Protocol,
+ SrcIp: c.SrcIP.String(),
+ SrcPort: uint32(c.SrcPort),
+ DstIp: c.DstIP.String(),
+ DstHost: c.DstHost,
+ DstPort: uint32(c.DstPort),
+ UserId: uint32(c.Entry.UserId),
+ ProcessId: uint32(c.Process.ID),
+ ProcessPath: c.Process.Path,
+ ProcessArgs: c.Process.Args,
+ ProcessEnv: c.Process.Env,
+ ProcessCwd: c.Process.CWD,
+ }
+}
--- /dev/null
+package conman
+
+import (
+ "fmt"
+ "net"
+ "testing"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+
+ "github.com/evilsocket/opensnitch/daemon/netfilter"
+)
+
+// Adding new packets:
+// wireshark -> right click -> Copy as HexDump -> create []byte{}
+
+func NewTCPPacket() gopacket.Packet {
+ // 47676:192.168.1.100 -> 1.1.1.1:23
+ testTCPPacket := []byte{0x4c, 0x6e, 0x6e, 0xd5, 0x79, 0xbf, 0x00, 0x28, 0x9d, 0x43, 0x7f, 0xd7, 0x08, 0x00, 0x45, 0x10,
+ 0x00, 0x3c, 0x1d, 0x07, 0x40, 0x00, 0x40, 0x06, 0x59, 0x8e, 0xc0, 0xa8, 0x01, 0x6d, 0x01, 0x01,
+ 0x01, 0x01, 0xba, 0x3c, 0x00, 0x17, 0x47, 0x7e, 0xf3, 0x0b, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02,
+ 0xfa, 0xf0, 0x4c, 0x27, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0x91, 0xfb,
+ 0xb5, 0xf4, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x0a}
+ return gopacket.NewPacket(testTCPPacket, layers.LinkTypeEthernet, gopacket.Default)
+}
+
+func NewUDPPacket() gopacket.Packet {
+ // 29517:192.168.1.109 -> 1.0.0.1:53
+ testUDPPacketDNS := []byte{
+ 0x4c, 0x6e, 0x6e, 0xd5, 0x79, 0xbf, 0x00, 0x28, 0x9d, 0x43, 0x7f, 0xd7, 0x08, 0x00, 0x45, 0x00,
+ 0x00, 0x40, 0x54, 0x1a, 0x40, 0x00, 0x3f, 0x11, 0x24, 0x7d, 0xc0, 0xa8, 0x01, 0x6d, 0x01, 0x00,
+ 0x00, 0x01, 0x73, 0x4d, 0x00, 0x35, 0x00, 0x2c, 0xf1, 0x17, 0x05, 0x51, 0x00, 0x20, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x70, 0x69, 0x04, 0x68, 0x6f, 0x6c, 0x65, 0x00, 0x00,
+ 0x01, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ }
+
+ return gopacket.NewPacket(testUDPPacketDNS, layers.LinkTypeEthernet, gopacket.Default)
+}
+
+func EstablishConnection(proto, dst string) (net.Conn, error) {
+ c, err := net.Dial(proto, dst)
+ if err != nil {
+ fmt.Println(err)
+ return nil, err
+ }
+ return c, nil
+}
+
+func ListenOnPort(proto, port string) (net.Listener, error) {
+ l, err := net.Listen(proto, port)
+ if err != nil {
+ fmt.Println(err)
+ return nil, err
+ }
+ return l, nil
+}
+
+func NewPacket(pkt gopacket.Packet) *netfilter.Packet {
+ return &netfilter.Packet{
+ Packet: pkt,
+ UID: 666,
+ NetworkProtocol: netfilter.IPv4,
+ }
+}
+
+func NewDummyConnection(src, dst net.IP) *Connection {
+ return &Connection{
+ SrcIP: src,
+ DstIP: dst,
+ }
+}
+
+// Test TCP parseDirection()
+func TestParseTCPDirection(t *testing.T) {
+ srcIP := net.IP{192, 168, 1, 100}
+ dstIP := net.IP{1, 1, 1, 1}
+ c := NewDummyConnection(srcIP, dstIP)
+ // 47676:192.168.1.100 -> 1.1.1.1:23
+ pkt := NewPacket(NewTCPPacket())
+ c.pkt = pkt
+
+ // parseDirection extracts the src and dst port from a network packet.
+ if c.parseDirection("") == false {
+ t.Error("parseDirection() should not be false")
+ t.Fail()
+ }
+ if c.SrcPort != 47676 {
+ t.Error("parseDirection() SrcPort mismatch:", c)
+ t.Fail()
+ }
+ if c.DstPort != 23 {
+ t.Error("parseDirection() DstPort mismatch:", c)
+ t.Fail()
+ }
+ if c.Protocol != "tcp" {
+ t.Error("parseDirection() Protocol mismatch:", c)
+ t.Fail()
+ }
+}
+
+// Test UDP parseDirection()
+func TestParseUDPDirection(t *testing.T) {
+ srcIP := net.IP{192, 168, 1, 100}
+ dstIP := net.IP{1, 0, 0, 1}
+ c := NewDummyConnection(srcIP, dstIP)
+ // 29517:192.168.1.109 -> 1.0.0.1:53
+ pkt := NewPacket(NewUDPPacket())
+ c.pkt = pkt
+
+ // parseDirection extracts the src and dst port from a network packet.
+ if c.parseDirection("") == false {
+ t.Error("parseDirection() should not be false")
+ t.Fail()
+ }
+ if c.SrcPort != 29517 {
+ t.Error("parseDirection() SrcPort mismatch:", c)
+ t.Fail()
+ }
+ if c.DstPort != 53 {
+ t.Error("parseDirection() DstPort mismatch:", c)
+ t.Fail()
+ }
+ if c.Protocol != "udp" {
+ t.Error("parseDirection() Protocol mismatch:", c)
+ t.Fail()
+ }
+}
--- /dev/null
+package core
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "os/user"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+const (
+ defaultTrimSet = "\r\n\t "
+)
+
+// Trim remove trailing spaces from a string.
+func Trim(s string) string {
+ return strings.Trim(s, defaultTrimSet)
+}
+
+// Exec spawns a new process and reurns the output.
+func Exec(executable string, args []string) (string, error) {
+ path, err := exec.LookPath(executable)
+ if err != nil {
+ return "", err
+ }
+
+ raw, err := exec.Command(path, args...).CombinedOutput()
+ if err != nil {
+ return "", err
+ }
+ return Trim(string(raw)), nil
+}
+
+// Exists checks if a path exists.
+func Exists(path string) bool {
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ return false
+ }
+ return true
+}
+
+// ExpandPath replaces '~' shorthand with the user's home directory.
+func ExpandPath(path string) (string, error) {
+ // Check if path is empty
+ if path != "" {
+ if strings.HasPrefix(path, "~") {
+ usr, err := user.Current()
+ if err != nil {
+ return "", err
+ }
+ // Replace only the first occurrence of ~
+ path = strings.Replace(path, "~", usr.HomeDir, 1)
+ }
+ return filepath.Abs(path)
+ }
+ return "", nil
+}
+
+// GetFileModTime checks if a file has been modified.
+func GetFileModTime(filepath string) (time.Time, error) {
+ fi, err := os.Stat(filepath)
+ if err != nil || fi.IsDir() {
+ return time.Now(), fmt.Errorf("GetFileModTime() Invalid file")
+ }
+ return fi.ModTime(), nil
+}
--- /dev/null
+package core
+
+import (
+ "io/ioutil"
+ "strings"
+)
+
+var (
+ // IPv6Enabled indicates if IPv6 protocol is enabled in the system
+ IPv6Enabled = Exists("/proc/sys/net/ipv6")
+)
+
+// GetHostname returns the name of the host where the daemon is running.
+func GetHostname() string {
+ hostname, _ := ioutil.ReadFile("/proc/sys/kernel/hostname")
+ return strings.Replace(string(hostname), "\n", "", -1)
+}
+
+// GetKernelVersion returns the name of the host where the daemon is running.
+func GetKernelVersion() string {
+ version, _ := ioutil.ReadFile("/proc/sys/kernel/version")
+ return strings.Replace(string(version), "\n", "", -1)
+}
--- /dev/null
+package core
+
+// version related consts
+const (
+ Name = "opensnitch-daemon"
+ Version = "1.5.8"
+ Author = "Simone 'evilsocket' Margaritelli"
+ Website = "https://github.com/evilsocket/opensnitch"
+)
--- /dev/null
+{
+ "Server":
+ {
+ "Address":"unix:///tmp/osui.sock",
+ "LogFile":"/var/log/opensnitchd.log"
+ },
+ "DefaultAction": "allow",
+ "DefaultDuration": "once",
+ "InterceptUnknown": false,
+ "ProcMonitorMethod": "ebpf",
+ "LogLevel": 2,
+ "Firewall": "iptables",
+ "Stats": {
+ "MaxEvents": 150,
+ "MaxStats": 25
+ }
+}
--- /dev/null
+package dns
+
+import (
+ "github.com/evilsocket/opensnitch/daemon/netfilter"
+ "github.com/google/gopacket/layers"
+)
+
+// GetQuestions retrieves the domain names a process is trying to resolve.
+func GetQuestions(nfp *netfilter.Packet) (questions []string) {
+ dnsLayer := nfp.Packet.Layer(layers.LayerTypeDNS)
+ if dnsLayer == nil {
+ return questions
+ }
+
+ dns, _ := dnsLayer.(*layers.DNS)
+ for _, dnsQuestion := range dns.Questions {
+ questions = append(questions, string(dnsQuestion.Name))
+ }
+
+ return questions
+}
--- /dev/null
+package dns
+
+import (
+ "net"
+ "sync"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+var (
+ responses = make(map[string]string, 0)
+ lock = sync.RWMutex{}
+)
+
+// TrackAnswers obtains the resolved domains of a DNS query.
+// If the packet is UDP DNS, the domain names are added to the list of resolved domains.
+func TrackAnswers(packet gopacket.Packet) bool {
+ udpLayer := packet.Layer(layers.LayerTypeUDP)
+ if udpLayer == nil {
+ return false
+ }
+
+ udp, ok := udpLayer.(*layers.UDP)
+ if ok == false || udp == nil {
+ return false
+ }
+ if udp.SrcPort != 53 {
+ return false
+ }
+
+ dnsLayer := packet.Layer(layers.LayerTypeDNS)
+ if dnsLayer == nil {
+ return false
+ }
+
+ dnsAns, ok := dnsLayer.(*layers.DNS)
+ if ok == false || dnsAns == nil {
+ return false
+ }
+
+ for _, ans := range dnsAns.Answers {
+ if ans.Name != nil {
+ if ans.IP != nil {
+ Track(ans.IP.String(), string(ans.Name))
+ } else if ans.CNAME != nil {
+ Track(string(ans.CNAME), string(ans.Name))
+ }
+ }
+ }
+
+ return true
+}
+
+// Track adds a resolved domain to the list.
+func Track(resolved string, hostname string) {
+ lock.Lock()
+ defer lock.Unlock()
+
+ if resolved == "127.0.0.1" || resolved == "::1" {
+ return
+ }
+ responses[resolved] = hostname
+
+ log.Debug("New DNS record: %s -> %s", resolved, hostname)
+}
+
+// Host returns if a resolved domain is in the list.
+func Host(resolved string) (host string, found bool) {
+ lock.RLock()
+ defer lock.RUnlock()
+
+ host, found = responses[resolved]
+ return
+}
+
+// HostOr checks if an IP has a domain name already resolved.
+// If the domain is in the list it's returned, otherwise the IP will be returned.
+func HostOr(ip net.IP, or string) string {
+ if host, found := Host(ip.String()); found == true {
+ // host might have been CNAME; go back until we reach the "root"
+ seen := make(map[string]bool) // prevent possibility of loops
+ for {
+ orig, had := Host(host)
+ if seen[orig] {
+ break
+ }
+ if !had {
+ break
+ }
+ seen[orig] = true
+ host = orig
+ }
+ return host
+ }
+ return or
+}
--- /dev/null
+package common
+
+import (
+ "sync"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+type (
+ callback func()
+ callbackBool func() bool
+
+ stopChecker struct {
+ sync.RWMutex
+ ch chan bool
+ }
+
+ // Common holds common fields and functionality of both firewalls,
+ // iptables and nftables.
+ Common struct {
+ sync.RWMutex
+ QueueNum uint16
+ Running bool
+ RulesChecker *time.Ticker
+ stopCheckerChan *stopChecker
+ }
+)
+
+func (s *stopChecker) exit() chan bool {
+ s.RLock()
+ defer s.RUnlock()
+ return s.ch
+}
+
+func (s *stopChecker) stop() {
+ s.Lock()
+ defer s.Unlock()
+
+ if s.ch != nil {
+ s.ch <- true
+ close(s.ch)
+ s.ch = nil
+ }
+}
+
+// SetQueueNum sets the queue number used by the firewall.
+// It's the queue where all intercepted connections will be sent.
+func (c *Common) SetQueueNum(qNum *int) {
+ c.Lock()
+ defer c.Unlock()
+
+ if qNum != nil {
+ c.QueueNum = uint16(*qNum)
+ }
+
+}
+
+// IsRunning returns if the firewall is running or not.
+func (c *Common) IsRunning() bool {
+ c.RLock()
+ defer c.RUnlock()
+
+ return c != nil && c.Running
+}
+
+// NewRulesChecker starts monitoring firewall for configuration or rules changes.
+func (c *Common) NewRulesChecker(areRulesLoaded callbackBool, reloadRules callback) {
+ c.Lock()
+ defer c.Unlock()
+
+ c.stopCheckerChan = &stopChecker{ch: make(chan bool, 1)}
+ c.RulesChecker = time.NewTicker(time.Second * 30)
+
+ go c.startCheckingRules(areRulesLoaded, reloadRules)
+}
+
+// StartCheckingRules monitors if our rules are loaded.
+// If the rules to intercept traffic are not loaded, we'll try to insert them again.
+func (c *Common) startCheckingRules(areRulesLoaded callbackBool, reloadRules callback) {
+ for {
+ select {
+ case <-c.stopCheckerChan.exit():
+ goto Exit
+ case <-c.RulesChecker.C:
+ if areRulesLoaded() == false {
+ reloadRules()
+ }
+ }
+ }
+
+Exit:
+ log.Info("exit checking iptables rules")
+}
+
+// StopCheckingRules stops checking if firewall rules are loaded.
+func (c *Common) StopCheckingRules() {
+ if c.RulesChecker != nil {
+ c.RulesChecker.Stop()
+ }
+ c.stopCheckerChan.stop()
+}
--- /dev/null
+// Package config provides functionality to load and monitor the system
+// firewall rules.
+// It's inherited by the different firewall packages (iptables, nftables).
+//
+// The firewall rules defined by the user are reloaded in these cases:
+// - When the file system-fw.json changes.
+// - When the firewall rules are not present when listing them.
+//
+package config
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "sync"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/fsnotify/fsnotify"
+)
+
+type callback func()
+
+// FwRule holds the fields of a rule
+type FwRule struct {
+ sync.RWMutex
+
+ Description string
+ Table string
+ Chain string
+ Parameters string
+ Target string
+ TargetParameters string
+}
+
+type rulesList struct {
+ sync.RWMutex
+
+ Rule *FwRule
+}
+
+// SystemConfig holds the list of rules to be added to the system
+type SystemConfig struct {
+ sync.RWMutex
+ SystemRules []*rulesList
+}
+
+// Config holds the functionality to re/load the firewall configuration from disk.
+// This is the configuration to manage the system firewall (iptables, nftables).
+type Config struct {
+ sync.Mutex
+
+ file string
+ watcher *fsnotify.Watcher
+ monitorExitChan chan bool
+ SysConfig SystemConfig
+
+ // subscribe to this channel to receive config reload events
+ ReloadConfChan chan bool
+
+ // preloadCallback is called before reloading the configuration,
+ // in order to delete old fw rules.
+ preloadCallback callback
+}
+
+// NewSystemFwConfig initializes config fields
+func (c *Config) NewSystemFwConfig(preLoadCb callback) (*Config, error) {
+ var err error
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ log.Warning("Error creating firewall config watcher: %s", err)
+ return nil, err
+ }
+
+ c.Lock()
+ defer c.Unlock()
+
+ c.file = "/etc/opensnitchd/system-fw.json"
+ c.monitorExitChan = make(chan bool, 1)
+ c.preloadCallback = preLoadCb
+ c.watcher = watcher
+ c.ReloadConfChan = make(chan bool, 1)
+ return c, nil
+}
+
+// LoadDiskConfiguration reads and loads the firewall configuration from disk
+func (c *Config) LoadDiskConfiguration(reload bool) {
+ c.Lock()
+ defer c.Unlock()
+
+ raw, err := ioutil.ReadFile(c.file)
+ if err != nil {
+ log.Error("Error reading firewall configuration from disk %s: %s", c.file, err)
+ return
+ }
+
+ c.loadConfiguration(raw)
+ // we need to monitor the configuration file for changes, regardless if it's
+ // malformed or not.
+ c.watcher.Remove(c.file)
+ if err := c.watcher.Add(c.file); err != nil {
+ log.Error("Could not watch firewall configuration: %s", err)
+ return
+ }
+
+ if reload {
+ c.ReloadConfChan <- true
+ return
+ }
+
+ go c.monitorConfigWorker()
+}
+
+// loadConfigutation reads the system firewall rules from disk.
+// Then the rules are added based on the configuration defined.
+func (c *Config) loadConfiguration(rawConfig []byte) {
+ c.SysConfig.Lock()
+ defer c.SysConfig.Unlock()
+
+ // delete old system rules, that may be different from the new ones
+ c.preloadCallback()
+
+ if err := json.Unmarshal(rawConfig, &c.SysConfig); err != nil {
+ // we only log the parser error, giving the user a chance to write a valid config
+ log.Error("Error parsing firewall configuration %s: %s", c.file, err)
+ }
+ log.Info("fw configuration loaded")
+}
+
+func (c *Config) saveConfiguration(rawConfig string) error {
+ conf, err := json.Marshal([]byte(rawConfig))
+ if err != nil {
+ log.Error("saving json firewall configuration: %s %s", err, conf)
+ return err
+ }
+
+ c.loadConfiguration([]byte(rawConfig))
+
+ if err = ioutil.WriteFile(c.file, []byte(rawConfig), 0644); err != nil {
+ log.Error("writing firewall configuration to disk: %s", err)
+ return err
+ }
+ return nil
+}
+
+// StopConfigWatcher stops the configuration watcher and stops the subroutine.
+func (c *Config) StopConfigWatcher() {
+ c.Lock()
+ defer c.Unlock()
+
+ if c.monitorExitChan != nil {
+ c.monitorExitChan <- true
+ close(c.monitorExitChan)
+ }
+ if c.ReloadConfChan != nil {
+ c.ReloadConfChan <- false // exit
+ close(c.ReloadConfChan)
+ }
+
+ if c.watcher != nil {
+ c.watcher.Remove(c.file)
+ c.watcher.Close()
+ }
+}
+
+func (c *Config) monitorConfigWorker() {
+ for {
+ select {
+ case <-c.monitorExitChan:
+ goto Exit
+ case event := <-c.watcher.Events:
+ if (event.Op&fsnotify.Write == fsnotify.Write) || (event.Op&fsnotify.Remove == fsnotify.Remove) {
+ c.LoadDiskConfiguration(true)
+ }
+ }
+ }
+Exit:
+ log.Debug("stop monitoring firewall config file")
+ c.Lock()
+ c.monitorExitChan = nil
+ c.Unlock()
+}
+
+// MonitorSystemFw waits for configuration reloads.
+func (c *Config) MonitorSystemFw(reloadCallback callback) {
+ for {
+ select {
+ case reload := <-c.ReloadConfChan:
+ if reload {
+ reloadCallback()
+ } else {
+ goto Exit
+ }
+ }
+ }
+Exit:
+ log.Info("iptables, stop monitoring system fw rules")
+ c.Lock()
+ c.ReloadConfChan = nil
+ c.Unlock()
+}
--- /dev/null
+package iptables
+
+import (
+ "os/exec"
+ "regexp"
+ "sync"
+
+ "github.com/evilsocket/opensnitch/daemon/firewall/common"
+ "github.com/evilsocket/opensnitch/daemon/firewall/config"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// Action is the modifier we apply to a rule.
+type Action string
+
+const (
+ // Name is the name that identifies this firewall
+ Name = "iptables"
+ // SystemRulePrefix prefix added to each system rule
+ SystemRulePrefix = "opensnitch-filter"
+)
+
+// Actions we apply to the firewall.
+const (
+ ADD = Action("-A")
+ INSERT = Action("-I")
+ DELETE = Action("-D")
+ FLUSH = Action("-F")
+ NEWCHAIN = Action("-N")
+ DELCHAIN = Action("-X")
+)
+
+// SystemChains holds the fw rules defined by the user
+type SystemChains struct {
+ sync.RWMutex
+ Rules map[string]config.FwRule
+}
+
+// Iptables struct holds the fields of the iptables fw
+type Iptables struct {
+ sync.Mutex
+ config.Config
+ common.Common
+
+ bin string
+ bin6 string
+
+ regexRulesQuery *regexp.Regexp
+ regexSystemRulesQuery *regexp.Regexp
+
+ chains SystemChains
+}
+
+// Fw initializes a new Iptables object
+func Fw() (*Iptables, error) {
+ if err := IsAvailable(); err != nil {
+ return nil, err
+ }
+
+ reRulesQuery, _ := regexp.Compile(`NFQUEUE.*ctstate NEW,RELATED.*NFQUEUE num.*bypass`)
+ reSystemRulesQuery, _ := regexp.Compile(SystemRulePrefix + ".*")
+
+ ipt := &Iptables{
+ bin: "iptables",
+ bin6: "ip6tables",
+ regexRulesQuery: reRulesQuery,
+ regexSystemRulesQuery: reSystemRulesQuery,
+ chains: SystemChains{Rules: make(map[string]config.FwRule)},
+ }
+ return ipt, nil
+}
+
+// Name returns the firewall name
+func (ipt *Iptables) Name() string {
+ return Name
+}
+
+// Init inserts the firewall rules and starts monitoring for firewall
+// changes.
+func (ipt *Iptables) Init(qNum *int) {
+ if ipt.IsRunning() {
+ return
+ }
+ ipt.SetQueueNum(qNum)
+
+ // In order to clean up any existing firewall rule before start,
+ // we need to load the fw configuration first.
+ ipt.NewSystemFwConfig(ipt.preloadConfCallback)
+ go ipt.MonitorSystemFw(ipt.AddSystemRules)
+ ipt.LoadDiskConfiguration(false)
+
+ // start from a clean state
+ ipt.CleanRules(false)
+ ipt.InsertRules()
+
+ ipt.AddSystemRules()
+ // start monitoring firewall rules to intercept network traffic
+ ipt.NewRulesChecker(ipt.AreRulesLoaded, ipt.reloadRulesCallback)
+
+ ipt.Running = true
+}
+
+// Stop deletes the firewall rules, allowing network traffic.
+func (ipt *Iptables) Stop() {
+ if ipt.Running == false {
+ return
+ }
+ ipt.StopConfigWatcher()
+ ipt.StopCheckingRules()
+ ipt.CleanRules(log.GetLogLevel() == log.DEBUG)
+
+ ipt.Running = false
+}
+
+// IsAvailable checks if iptables is installed in the system.
+func IsAvailable() error {
+ _, err := exec.Command("iptables", []string{"-V"}...).CombinedOutput()
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// InsertRules adds fw rules to intercept connections
+func (ipt *Iptables) InsertRules() {
+ if err4, err6 := ipt.QueueDNSResponses(true, true); err4 != nil || err6 != nil {
+ log.Error("Error while running DNS firewall rule: %s %s", err4, err6)
+ } else if err4, err6 = ipt.QueueConnections(true, true); err4 != nil || err6 != nil {
+ log.Fatal("Error while running conntrack firewall rule: %s %s", err4, err6)
+ }
+}
+
+// CleanRules deletes the rules we added.
+func (ipt *Iptables) CleanRules(logErrors bool) {
+ ipt.QueueDNSResponses(false, logErrors)
+ ipt.QueueConnections(false, logErrors)
+ ipt.DeleteSystemRules(true, logErrors)
+}
--- /dev/null
+package iptables
+
+import (
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// AreRulesLoaded checks if the firewall rules for intercept traffic are loaded.
+func (ipt *Iptables) AreRulesLoaded() bool {
+ var outMangle6 string
+
+ outMangle, err := core.Exec("iptables", []string{"-n", "-L", "OUTPUT", "-t", "mangle"})
+ if err != nil {
+ return false
+ }
+
+ if core.IPv6Enabled {
+ outMangle6, err = core.Exec("ip6tables", []string{"-n", "-L", "OUTPUT", "-t", "mangle"})
+ if err != nil {
+ return false
+ }
+ }
+
+ systemRulesLoaded := true
+ ipt.chains.RLock()
+ if len(ipt.chains.Rules) > 0 {
+ for _, rule := range ipt.chains.Rules {
+ if chainOut4, err4 := core.Exec("iptables", []string{"-n", "-L", rule.Chain, "-t", rule.Table}); err4 == nil {
+ if ipt.regexSystemRulesQuery.FindString(chainOut4) == "" {
+ systemRulesLoaded = false
+ break
+ }
+ }
+ if core.IPv6Enabled {
+ if chainOut6, err6 := core.Exec("ip6tables", []string{"-n", "-L", rule.Chain, "-t", rule.Table}); err6 == nil {
+ if ipt.regexSystemRulesQuery.FindString(chainOut6) == "" {
+ systemRulesLoaded = false
+ break
+ }
+ }
+ }
+ }
+ }
+ ipt.chains.RUnlock()
+
+ result := ipt.regexRulesQuery.FindString(outMangle) != "" &&
+ systemRulesLoaded
+
+ if core.IPv6Enabled {
+ result = result && ipt.regexRulesQuery.FindString(outMangle6) != ""
+ }
+
+ return result
+}
+
+func (ipt *Iptables) reloadRulesCallback() {
+ log.Important("firewall rules changed, reloading")
+ ipt.QueueDNSResponses(false, false)
+ ipt.QueueConnections(false, false)
+ ipt.InsertRules()
+ ipt.AddSystemRules()
+}
--- /dev/null
+package iptables
+
+import (
+ "fmt"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/vishvananda/netlink"
+)
+
+// RunRule inserts or deletes a firewall rule.
+func (ipt *Iptables) RunRule(action Action, enable bool, logError bool, rule []string) (err4, err6 error) {
+ if enable == false {
+ action = "-D"
+ }
+
+ rule = append([]string{string(action)}, rule...)
+
+ ipt.Lock()
+ defer ipt.Unlock()
+
+ if _, err4 = core.Exec(ipt.bin, rule); err4 != nil {
+ if logError {
+ log.Error("Error while running firewall rule, ipv4 err: %s", err4)
+ log.Error("rule: %s", rule)
+ }
+ }
+
+ // On some systems IPv6 is disabled
+ if core.IPv6Enabled {
+ if _, err6 = core.Exec(ipt.bin6, rule); err6 != nil {
+ if logError {
+ log.Error("Error while running firewall rule, ipv6 err: %s", err6)
+ log.Error("rule: %s", rule)
+ }
+ }
+ }
+
+ return
+}
+
+// QueueDNSResponses redirects DNS responses to us, in order to keep a cache
+// of resolved domains.
+// INPUT --protocol udp --sport 53 -j NFQUEUE --queue-num 0 --queue-bypass
+func (ipt *Iptables) QueueDNSResponses(enable bool, logError bool) (err4, err6 error) {
+ return ipt.RunRule(INSERT, enable, logError, []string{
+ "INPUT",
+ "--protocol", "udp",
+ "--sport", "53",
+ "-j", "NFQUEUE",
+ "--queue-num", fmt.Sprintf("%d", ipt.QueueNum),
+ "--queue-bypass",
+ })
+}
+
+// QueueConnections inserts the firewall rule which redirects connections to us.
+// They are queued until the user denies/accept them, or reaches a timeout.
+// OUTPUT -t mangle -m conntrack --ctstate NEW,RELATED -j NFQUEUE --queue-num 0 --queue-bypass
+func (ipt *Iptables) QueueConnections(enable bool, logError bool) (error, error) {
+ err4, err6 := ipt.RunRule(INSERT, enable, logError, []string{
+ "OUTPUT",
+ "-t", "mangle",
+ "-m", "conntrack",
+ "--ctstate", "NEW,RELATED",
+ "-j", "NFQUEUE",
+ "--queue-num", fmt.Sprintf("%d", ipt.QueueNum),
+ "--queue-bypass",
+ })
+ if enable {
+ // flush conntrack as soon as netfilter rule is set. This ensures that already-established
+ // connections will go to netfilter queue.
+ if err := netlink.ConntrackTableFlush(netlink.ConntrackTable); err != nil {
+ log.Error("error in ConntrackTableFlush %s", err)
+ }
+ }
+ return err4, err6
+}
--- /dev/null
+package iptables
+
+import (
+ "strings"
+
+ "github.com/evilsocket/opensnitch/daemon/firewall/config"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// CreateSystemRule creates the custom firewall chains and adds them to the system.
+func (ipt *Iptables) CreateSystemRule(rule *config.FwRule, logErrors bool) {
+ ipt.chains.Lock()
+ defer ipt.chains.Unlock()
+ if rule == nil {
+ return
+ }
+
+ chainName := SystemRulePrefix + "-" + rule.Chain
+ if _, ok := ipt.chains.Rules[rule.Table+"-"+chainName]; ok {
+ return
+ }
+ ipt.RunRule(NEWCHAIN, true, logErrors, []string{chainName, "-t", rule.Table})
+
+ // Insert the rule at the top of the chain
+ if err4, err6 := ipt.RunRule(INSERT, true, logErrors, []string{rule.Chain, "-t", rule.Table, "-j", chainName}); err4 == nil && err6 == nil {
+ ipt.chains.Rules[rule.Table+"-"+chainName] = *rule
+ }
+}
+
+// DeleteSystemRules deletes the system rules.
+// If force is false and the rule has not been previously added,
+// it won't try to delete the rules. Otherwise it'll try to delete them.
+func (ipt *Iptables) DeleteSystemRules(force, logErrors bool) {
+ ipt.chains.Lock()
+ defer ipt.chains.Unlock()
+
+ for _, r := range ipt.SysConfig.SystemRules {
+ if r.Rule == nil {
+ continue
+ }
+ chain := SystemRulePrefix + "-" + r.Rule.Chain
+ if _, ok := ipt.chains.Rules[r.Rule.Table+"-"+chain]; !ok && !force {
+ continue
+ }
+ ipt.RunRule(FLUSH, true, false, []string{chain, "-t", r.Rule.Table})
+ ipt.RunRule(DELETE, false, logErrors, []string{r.Rule.Chain, "-t", r.Rule.Table, "-j", chain})
+ ipt.RunRule(DELCHAIN, true, false, []string{chain, "-t", r.Rule.Table})
+ delete(ipt.chains.Rules, r.Rule.Table+"-"+chain)
+ }
+}
+
+// AddSystemRule inserts a new rule.
+func (ipt *Iptables) AddSystemRule(rule *config.FwRule, enable bool) (err4, err6 error) {
+ if rule == nil {
+ return nil, nil
+ }
+ rule.RLock()
+ defer rule.RUnlock()
+
+ chain := SystemRulePrefix + "-" + rule.Chain
+ if rule.Table == "" {
+ rule.Table = "filter"
+ }
+ r := []string{chain, "-t", rule.Table}
+ if rule.Parameters != "" {
+ r = append(r, strings.Split(rule.Parameters, " ")...)
+ }
+ r = append(r, []string{"-j", rule.Target}...)
+ if rule.TargetParameters != "" {
+ r = append(r, strings.Split(rule.TargetParameters, " ")...)
+ }
+
+ return ipt.RunRule(ADD, enable, true, r)
+}
+
+// AddSystemRules creates the system firewall from configuration.
+func (ipt *Iptables) AddSystemRules() {
+ ipt.DeleteSystemRules(true, false)
+
+ for _, r := range ipt.SysConfig.SystemRules {
+ ipt.CreateSystemRule(r.Rule, true)
+ ipt.AddSystemRule(r.Rule, true)
+ }
+}
+
+// preloadConfCallback gets called before the fw configuration is reloaded
+func (ipt *Iptables) preloadConfCallback() {
+ ipt.DeleteSystemRules(true, log.GetLogLevel() == log.DEBUG)
+}
--- /dev/null
+package nftables
+
+import (
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// AreRulesLoaded checks if the firewall rules for intercept traffic are loaded.
+func (n *Nft) AreRulesLoaded() bool {
+ n.Lock()
+ defer n.Unlock()
+
+ nRules := 0
+ for _, table := range n.mangleTables {
+ rules, err := n.conn.GetRule(table, n.outputChains[table])
+ if err != nil {
+ log.Error("nftables mangle rules error: %s, %s", table.Name, n.outputChains[table].Name)
+ return false
+ }
+ for _, r := range rules {
+ if string(r.UserData) == fwKey {
+ nRules++
+ }
+ }
+ }
+ if nRules != 2 {
+ log.Warning("nftables mangle rules not loaded: %d", nRules)
+ return false
+ }
+
+ nRules = 0
+ for _, table := range n.filterTables {
+ rules, err := n.conn.GetRule(table, n.inputChains[table])
+ if err != nil {
+ log.Error("nftables filter rules error: %s, %s", table.Name, n.inputChains[table].Name)
+ return false
+ }
+ for _, r := range rules {
+ if string(r.UserData) == fwKey {
+ nRules++
+ }
+ }
+ }
+ if nRules != 2 {
+ log.Warning("nfables filter rules not loaded: %d", nRules)
+ return false
+ }
+
+ return true
+}
+
+func (n *Nft) reloadRulesCallback() {
+ log.Important("nftables firewall rules changed, reloading")
+ n.AddSystemRules()
+ n.InsertRules()
+}
--- /dev/null
+package nftables
+
+import (
+ "sync"
+
+ "github.com/evilsocket/opensnitch/daemon/firewall/common"
+ "github.com/evilsocket/opensnitch/daemon/firewall/config"
+ "github.com/evilsocket/opensnitch/daemon/firewall/iptables"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/google/nftables"
+)
+
+const (
+ // Name is the name that identifies this firewall
+ Name = "nftables"
+
+ mangleTableName = "mangle"
+ filterTableName = "filter"
+ // The following chains will be under our own mangle or filter tables.
+ // There shouldn't be other chains with the same name here.
+ outputChain = "output"
+ inputChain = "input"
+ // key assigned to every fw rule we add, in order to get rules by this key.
+ fwKey = "opensnitch-key"
+)
+
+var (
+ filterTable = &nftables.Table{
+ Family: nftables.TableFamilyIPv4,
+ Name: filterTableName,
+ }
+ filterTable6 = &nftables.Table{
+ Family: nftables.TableFamilyIPv6,
+ Name: filterTableName,
+ }
+ mangleTable = &nftables.Table{
+ Family: nftables.TableFamilyIPv4,
+ Name: mangleTableName,
+ }
+ mangleTable6 = &nftables.Table{
+ Family: nftables.TableFamilyIPv6,
+ Name: mangleTableName,
+ }
+)
+
+// Nft holds the fields of our nftables firewall
+type Nft struct {
+ sync.Mutex
+ config.Config
+ common.Common
+
+ conn *nftables.Conn
+
+ mangleTables []*nftables.Table
+ filterTables []*nftables.Table
+ outputChains map[*nftables.Table]*nftables.Chain
+ inputChains map[*nftables.Table]*nftables.Chain
+
+ chains iptables.SystemChains
+}
+
+// NewNft creates a new nftables object
+func NewNft() *nftables.Conn {
+ return &nftables.Conn{}
+}
+
+// Fw initializes a new nftables object
+func Fw() (*Nft, error) {
+ n := &Nft{
+ outputChains: make(map[*nftables.Table]*nftables.Chain),
+ inputChains: make(map[*nftables.Table]*nftables.Chain),
+ chains: iptables.SystemChains{Rules: make(map[string]config.FwRule)},
+ }
+ return n, nil
+}
+
+// Name returns the name of the firewall
+func (n *Nft) Name() string {
+ return Name
+}
+
+// Init inserts the firewall rules and starts monitoring for firewall
+// changes.
+func (n *Nft) Init(qNum *int) {
+ if n.IsRunning() {
+ return
+ }
+ n.SetQueueNum(qNum)
+ n.conn = NewNft()
+
+ // In order to clean up any existing firewall rule before start,
+ // we need to load the fw configuration first.
+ n.NewSystemFwConfig(n.preloadConfCallback)
+ go n.MonitorSystemFw(n.AddSystemRules)
+ n.LoadDiskConfiguration(false)
+
+ // start from a clean state
+ n.CleanRules(false)
+ n.AddSystemRules()
+
+ n.InsertRules()
+ // start monitoring firewall rules to intercept network traffic.
+ n.NewRulesChecker(n.AreRulesLoaded, n.reloadRulesCallback)
+
+ n.Running = true
+}
+
+// Stop deletes the firewall rules, allowing network traffic.
+func (n *Nft) Stop() {
+ if n.IsRunning() == false {
+ return
+ }
+ n.StopConfigWatcher()
+ n.StopCheckingRules()
+ n.CleanRules(log.GetLogLevel() == log.DEBUG)
+
+ n.Running = false
+}
+
+// InsertRules adds fw rules to intercept connections
+func (n *Nft) InsertRules() {
+ n.delInterceptionRules()
+ n.addGlobalTables()
+ n.addGlobalChains()
+
+ if err, _ := n.QueueDNSResponses(true, true); err != nil {
+ log.Error("Error while Running DNS nftables rule: %s", err)
+ } else if err, _ = n.QueueConnections(true, true); err != nil {
+ log.Fatal("Error while Running conntrack nftables rule: %s", err)
+ }
+}
+
+// CleanRules deletes the rules we added.
+func (n *Nft) CleanRules(logErrors bool) {
+ n.delInterceptionRules()
+ err := n.conn.Flush()
+ if err != nil && logErrors {
+ log.Error("Error cleaning nftables tables: %s", err)
+ }
+ n.DeleteSystemRules(true, logErrors)
+}
--- /dev/null
+package nftables
+
+import (
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/google/nftables"
+ "github.com/google/nftables/binaryutil"
+ "github.com/google/nftables/expr"
+ "github.com/vishvananda/netlink"
+ "golang.org/x/sys/unix"
+)
+
+func (n *Nft) addGlobalTables() error {
+ filter := n.conn.AddTable(filterTable)
+ filter6 := n.conn.AddTable(filterTable6)
+
+ mangle := n.conn.AddTable(mangleTable)
+ mangle6 := n.conn.AddTable(mangleTable6)
+ n.mangleTables = []*nftables.Table{mangle, mangle6}
+ n.filterTables = []*nftables.Table{filter, filter6}
+
+ // apply changes
+ if err := n.conn.Flush(); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// TODO: add more parameters, make it more generic
+func (n *Nft) addChain(name string, table *nftables.Table, prio *nftables.ChainPriority, ctype nftables.ChainType, hook *nftables.ChainHook) *nftables.Chain {
+ // nft list chains
+ return n.conn.AddChain(&nftables.Chain{
+ Name: name,
+ Table: table,
+ Type: ctype,
+ Hooknum: hook,
+ Priority: prio,
+ //Policy: nftables.ChainPolicyDrop
+ })
+}
+
+func (n *Nft) addGlobalChains() error {
+ // nft list tables
+ for _, table := range n.mangleTables {
+ n.outputChains[table] = n.addChain(outputChain, table, nftables.ChainPriorityMangle, nftables.ChainTypeRoute, nftables.ChainHookOutput)
+ }
+ for _, table := range n.filterTables {
+ n.inputChains[table] = n.addChain(inputChain, table, nftables.ChainPriorityFilter, nftables.ChainTypeFilter, nftables.ChainHookInput)
+ }
+ // apply changes
+ if err := n.conn.Flush(); err != nil {
+ log.Warning("Error adding nftables mangle tables: %v", err)
+ }
+
+ return nil
+}
+
+// QueueDNSResponses redirects DNS responses to us, in order to keep a cache
+// of resolved domains.
+// nft insert rule ip filter input udp sport 53 queue num 0 bypass
+func (n *Nft) QueueDNSResponses(enable bool, logError bool) (error, error) {
+ if n.conn == nil {
+ return nil, nil
+ }
+ for _, table := range n.filterTables {
+ // nft list ruleset -a
+ n.conn.InsertRule(&nftables.Rule{
+ Position: 0,
+ Table: table,
+ Chain: n.inputChains[table],
+ Exprs: []expr.Any{
+ &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
+ &expr.Cmp{
+ Op: expr.CmpOpEq,
+ Register: 1,
+ Data: []byte{unix.IPPROTO_UDP},
+ },
+ &expr.Payload{
+ DestRegister: 1,
+ Base: expr.PayloadBaseTransportHeader,
+ Offset: 0,
+ Len: 2,
+ },
+ &expr.Cmp{
+ Op: expr.CmpOpEq,
+ Register: 1,
+ Data: binaryutil.BigEndian.PutUint16(uint16(53)),
+ },
+ &expr.Queue{
+ Num: n.QueueNum,
+ Flag: expr.QueueFlagBypass,
+ },
+ },
+ // rule key, to allow get it later by key
+ UserData: []byte(fwKey),
+ })
+ }
+ // apply changes
+ if err := n.conn.Flush(); err != nil {
+ return err, nil
+ }
+
+ return nil, nil
+}
+
+// QueueConnections inserts the firewall rule which redirects connections to us.
+// They are queued until the user denies/accept them, or reaches a timeout.
+// nft insert rule ip mangle OUTPUT ct state new queue num 0 bypass
+func (n *Nft) QueueConnections(enable bool, logError bool) (error, error) {
+ if n.conn == nil {
+ return nil, nil
+ }
+ if enable {
+ // flush conntrack as soon as netfilter rule is set. This ensures that already-established
+ // connections will go to netfilter queue.
+ if err := netlink.ConntrackTableFlush(netlink.ConntrackTable); err != nil {
+ log.Error("nftables, error in ConntrackTableFlush %s", err)
+ }
+ }
+
+ for _, table := range n.mangleTables {
+ n.conn.InsertRule(&nftables.Rule{
+ Position: 0,
+ Table: table,
+ Chain: n.outputChains[table],
+ Exprs: []expr.Any{
+ &expr.Ct{Register: 1, SourceRegister: false, Key: expr.CtKeySTATE},
+ &expr.Bitwise{
+ SourceRegister: 1,
+ DestRegister: 1,
+ Len: 4,
+ Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitNEW | expr.CtStateBitRELATED),
+ Xor: binaryutil.NativeEndian.PutUint32(0),
+ },
+ &expr.Cmp{Op: expr.CmpOpNeq, Register: 1, Data: []byte{0, 0, 0, 0}},
+ &expr.Queue{
+ Num: n.QueueNum,
+ Flag: expr.QueueFlagBypass,
+ },
+ },
+ // rule key, to allow get it later by key
+ UserData: []byte(fwKey),
+ })
+ }
+ // apply changes
+ if err := n.conn.Flush(); err != nil {
+ return err, nil
+ }
+
+ return nil, nil
+}
+
+func (n *Nft) delInterceptionRules() {
+ n.delRulesByKey(fwKey)
+}
+
+func (n *Nft) delRulesByKey(key string) {
+ chains, err := n.conn.ListChains()
+ if err != nil {
+ log.Warning("nftables, error listing chains: %s", err)
+ return
+ }
+ commit := false
+ for _, c := range chains {
+ rules, err := n.conn.GetRule(c.Table, c)
+ if err != nil {
+ log.Warning("nftables, error listing rules (%s): %s", c.Table.Name, err)
+ continue
+ }
+
+ commit = false
+ for _, r := range rules {
+ if string(r.UserData) != key {
+ continue
+ }
+ // just passing the rule object doesn't work.
+ if err := n.conn.DelRule(&nftables.Rule{
+ Table: c.Table,
+ Chain: c,
+ Handle: r.Handle,
+ }); err != nil {
+ log.Warning("nftables, error adding rule to be deleted (%s/%s): %s", c.Table.Name, c.Name, err)
+ continue
+ }
+ commit = true
+ }
+ if commit {
+ if err := n.conn.Flush(); err != nil {
+ log.Warning("nftables, error deleting interception rules (%s/%s): %s", c.Table.Name, c.Name, err)
+ }
+ }
+ if rules, err := n.conn.GetRule(c.Table, c); err == nil {
+ if commit && len(rules) == 0 {
+ n.conn.DelChain(c)
+ n.conn.Flush()
+ }
+ }
+ }
+
+ return
+}
--- /dev/null
+package nftables
+
+import (
+ "github.com/evilsocket/opensnitch/daemon/firewall/config"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// CreateSystemRule create the custom firewall chains and adds them to system.
+// nft insert rule ip opensnitch-filter opensnitch-input udp dport 1153
+func (n *Nft) CreateSystemRule(rule *config.FwRule, logErrors bool) {
+ // TODO
+}
+
+// DeleteSystemRules deletes the system rules.
+// If force is false and the rule has not been previously added,
+// it won't try to delete the rules. Otherwise it'll try to delete them.
+func (n *Nft) DeleteSystemRules(force, logErrors bool) {
+ // TODO
+}
+
+// AddSystemRule inserts a new rule.
+func (n *Nft) AddSystemRule(rule *config.FwRule, enable bool) (error, error) {
+ // TODO
+ return nil, nil
+}
+
+// AddSystemRules creates the system firewall from configuration
+func (n *Nft) AddSystemRules() {
+ n.DeleteSystemRules(true, false)
+
+ for _, r := range n.SysConfig.SystemRules {
+ n.CreateSystemRule(r.Rule, true)
+ n.AddSystemRule(r.Rule, true)
+ }
+}
+
+// preloadConfCallback gets called before the fw configuration is reloaded
+func (n *Nft) preloadConfCallback() {
+ n.DeleteSystemRules(true, log.GetLogLevel() == log.DEBUG)
+}
--- /dev/null
+package firewall
+
+import (
+ "github.com/evilsocket/opensnitch/daemon/firewall/config"
+ "github.com/evilsocket/opensnitch/daemon/firewall/iptables"
+ "github.com/evilsocket/opensnitch/daemon/firewall/nftables"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// Firewall is the interface that all firewalls (iptables, nftables) must implement.
+type Firewall interface {
+ Init(*int)
+ Stop()
+ Name() string
+ IsRunning() bool
+ SetQueueNum(num *int)
+
+ InsertRules()
+ QueueDNSResponses(bool, bool) (error, error)
+ QueueConnections(bool, bool) (error, error)
+ CleanRules(bool)
+
+ AddSystemRules()
+ DeleteSystemRules(bool, bool)
+ AddSystemRule(*config.FwRule, bool) (error, error)
+ CreateSystemRule(*config.FwRule, bool)
+}
+
+var fw Firewall
+
+// IsRunning returns if the firewall is running or not.
+func IsRunning() bool {
+ return fw != nil && fw.IsRunning()
+}
+
+// CleanRules deletes the rules we added.
+func CleanRules(logErrors bool) {
+ if fw == nil {
+ return
+ }
+ fw.CleanRules(logErrors)
+}
+
+// Stop deletes the firewall rules, allowing network traffic.
+func Stop() {
+ if fw == nil {
+ return
+ }
+ fw.Stop()
+}
+
+// Init initializes the firewall and loads firewall rules.
+func Init(fwType string, qNum *int) {
+ var err error
+
+ if fwType == iptables.Name {
+ fw, err = iptables.Fw()
+ if err != nil {
+ log.Warning("iptables not available: %s", err)
+ }
+ }
+
+ // if iptables is not installed, we can add nftables rules directly to the kernel,
+ // without relying on any binaries.
+ if fwType == nftables.Name || err != nil {
+ fw, err = nftables.Fw()
+ if err != nil {
+ log.Warning("nftables not available: %s", err)
+ }
+ }
+
+ if err != nil {
+ log.Warning("firewall error: %s, not iptables nor nftables are available or are usable. Please, report it on github.", err)
+ return
+ }
+
+ if fw == nil {
+ log.Error("firewall not initialized.")
+ return
+ }
+ fw.Stop()
+ fw.Init(qNum)
+
+ log.Info("Using %s firewall", fw.Name())
+}
--- /dev/null
+module github.com/evilsocket/opensnitch/daemon
+
+go 1.15
+
+require (
+ github.com/fsnotify/fsnotify v1.4.7
+ github.com/golang/protobuf v1.5.0
+ github.com/google/gopacket v1.1.14
+ github.com/google/nftables v0.1.0
+ github.com/iovisor/gobpf v0.2.0
+ github.com/vishvananda/netlink v0.0.0-20210811191823-e1a867c6b452
+ golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271
+ golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1
+ google.golang.org/grpc v1.32.0
+ google.golang.org/protobuf v1.26.0
+)
--- /dev/null
+package log
+
+import (
+ "fmt"
+ "os"
+ "strings"
+ "sync"
+ "time"
+)
+
+type Handler func(format string, args ...interface{})
+
+// https://misc.flogisoft.com/bash/tip_colors_and_formatting
+const (
+ BOLD = "\033[1m"
+ DIM = "\033[2m"
+
+ RED = "\033[31m"
+ GREEN = "\033[32m"
+ BLUE = "\033[34m"
+ YELLOW = "\033[33m"
+
+ FG_BLACK = "\033[30m"
+ FG_WHITE = "\033[97m"
+
+ BG_DGRAY = "\033[100m"
+ BG_RED = "\033[41m"
+ BG_GREEN = "\033[42m"
+ BG_YELLOW = "\033[43m"
+ BG_LBLUE = "\033[104m"
+
+ RESET = "\033[0m"
+)
+
+// log level constants
+const (
+ DEBUG = iota
+ INFO
+ IMPORTANT
+ WARNING
+ ERROR
+ FATAL
+)
+
+//
+var (
+ WithColors = true
+ Output = os.Stdout
+ StdoutFile = "/dev/stdout"
+ DateFormat = "2006-01-02 15:04:05"
+ MinLevel = INFO
+
+ mutex = &sync.RWMutex{}
+ labels = map[int]string{
+ DEBUG: "DBG",
+ INFO: "INF",
+ IMPORTANT: "IMP",
+ WARNING: "WAR",
+ ERROR: "ERR",
+ FATAL: "!!!",
+ }
+ colors = map[int]string{
+ DEBUG: DIM + FG_BLACK + BG_DGRAY,
+ INFO: FG_WHITE + BG_GREEN,
+ IMPORTANT: FG_WHITE + BG_LBLUE,
+ WARNING: FG_WHITE + BG_YELLOW,
+ ERROR: FG_WHITE + BG_RED,
+ FATAL: FG_WHITE + BG_RED + BOLD,
+ }
+)
+
+// Wrap wraps a text with effects
+func Wrap(s, effect string) string {
+ if WithColors == true {
+ s = effect + s + RESET
+ }
+ return s
+}
+
+// Dim dims a text
+func Dim(s string) string {
+ return Wrap(s, DIM)
+}
+
+// Bold bolds a text
+func Bold(s string) string {
+ return Wrap(s, BOLD)
+}
+
+// Red reds the text
+func Red(s string) string {
+ return Wrap(s, RED)
+}
+
+// Green greens the text
+func Green(s string) string {
+ return Wrap(s, GREEN)
+}
+
+// Blue blues the text
+func Blue(s string) string {
+ return Wrap(s, BLUE)
+}
+
+// Yellow yellows the text
+func Yellow(s string) string {
+ return Wrap(s, YELLOW)
+}
+
+// Raw prints out a text without colors
+func Raw(format string, args ...interface{}) {
+ mutex.Lock()
+ defer mutex.Unlock()
+ fmt.Fprintf(Output, format, args...)
+}
+
+// SetLogLevel sets the log level
+func SetLogLevel(newLevel int) {
+ mutex.Lock()
+ defer mutex.Unlock()
+ MinLevel = newLevel
+}
+
+// GetLogLevel returns the current log level configured.
+func GetLogLevel() int {
+ mutex.Lock()
+ defer mutex.Unlock()
+
+ return MinLevel
+}
+
+// Log prints out a text with the given color and format
+func Log(level int, format string, args ...interface{}) {
+ mutex.Lock()
+ defer mutex.Unlock()
+ if level >= MinLevel {
+ label := labels[level]
+ color := colors[level]
+ when := time.Now().UTC().Format(DateFormat)
+
+ what := fmt.Sprintf(format, args...)
+ if strings.HasSuffix(what, "\n") == false {
+ what += "\n"
+ }
+
+ l := Dim("[%s]")
+ r := Wrap(" %s ", color) + " %s"
+
+ fmt.Fprintf(Output, l+" "+r, when, label, what)
+ }
+}
+
+func setDefaultLogOutput() {
+ mutex.Lock()
+ Output = os.Stdout
+ mutex.Unlock()
+}
+
+// OpenFile opens a file to print out the logs
+func OpenFile(logFile string) (err error) {
+ if logFile == StdoutFile {
+ setDefaultLogOutput()
+ return
+ }
+
+ if Output, err = os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err != nil {
+ Error("Error opening log: %s %s", logFile, err)
+ //fallback to stdout
+ setDefaultLogOutput()
+ }
+ Important("Start writing logs to %s", logFile)
+
+ return err
+}
+
+// Close closes the current output file descriptor
+func Close() {
+ if Output != os.Stdout {
+ Output.Close()
+ }
+}
+
+// Debug is the log level for debugging purposes
+func Debug(format string, args ...interface{}) {
+ Log(DEBUG, format, args...)
+}
+
+// Info is the log level for informative messages
+func Info(format string, args ...interface{}) {
+ Log(INFO, format, args...)
+}
+
+// Important is the log level for things that must pay attention
+func Important(format string, args ...interface{}) {
+ Log(IMPORTANT, format, args...)
+}
+
+// Warning is the log level for non-critical errors
+func Warning(format string, args ...interface{}) {
+ Log(WARNING, format, args...)
+}
+
+// Error is the log level for errors that should be corrected
+func Error(format string, args ...interface{}) {
+ Log(ERROR, format, args...)
+}
+
+// Fatal is the log level for errors that must be corrected before continue
+func Fatal(format string, args ...interface{}) {
+ Log(FATAL, format, args...)
+ os.Exit(1)
+}
--- /dev/null
+package main
+
+import (
+ "bytes"
+ "context"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ golog "log"
+ "os"
+ "os/signal"
+ "runtime"
+ "runtime/pprof"
+ "syscall"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/conman"
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/dns"
+ "github.com/evilsocket/opensnitch/daemon/firewall"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/evilsocket/opensnitch/daemon/netfilter"
+ "github.com/evilsocket/opensnitch/daemon/netlink"
+ "github.com/evilsocket/opensnitch/daemon/procmon/monitor"
+ "github.com/evilsocket/opensnitch/daemon/rule"
+ "github.com/evilsocket/opensnitch/daemon/statistics"
+ "github.com/evilsocket/opensnitch/daemon/ui"
+)
+
+var (
+ showVersion = false
+ procmonMethod = ""
+ logFile = ""
+ rulesPath = "rules"
+ noLiveReload = false
+ queueNum = 0
+ repeatQueueNum int //will be set later to queueNum + 1
+ workers = 16
+ debug = false
+ warning = false
+ important = false
+ errorlog = false
+
+ uiSocket = ""
+ uiClient = (*ui.Client)(nil)
+
+ cpuProfile = ""
+ memProfile = ""
+
+ ctx = (context.Context)(nil)
+ cancel = (context.CancelFunc)(nil)
+ err = (error)(nil)
+ rules = (*rule.Loader)(nil)
+ stats = (*statistics.Statistics)(nil)
+ queue = (*netfilter.Queue)(nil)
+ repeatPktChan = (<-chan netfilter.Packet)(nil)
+ pktChan = (<-chan netfilter.Packet)(nil)
+ wrkChan = (chan netfilter.Packet)(nil)
+ sigChan = (chan os.Signal)(nil)
+ exitChan = (chan bool)(nil)
+)
+
+func init() {
+ flag.BoolVar(&showVersion, "version", debug, "Show daemon version of this executable and exit.")
+
+ flag.StringVar(&procmonMethod, "process-monitor-method", procmonMethod, "How to search for processes path. Options: ftrace, audit (experimental), ebpf (experimental), proc (default)")
+ flag.StringVar(&uiSocket, "ui-socket", uiSocket, "Path the UI gRPC service listener (https://github.com/grpc/grpc/blob/master/doc/naming.md).")
+ flag.StringVar(&rulesPath, "rules-path", rulesPath, "Path to load JSON rules from.")
+ flag.IntVar(&queueNum, "queue-num", queueNum, "Netfilter queue number.")
+ flag.IntVar(&workers, "workers", workers, "Number of concurrent workers.")
+ flag.BoolVar(&noLiveReload, "no-live-reload", debug, "Disable rules live reloading.")
+
+ flag.StringVar(&logFile, "log-file", logFile, "Write logs to this file instead of the standard output.")
+ flag.BoolVar(&debug, "debug", debug, "Enable debug level logs.")
+ flag.BoolVar(&warning, "warning", warning, "Enable warning level logs.")
+ flag.BoolVar(&important, "important", important, "Enable important level logs.")
+ flag.BoolVar(&errorlog, "error", errorlog, "Enable error level logs.")
+
+ flag.StringVar(&cpuProfile, "cpu-profile", cpuProfile, "Write CPU profile to this file.")
+ flag.StringVar(&memProfile, "mem-profile", memProfile, "Write memory profile to this file.")
+}
+
+func overwriteLogging() bool {
+ return debug || warning || important || errorlog || logFile != ""
+}
+
+func setupLogging() {
+ golog.SetOutput(ioutil.Discard)
+ if debug {
+ log.SetLogLevel(log.DEBUG)
+ } else if warning {
+ log.SetLogLevel(log.WARNING)
+ } else if important {
+ log.SetLogLevel(log.IMPORTANT)
+ } else if errorlog {
+ log.SetLogLevel(log.ERROR)
+ } else {
+ log.SetLogLevel(log.INFO)
+ }
+
+ var logFileToUse string
+ if logFile == "" {
+ logFileToUse = log.StdoutFile
+ } else {
+ logFileToUse = logFile
+ }
+ log.Close()
+ if err := log.OpenFile(logFileToUse); err != nil {
+ log.Error("Error opening user defined log: %s %s", logFileToUse, err)
+ }
+}
+
+func setupSignals() {
+ sigChan = make(chan os.Signal, 1)
+ exitChan = make(chan bool, workers+1)
+ signal.Notify(sigChan,
+ syscall.SIGHUP,
+ syscall.SIGINT,
+ syscall.SIGTERM,
+ syscall.SIGQUIT)
+ go func() {
+ sig := <-sigChan
+ log.Raw("\n")
+ log.Important("Got signal: %v", sig)
+ cancel()
+ }()
+}
+
+func worker(id int) {
+ log.Debug("Worker #%d started.", id)
+ for true {
+ select {
+ case <-ctx.Done():
+ goto Exit
+ default:
+ pkt, ok := <-wrkChan
+ if !ok {
+ log.Debug("worker channel closed %d", id)
+ goto Exit
+ }
+ onPacket(pkt)
+ }
+ }
+Exit:
+ log.Debug("worker #%d exit", id)
+}
+
+func setupWorkers() {
+ log.Debug("Starting %d workers ...", workers)
+ // setup the workers
+ wrkChan = make(chan netfilter.Packet)
+ for i := 0; i < workers; i++ {
+ go worker(i)
+ }
+}
+
+func doCleanup(queue, repeatQueue *netfilter.Queue) {
+ log.Info("Cleaning up ...")
+ firewall.Stop()
+ monitor.End()
+ uiClient.Close()
+ queue.Close()
+ repeatQueue.Close()
+
+ if cpuProfile != "" {
+ pprof.StopCPUProfile()
+ }
+
+ if memProfile != "" {
+ f, err := os.Create(memProfile)
+ if err != nil {
+ fmt.Printf("Could not create memory profile: %s\n", err)
+ return
+ }
+ defer f.Close()
+ runtime.GC() // get up-to-date statistics
+ if err := pprof.WriteHeapProfile(f); err != nil {
+ fmt.Printf("Could not write memory profile: %s\n", err)
+ }
+ }
+}
+
+func onPacket(packet netfilter.Packet) {
+ // DNS response, just parse, track and accept.
+ if dns.TrackAnswers(packet.Packet) == true {
+ packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark)
+ stats.OnDNSResponse()
+ return
+ }
+
+ // Parse the connection state
+ con := conman.Parse(packet, uiClient.InterceptUnknown())
+ if con == nil {
+ applyDefaultAction(&packet)
+ return
+ }
+ // accept our own connections
+ if con.Process.ID == os.Getpid() {
+ packet.SetVerdict(netfilter.NF_ACCEPT)
+ return
+ }
+
+ // search a match in preloaded rules
+ r := acceptOrDeny(&packet, con)
+
+ stats.OnConnectionEvent(con, r, r == nil)
+}
+
+func applyDefaultAction(packet *netfilter.Packet) {
+ if uiClient.DefaultAction() == rule.Allow {
+ packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark)
+ } else {
+ packet.SetVerdict(netfilter.NF_DROP)
+ }
+}
+
+func acceptOrDeny(packet *netfilter.Packet, con *conman.Connection) *rule.Rule {
+ r := rules.FindFirstMatch(con)
+ if r == nil {
+ // no rule matched
+ // Note that as soon as we set a verdict on a packet, the next packet in the netfilter queue
+ // will begin to be processed even if this function hasn't yet returned
+
+ // send a request to the UI client if
+ // 1) connected and running and 2) we are not already asking
+ if uiClient.Connected() == false || uiClient.GetIsAsking() == true {
+ applyDefaultAction(packet)
+ log.Debug("UI is not running or busy, connected: %v, running: %v", uiClient.Connected(), uiClient.GetIsAsking())
+ return nil
+ }
+
+ uiClient.SetIsAsking(true)
+ defer uiClient.SetIsAsking(false)
+
+ // In order not to block packet processing, we send our packet to a different netfilter queue
+ // and then immediately pull it back out of that queue
+ packet.SetRequeueVerdict(uint16(repeatQueueNum))
+
+ var o bool
+ var pkt netfilter.Packet
+ // don't wait for the packet longer than 1 sec
+ select {
+ case pkt, o = <-repeatPktChan:
+ if !o {
+ log.Debug("error while receiving packet from repeatPktChan")
+ return nil
+ }
+ case <-time.After(1 * time.Second):
+ log.Debug("timed out while receiving packet from repeatPktChan")
+ return nil
+ }
+
+ //check if the pulled out packet is the same we put in
+ if res := bytes.Compare(packet.Packet.Data(), pkt.Packet.Data()); res != 0 {
+ log.Error("The packet which was requeued has changed abruptly. This should never happen. Please report this incident to the Opensnitch developers. %v %v ", packet, pkt)
+ return nil
+ }
+ packet = &pkt
+
+ r = uiClient.Ask(con)
+ if r == nil {
+ log.Error("Invalid rule received, applying default action")
+ applyDefaultAction(packet)
+ return nil
+ }
+ ok := false
+ pers := ""
+ action := string(r.Action)
+ if r.Action == rule.Allow {
+ action = log.Green(action)
+ } else {
+ action = log.Red(action)
+ }
+
+ // check if and how the rule needs to be saved
+ if r.Duration == rule.Always {
+ pers = "Saved"
+ // add to the loaded rules and persist on disk
+ if err := rules.Add(r, true); err != nil {
+ log.Error("Error while saving rule: %s", err)
+ } else {
+ ok = true
+ }
+ } else {
+ pers = "Added"
+ // add to the rules but do not save to disk
+ if err := rules.Add(r, false); err != nil {
+ log.Error("Error while adding rule: %s", err)
+ } else {
+ ok = true
+ }
+ }
+
+ if ok {
+ log.Important("%s new rule: %s if %s", pers, action, r.Operator.String())
+ }
+
+ }
+ if packet == nil {
+ log.Debug("Packet nil after processing rules")
+ return r
+ }
+
+ if r.Enabled == false {
+ applyDefaultAction(packet)
+ ruleName := log.Green(r.Name)
+ log.Info("DISABLED (%s) %s %s -> %s:%d (%s)", uiClient.DefaultAction(), log.Bold(log.Green("✔")), log.Bold(con.Process.Path), log.Bold(con.To()), con.DstPort, ruleName)
+
+ } else if r.Action == rule.Allow {
+ packet.SetVerdictAndMark(netfilter.NF_ACCEPT, packet.Mark)
+ ruleName := log.Green(r.Name)
+ if r.Operator.Operand == rule.OpTrue {
+ ruleName = log.Dim(r.Name)
+ }
+ log.Debug("%s %s -> %s:%d (%s)", log.Bold(log.Green("✔")), log.Bold(con.Process.Path), log.Bold(con.To()), con.DstPort, ruleName)
+ } else {
+ if r.Action == rule.Reject {
+ netlink.KillSocket(con.Protocol, con.SrcIP, con.SrcPort, con.DstIP, con.DstPort)
+ }
+ packet.SetVerdict(netfilter.NF_DROP)
+
+ log.Debug("%s %s -> %s:%d (%s)", log.Bold(log.Red("✘")), log.Bold(con.Process.Path), log.Bold(con.To()), con.DstPort, log.Red(r.Name))
+ }
+
+ return r
+}
+
+func main() {
+ ctx, cancel = context.WithCancel(context.Background())
+ defer cancel()
+ flag.Parse()
+
+ if showVersion {
+ fmt.Println(core.Version)
+ os.Exit(0)
+ }
+
+ setupLogging()
+
+ if cpuProfile != "" {
+ if f, err := os.Create(cpuProfile); err != nil {
+ log.Fatal("%s", err)
+ } else if err := pprof.StartCPUProfile(f); err != nil {
+ log.Fatal("%s", err)
+ }
+ }
+
+ log.Important("Starting %s v%s", core.Name, core.Version)
+
+ rulesPath, err := core.ExpandPath(rulesPath)
+ if err != nil {
+ log.Fatal("%s", err)
+ }
+
+ setupSignals()
+
+ log.Info("Loading rules from %s ...", rulesPath)
+ if rules, err = rule.NewLoader(!noLiveReload); err != nil {
+ log.Fatal("%s", err)
+ } else if err = rules.Load(rulesPath); err != nil {
+ log.Fatal("%s", err)
+ }
+ stats = statistics.New(rules)
+
+ // prepare the queue
+ setupWorkers()
+ queue, err := netfilter.NewQueue(uint16(queueNum))
+ if err != nil {
+ log.Warning("Is opensnitchd already running?")
+ log.Fatal("Error while creating queue #%d: %s", queueNum, err)
+ }
+ pktChan = queue.Packets()
+
+ repeatQueueNum = queueNum + 1
+ repeatQueue, rqerr := netfilter.NewQueue(uint16(repeatQueueNum))
+ if rqerr != nil {
+ log.Warning("Is opensnitchd already running?")
+ log.Fatal("Error while creating queue #%d: %s", repeatQueueNum, rqerr)
+ }
+ repeatPktChan = repeatQueue.Packets()
+
+ uiClient = ui.NewClient(uiSocket, stats, rules)
+ stats.SetConfig(uiClient.GetStatsConfig())
+
+ // queue is ready, run firewall rules
+ firewall.Init(uiClient.GetFirewallType(), &queueNum)
+
+ if overwriteLogging() {
+ setupLogging()
+ }
+ // overwrite monitor method from configuration if the user has passed
+ // the option via command line.
+ if procmonMethod != "" {
+ if err := monitor.ReconfigureMonitorMethod(procmonMethod); err != nil {
+ log.Warning("Unable to set process monitor method via parameter: %v", err)
+ }
+ }
+
+ log.Info("Running on netfilter queue #%d ...", queueNum)
+ for {
+ select {
+ case <-ctx.Done():
+ goto Exit
+ case pkt, ok := <-pktChan:
+ if !ok {
+ goto Exit
+ }
+ wrkChan <- pkt
+ }
+ }
+Exit:
+ close(wrkChan)
+ doCleanup(queue, repeatQueue)
+ os.Exit(0)
+}
--- /dev/null
+package netfilter
+
+import "C"
+
+import (
+ "github.com/google/gopacket"
+)
+
+// packet consts
+const (
+ IPv4 = 4
+)
+
+// Verdict holds the action to perform on a packet (NF_DROP, NF_ACCEPT, etc)
+type Verdict C.uint
+
+type VerdictContainer struct {
+ Verdict Verdict
+ Mark uint32
+ Packet []byte
+}
+
+// Packet holds the data of a network packet
+type Packet struct {
+ Packet gopacket.Packet
+ Mark uint32
+ verdictChannel chan VerdictContainer
+ UID uint32
+ NetworkProtocol uint8
+}
+
+// SetVerdict emits a veredict on a packet
+func (p *Packet) SetVerdict(v Verdict) {
+ p.verdictChannel <- VerdictContainer{Verdict: v, Packet: nil, Mark: 0}
+}
+
+// SetVerdictAndMark emits a veredict on a packet and marks it in order to not
+// analyze it again.
+func (p *Packet) SetVerdictAndMark(v Verdict, mark uint32) {
+ p.verdictChannel <- VerdictContainer{Verdict: v, Packet: nil, Mark: mark}
+}
+
+func (p *Packet) SetRequeueVerdict(newQueueId uint16) {
+ v := uint(NF_QUEUE)
+ q := (uint(newQueueId) << 16)
+ v = v | q
+ p.verdictChannel <- VerdictContainer{Verdict: Verdict(v), Packet: nil, Mark: 0}
+}
+
+func (p *Packet) SetVerdictWithPacket(v Verdict, packet []byte) {
+ p.verdictChannel <- VerdictContainer{Verdict: v, Packet: packet, Mark: 0}
+}
+
+// IsIPv4 returns if the packet is IPv4
+func (p *Packet) IsIPv4() bool {
+ return p.NetworkProtocol == IPv4
+}
--- /dev/null
+#include "queue.h"
+
--- /dev/null
+package netfilter
+
+/*
+#cgo pkg-config: libnetfilter_queue
+#cgo CFLAGS: -Wall -I/usr/include
+#cgo LDFLAGS: -L/usr/lib64/ -ldl
+
+#include "queue.h"
+*/
+import "C"
+
+import (
+ "fmt"
+ "os"
+ "sync"
+ "time"
+ "unsafe"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+)
+
+const (
+ AF_INET = 2
+ AF_INET6 = 10
+
+ NF_DROP Verdict = 0
+ NF_ACCEPT Verdict = 1
+ NF_STOLEN Verdict = 2
+ NF_QUEUE Verdict = 3
+ NF_REPEAT Verdict = 4
+ NF_STOP Verdict = 5
+
+ NF_DEFAULT_QUEUE_SIZE uint32 = 4096
+ NF_DEFAULT_PACKET_SIZE uint32 = 4096
+)
+
+var (
+ queueIndex = make(map[uint32]*chan Packet, 0)
+ queueIndexLock = sync.RWMutex{}
+ exitChan = make(chan bool, 1)
+
+ gopacketDecodeOptions = gopacket.DecodeOptions{Lazy: true, NoCopy: true}
+)
+
+// VerdictContainerC is the struct that contains the mark, action, length and
+// payload of a packet.
+// It's defined in queue.h, and filled on go_callback()
+type VerdictContainerC C.verdictContainer
+
+// Queue holds the information of a netfilter queue.
+// The handles of the connection to the kernel and the created queue.
+// A channel where the intercepted packets will be received.
+// The ID of the queue.
+type Queue struct {
+ h *C.struct_nfq_handle
+ qh *C.struct_nfq_q_handle
+ fd C.int
+ packets chan Packet
+ idx uint32
+}
+
+// NewQueue opens a new netfilter queue to receive packets marked with a mark.
+func NewQueue(queueID uint16) (q *Queue, err error) {
+ q = &Queue{
+ idx: uint32(time.Now().UnixNano()),
+ packets: make(chan Packet),
+ }
+
+ if err = q.create(queueID); err != nil {
+ return nil, err
+ } else if err = q.setup(); err != nil {
+ return nil, err
+ }
+
+ go q.run(exitChan)
+
+ return q, nil
+}
+
+func (q *Queue) create(queueID uint16) (err error) {
+ var ret C.int
+
+ if q.h, err = C.nfq_open(); err != nil {
+ return fmt.Errorf("Error opening Queue handle: %v", err)
+ } else if ret, err = C.nfq_unbind_pf(q.h, AF_INET); err != nil || ret < 0 {
+ return fmt.Errorf("Error unbinding existing q handler from AF_INET protocol family: %v", err)
+ } else if ret, err = C.nfq_unbind_pf(q.h, AF_INET6); err != nil || ret < 0 {
+ return fmt.Errorf("Error unbinding existing q handler from AF_INET6 protocol family: %v", err)
+ } else if ret, err := C.nfq_bind_pf(q.h, AF_INET); err != nil || ret < 0 {
+ return fmt.Errorf("Error binding to AF_INET protocol family: %v", err)
+ } else if ret, err := C.nfq_bind_pf(q.h, AF_INET6); err != nil || ret < 0 {
+ return fmt.Errorf("Error binding to AF_INET6 protocol family: %v", err)
+ } else if q.qh, err = C.CreateQueue(q.h, C.u_int16_t(queueID), C.u_int32_t(q.idx)); err != nil || q.qh == nil {
+ q.destroy()
+ return fmt.Errorf("Error binding to queue: %v", err)
+ }
+
+ queueIndexLock.Lock()
+ queueIndex[q.idx] = &q.packets
+ queueIndexLock.Unlock()
+
+ return nil
+}
+
+func (q *Queue) setup() (err error) {
+ var ret C.int
+
+ queueSize := C.u_int32_t(NF_DEFAULT_QUEUE_SIZE)
+ bufferSize := C.uint(NF_DEFAULT_PACKET_SIZE)
+ totSize := C.uint(NF_DEFAULT_QUEUE_SIZE * NF_DEFAULT_PACKET_SIZE)
+
+ if ret, err = C.nfq_set_queue_maxlen(q.qh, queueSize); err != nil || ret < 0 {
+ q.destroy()
+ return fmt.Errorf("Unable to set max packets in queue: %v", err)
+ } else if C.nfq_set_mode(q.qh, C.u_int8_t(2), bufferSize) < 0 {
+ q.destroy()
+ return fmt.Errorf("Unable to set packets copy mode: %v", err)
+ } else if q.fd, err = C.nfq_fd(q.h); err != nil {
+ q.destroy()
+ return fmt.Errorf("Unable to get queue file-descriptor. %v", err)
+ } else if C.nfnl_rcvbufsiz(C.nfq_nfnlh(q.h), totSize) < 0 {
+ q.destroy()
+ return fmt.Errorf("Unable to increase netfilter buffer space size")
+ }
+
+ return nil
+}
+
+func (q *Queue) run(exitCh chan<- bool) {
+ if errno := C.Run(q.h, q.fd); errno != 0 {
+ fmt.Fprintf(os.Stderr, "Terminating, unable to receive packet due to errno=%d", errno)
+ }
+ exitChan <- true
+}
+
+// Close ensures that nfqueue resources are freed and closed.
+// C.stop_reading_packets() stops the reading packets loop, which causes
+// go-subroutine run() to exit.
+// After exit, listening queue is destroyed and closed.
+// If for some reason any of the steps stucks while closing it, we'll exit by timeout.
+func (q *Queue) Close() {
+ close(q.packets)
+ C.stop_reading_packets()
+ q.destroy()
+ queueIndexLock.Lock()
+ delete(queueIndex, q.idx)
+ queueIndexLock.Unlock()
+}
+
+func (q *Queue) destroy() {
+ // we'll try to exit cleanly, but sometimes nfqueue gets stuck
+ time.AfterFunc(5*time.Second, func() {
+ log.Warning("queue stuck, closing by timeout")
+ if q != nil {
+ C.close(q.fd)
+ q.closeNfq()
+ }
+ os.Exit(0)
+ })
+ C.nfq_unbind_pf(q.h, AF_INET)
+ C.nfq_unbind_pf(q.h, AF_INET6)
+ if q.qh != nil {
+ if ret := C.nfq_destroy_queue(q.qh); ret != 0 {
+ log.Warning("Queue.destroy(), nfq_destroy_queue() not closed: %d", ret)
+ }
+ }
+
+ q.closeNfq()
+}
+
+func (q *Queue) closeNfq() {
+ if q.h != nil {
+ if ret := C.nfq_close(q.h); ret != 0 {
+ log.Warning("Queue.destroy(), nfq_close() not closed: %d", ret)
+ }
+ }
+}
+
+// Packets return the list of enqueued packets.
+func (q *Queue) Packets() <-chan Packet {
+ return q.packets
+}
+
+// FYI: the export keyword is mandatory to specify that go_callback is defined elsewhere
+
+//export go_callback
+func go_callback(queueID C.int, data *C.uchar, length C.int, mark C.uint, idx uint32, vc *VerdictContainerC, uid uint32) {
+ (*vc).verdict = C.uint(NF_ACCEPT)
+ (*vc).data = nil
+ (*vc).mark_set = 0
+ (*vc).length = 0
+
+ queueIndexLock.RLock()
+ queueChannel, found := queueIndex[idx]
+ queueIndexLock.RUnlock()
+ if !found {
+ fmt.Fprintf(os.Stderr, "Unexpected queue idx %d\n", idx)
+ return
+ }
+
+ xdata := C.GoBytes(unsafe.Pointer(data), length)
+
+ p := Packet{
+ verdictChannel: make(chan VerdictContainer),
+ Mark: uint32(mark),
+ UID: uid,
+ NetworkProtocol: xdata[0] >> 4, // first 4 bits is the version
+ }
+
+ var packet gopacket.Packet
+ if p.IsIPv4() {
+ packet = gopacket.NewPacket(xdata, layers.LayerTypeIPv4, gopacketDecodeOptions)
+ } else {
+ packet = gopacket.NewPacket(xdata, layers.LayerTypeIPv6, gopacketDecodeOptions)
+ }
+
+ p.Packet = packet
+
+ select {
+ case *queueChannel <- p:
+ select {
+ case v := <-p.verdictChannel:
+ if v.Packet == nil {
+ (*vc).verdict = C.uint(v.Verdict)
+ } else {
+ (*vc).verdict = C.uint(v.Verdict)
+ (*vc).data = (*C.uchar)(unsafe.Pointer(&v.Packet[0]))
+ (*vc).length = C.uint(len(v.Packet))
+ }
+
+ if v.Mark != 0 {
+ (*vc).mark_set = C.uint(1)
+ (*vc).mark = C.uint(v.Mark)
+ }
+ }
+
+ case <-time.After(1 * time.Millisecond):
+ fmt.Fprintf(os.Stderr, "Timed out while sending packet to queue channel %d\n", idx)
+ }
+}
--- /dev/null
+#ifndef _NETFILTER_QUEUE_H
+#define _NETFILTER_QUEUE_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <math.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <netinet/in.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/netfilter.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+
+typedef struct {
+ uint verdict;
+ uint mark;
+ uint mark_set;
+ uint length;
+ unsigned char *data;
+} verdictContainer;
+
+static void *get_uid = NULL;
+
+extern void go_callback(int id, unsigned char* data, int len, uint mark, u_int32_t idx, verdictContainer *vc, uint32_t uid);
+
+static uint8_t stop = 0;
+
+static inline void configure_uid_if_available(struct nfq_q_handle *qh){
+ void *hndl = dlopen("libnetfilter_queue.so.1", RTLD_LAZY);
+ if (!hndl) {
+ hndl = dlopen("libnetfilter_queue.so", RTLD_LAZY);
+ if (!hndl){
+ printf("WARNING: libnetfilter_queue not available\n");
+ return;
+ }
+ }
+ if ((get_uid = dlsym(hndl, "nfq_get_uid")) == NULL){
+ printf("WARNING: nfq_get_uid not available\n");
+ return;
+ }
+ printf("OK: libnetfiler_queue supports nfq_get_uid\n");
+#ifdef NFQA_CFG_F_UID_GID
+ if (qh != NULL && nfq_set_queue_flags(qh, NFQA_CFG_F_UID_GID, NFQA_CFG_F_UID_GID)){
+ printf("WARNING: UID not available on this kernel/libnetfilter_queue\n");
+ }
+#endif
+}
+
+static int nf_callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *arg){
+ if (stop) {
+ return -1;
+ }
+
+ uint32_t id = -1, idx = 0, mark = 0;
+ struct nfqnl_msg_packet_hdr *ph = NULL;
+ unsigned char *buffer = NULL;
+ int size = 0;
+ verdictContainer vc = {0};
+ uint32_t uid = 0xffffffff;
+
+ mark = nfq_get_nfmark(nfa);
+ ph = nfq_get_msg_packet_hdr(nfa);
+ id = ntohl(ph->packet_id);
+ size = nfq_get_payload(nfa, &buffer);
+ idx = (uint32_t)((uintptr_t)arg);
+
+#ifdef NFQA_CFG_F_UID_GID
+ if (get_uid)
+ nfq_get_uid(nfa, &uid);
+#endif
+
+ go_callback(id, buffer, size, mark, idx, &vc, uid);
+
+ if( vc.mark_set == 1 ) {
+ return nfq_set_verdict2(qh, id, vc.verdict, vc.mark, vc.length, vc.data);
+ }
+ return nfq_set_verdict2(qh, id, vc.verdict, vc.mark, vc.length, vc.data);
+}
+
+static inline struct nfq_q_handle* CreateQueue(struct nfq_handle *h, u_int16_t queue, u_int32_t idx) {
+ struct nfq_q_handle* qh = nfq_create_queue(h, queue, &nf_callback, (void*)((uintptr_t)idx));
+ if (qh == NULL){
+ printf("ERROR: nfq_create_queue() queue not created\n");
+ } else {
+ configure_uid_if_available(qh);
+ }
+ return qh;
+}
+
+static inline void stop_reading_packets() {
+ stop = 1;
+}
+
+static inline int Run(struct nfq_handle *h, int fd) {
+ char buf[4096] __attribute__ ((aligned));
+ int rcvd, opt = 1;
+
+ setsockopt(fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeof(int));
+
+ while ((rcvd = recv(fd, buf, sizeof(buf), 0)) >= 0) {
+ if (stop == 1) {
+ return errno;
+ }
+ nfq_handle_packet(h, buf, rcvd);
+ }
+
+ return errno;
+}
+
+#endif
--- /dev/null
+package netlink
+
+import (
+ "fmt"
+ "net"
+ "strconv"
+ "syscall"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// GetSocketInfo asks the kernel via netlink for a given connection.
+// If the connection is found, we return the uid and the possible
+// associated inodes.
+// If the outgoing connection is not found but there're entries with the source
+// port and same protocol, add all the inodes to the list.
+//
+// Some examples:
+// outgoing connection as seen by netfilter || connection details dumped from kernel
+//
+// 47344:192.168.1.106 -> 151.101.65.140:443 || in kernel: 47344:192.168.1.106 -> 151.101.65.140:443
+// 8612:192.168.1.5 -> 192.168.1.255:8612 || in kernel: 8612:192.168.1.105 -> 0.0.0.0:0
+// 123:192.168.1.5 -> 217.144.138.234:123 || in kernel: 123:0.0.0.0 -> 0.0.0.0:0
+// 45015:127.0.0.1 -> 239.255.255.250:1900 || in kernel: 45015:127.0.0.1 -> 0.0.0.0:0
+// 50416:fe80::9fc2:ddcf:df22:aa50 -> fe80::1:53 || in kernel: 50416:254.128.0.0 -> 254.128.0.0:53
+// 51413:192.168.1.106 -> 103.224.182.250:1337 || in kernel: 51413:0.0.0.0 -> 0.0.0.0:0
+func GetSocketInfo(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) (uid int, inodes []int) {
+ uid = -1
+ family := uint8(syscall.AF_INET)
+ ipproto := uint8(syscall.IPPROTO_TCP)
+ protoLen := len(proto)
+ if proto[protoLen-1:protoLen] == "6" {
+ family = syscall.AF_INET6
+ }
+
+ if proto[:3] == "udp" {
+ ipproto = syscall.IPPROTO_UDP
+ if protoLen >= 7 && proto[:7] == "udplite" {
+ ipproto = syscall.IPPROTO_UDPLITE
+ }
+ }
+ if sockList, err := SocketGet(family, ipproto, uint16(srcPort), uint16(dstPort), srcIP, dstIP); err == nil {
+ for n, sock := range sockList {
+ if sock.UID != 0xffffffff {
+ uid = int(sock.UID)
+ }
+ log.Debug("[%d/%d] outgoing connection uid: %d, %d:%v -> %v:%d || netlink response: %d:%v -> %v:%d inode: %d - loopback: %v multicast: %v unspecified: %v linklocalunicast: %v ifaceLocalMulticast: %v GlobalUni: %v ",
+ n, len(sockList),
+ int(sock.UID),
+ srcPort, srcIP, dstIP, dstPort,
+ sock.ID.SourcePort, sock.ID.Source,
+ sock.ID.Destination, sock.ID.DestinationPort, sock.INode,
+ sock.ID.Destination.IsLoopback(),
+ sock.ID.Destination.IsMulticast(),
+ sock.ID.Destination.IsUnspecified(),
+ sock.ID.Destination.IsLinkLocalUnicast(),
+ sock.ID.Destination.IsLinkLocalMulticast(),
+ sock.ID.Destination.IsGlobalUnicast(),
+ )
+
+ if sock.ID.SourcePort == uint16(srcPort) && sock.ID.Source.Equal(srcIP) &&
+ (sock.ID.DestinationPort == uint16(dstPort)) &&
+ ((sock.ID.Destination.IsGlobalUnicast() || sock.ID.Destination.IsLoopback()) && sock.ID.Destination.Equal(dstIP)) {
+ inodes = append([]int{int(sock.INode)}, inodes...)
+ continue
+ }
+ log.Debug("GetSocketInfo() invalid: %d:%v -> %v:%d", sock.ID.SourcePort, sock.ID.Source, sock.ID.Destination, sock.ID.DestinationPort)
+ }
+
+ // handle special cases (see function description): ntp queries (123), broadcasts, incomming connections.
+ if len(inodes) == 0 && len(sockList) > 0 {
+ for n, sock := range sockList {
+ if sockList[n].ID.Destination.Equal(net.IPv4zero) || sockList[n].ID.Destination.Equal(net.IPv6zero) {
+ inodes = append([]int{int(sock.INode)}, inodes...)
+ log.Debug("netlink socket not found, adding entry: %d:%v -> %v:%d || %d:%v -> %v:%d inode: %d state: %s",
+ srcPort, srcIP, dstIP, dstPort,
+ sockList[n].ID.SourcePort, sockList[n].ID.Source,
+ sockList[n].ID.Destination, sockList[n].ID.DestinationPort,
+ sockList[n].INode, TCPStatesMap[sock.State])
+ } else if sock.ID.SourcePort == uint16(srcPort) && sock.ID.Source.Equal(srcIP) &&
+ (sock.ID.DestinationPort == uint16(dstPort)) {
+ inodes = append([]int{int(sock.INode)}, inodes...)
+ continue
+ } else {
+ log.Debug("netlink socket not found, EXCLUDING entry: %d:%v -> %v:%d || %d:%v -> %v:%d inode: %d state: %s",
+ srcPort, srcIP, dstIP, dstPort,
+ sockList[n].ID.SourcePort, sockList[n].ID.Source,
+ sockList[n].ID.Destination, sockList[n].ID.DestinationPort,
+ sockList[n].INode, TCPStatesMap[sock.State])
+ }
+ }
+ }
+ } else {
+ log.Debug("netlink socket error: %v - %d:%v -> %v:%d", err, srcPort, srcIP, dstIP, dstPort)
+ }
+
+ return uid, inodes
+}
+
+// GetSocketInfoByInode dumps the kernel sockets table and searches the given
+// inode on it.
+func GetSocketInfoByInode(inodeStr string) (*Socket, error) {
+ inode, err := strconv.ParseUint(inodeStr, 10, 32)
+ if err != nil {
+ return nil, err
+ }
+
+ type inetStruct struct{ family, proto uint8 }
+ socketTypes := []inetStruct{
+ {syscall.AF_INET, syscall.IPPROTO_TCP},
+ {syscall.AF_INET, syscall.IPPROTO_UDP},
+ {syscall.AF_INET6, syscall.IPPROTO_TCP},
+ {syscall.AF_INET6, syscall.IPPROTO_UDP},
+ }
+
+ for _, socket := range socketTypes {
+ socketList, err := SocketsDump(socket.family, socket.proto)
+ if err != nil {
+ return nil, err
+ }
+ for idx := range socketList {
+ if uint32(inode) == socketList[idx].INode {
+ return socketList[idx], nil
+ }
+ }
+ }
+ return nil, fmt.Errorf("Inode not found")
+}
+
+// KillSocket kills a socket given the properties of a connection.
+func KillSocket(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) {
+ family := uint8(syscall.AF_INET)
+ ipproto := uint8(syscall.IPPROTO_TCP)
+ protoLen := len(proto)
+ if proto[protoLen-1:protoLen] == "6" {
+ family = syscall.AF_INET6
+ }
+
+ if proto[:3] == "udp" {
+ ipproto = syscall.IPPROTO_UDP
+ if protoLen >= 7 && proto[:7] == "udplite" {
+ ipproto = syscall.IPPROTO_UDPLITE
+ }
+ }
+
+ if sockList, err := SocketGet(family, ipproto, uint16(srcPort), uint16(dstPort), srcIP, dstIP); err == nil {
+ for _, s := range sockList {
+ if err := socketKill(family, ipproto, s.ID); err != nil {
+ log.Debug("Unable to kill socket: %d, %d, %v", srcPort, dstPort, err)
+ }
+ }
+ }
+}
--- /dev/null
+package netlink
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "net"
+ "syscall"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/vishvananda/netlink/nl"
+)
+
+// This is a modification of https://github.com/vishvananda/netlink socket_linux.go - Apache2.0 license
+// which adds support for query UDP, UDPLITE and IPv6 sockets to SocketGet()
+
+const (
+ SOCK_DESTROY = 21
+ sizeofSocketID = 0x30
+ sizeofSocketRequest = sizeofSocketID + 0x8
+ sizeofSocket = sizeofSocketID + 0x18
+)
+
+var (
+ native = nl.NativeEndian()
+ networkOrder = binary.BigEndian
+ TCP_ALL = uint32(0xfff)
+)
+
+// https://elixir.bootlin.com/linux/latest/source/include/net/tcp_states.h
+const (
+ TCP_INVALID = iota
+ TCP_ESTABLISHED
+ TCP_SYN_SENT
+ TCP_SYN_RECV
+ TCP_FIN_WAIT1
+ TCP_FIN_WAIT2
+ TCP_TIME_WAIT
+ TCP_CLOSE
+ TCP_CLOSE_WAIT
+ TCP_LAST_ACK
+ TCP_LISTEN
+ TCP_CLOSING
+ TCP_NEW_SYN_REC
+ TCP_MAX_STATES
+)
+
+// TCPStatesMap holds the list of TCP states
+var TCPStatesMap = map[uint8]string{
+ TCP_INVALID: "invalid",
+ TCP_ESTABLISHED: "established",
+ TCP_SYN_SENT: "syn_sent",
+ TCP_SYN_RECV: "syn_recv",
+ TCP_FIN_WAIT1: "fin_wait1",
+ TCP_FIN_WAIT2: "fin_wait2",
+ TCP_TIME_WAIT: "time_wait",
+ TCP_CLOSE: "close",
+ TCP_CLOSE_WAIT: "close_wait",
+ TCP_LAST_ACK: "last_ack",
+ TCP_LISTEN: "listen",
+ TCP_CLOSING: "closing",
+}
+
+// SocketID holds the socket information of a request/response to the kernel
+type SocketID struct {
+ SourcePort uint16
+ DestinationPort uint16
+ Source net.IP
+ Destination net.IP
+ Interface uint32
+ Cookie [2]uint32
+}
+
+// Socket represents a netlink socket.
+type Socket struct {
+ Family uint8
+ State uint8
+ Timer uint8
+ Retrans uint8
+ ID SocketID
+ Expires uint32
+ RQueue uint32
+ WQueue uint32
+ UID uint32
+ INode uint32
+}
+
+// SocketRequest holds the request/response of a connection to the kernel
+type SocketRequest struct {
+ Family uint8
+ Protocol uint8
+ Ext uint8
+ pad uint8
+ States uint32
+ ID SocketID
+}
+
+type writeBuffer struct {
+ Bytes []byte
+ pos int
+}
+
+func (b *writeBuffer) Write(c byte) {
+ b.Bytes[b.pos] = c
+ b.pos++
+}
+
+func (b *writeBuffer) Next(n int) []byte {
+ s := b.Bytes[b.pos : b.pos+n]
+ b.pos += n
+ return s
+}
+
+// Serialize convert SocketRequest struct to bytes.
+func (r *SocketRequest) Serialize() []byte {
+ b := writeBuffer{Bytes: make([]byte, sizeofSocketRequest)}
+ b.Write(r.Family)
+ b.Write(r.Protocol)
+ b.Write(r.Ext)
+ b.Write(r.pad)
+ native.PutUint32(b.Next(4), r.States)
+ networkOrder.PutUint16(b.Next(2), r.ID.SourcePort)
+ networkOrder.PutUint16(b.Next(2), r.ID.DestinationPort)
+ if r.Family == syscall.AF_INET6 {
+ copy(b.Next(16), r.ID.Source)
+ copy(b.Next(16), r.ID.Destination)
+ } else {
+ copy(b.Next(4), r.ID.Source.To4())
+ b.Next(12)
+ copy(b.Next(4), r.ID.Destination.To4())
+ b.Next(12)
+ }
+ native.PutUint32(b.Next(4), r.ID.Interface)
+ native.PutUint32(b.Next(4), r.ID.Cookie[0])
+ native.PutUint32(b.Next(4), r.ID.Cookie[1])
+ return b.Bytes
+}
+
+// Len returns the size of a socket request
+func (r *SocketRequest) Len() int { return sizeofSocketRequest }
+
+type readBuffer struct {
+ Bytes []byte
+ pos int
+}
+
+func (b *readBuffer) Read() byte {
+ c := b.Bytes[b.pos]
+ b.pos++
+ return c
+}
+
+func (b *readBuffer) Next(n int) []byte {
+ s := b.Bytes[b.pos : b.pos+n]
+ b.pos += n
+ return s
+}
+
+func (s *Socket) deserialize(b []byte) error {
+ if len(b) < sizeofSocket {
+ return fmt.Errorf("socket data short read (%d); want %d", len(b), sizeofSocket)
+ }
+ rb := readBuffer{Bytes: b}
+ s.Family = rb.Read()
+ s.State = rb.Read()
+ s.Timer = rb.Read()
+ s.Retrans = rb.Read()
+ s.ID.SourcePort = networkOrder.Uint16(rb.Next(2))
+ s.ID.DestinationPort = networkOrder.Uint16(rb.Next(2))
+ if s.Family == syscall.AF_INET6 {
+ s.ID.Source = net.IP(rb.Next(16))
+ s.ID.Destination = net.IP(rb.Next(16))
+ } else {
+ s.ID.Source = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read())
+ rb.Next(12)
+ s.ID.Destination = net.IPv4(rb.Read(), rb.Read(), rb.Read(), rb.Read())
+ rb.Next(12)
+ }
+ s.ID.Interface = native.Uint32(rb.Next(4))
+ s.ID.Cookie[0] = native.Uint32(rb.Next(4))
+ s.ID.Cookie[1] = native.Uint32(rb.Next(4))
+ s.Expires = native.Uint32(rb.Next(4))
+ s.RQueue = native.Uint32(rb.Next(4))
+ s.WQueue = native.Uint32(rb.Next(4))
+ s.UID = native.Uint32(rb.Next(4))
+ s.INode = native.Uint32(rb.Next(4))
+ return nil
+}
+
+// SocketKill kills a connection
+func socketKill(family, proto uint8, sockID SocketID) error {
+
+ sockReq := &SocketRequest{
+ Family: family,
+ Protocol: proto,
+ ID: sockID,
+ }
+
+ req := nl.NewNetlinkRequest(SOCK_DESTROY, syscall.NLM_F_REQUEST|syscall.NLM_F_ACK)
+ req.AddData(sockReq)
+ _, err := req.Execute(syscall.NETLINK_INET_DIAG, 0)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// SocketGet returns the list of active connections in the kernel
+// filtered by several fields. Currently it returns connections
+// filtered by source port and protocol.
+func SocketGet(family uint8, proto uint8, srcPort, dstPort uint16, local, remote net.IP) ([]*Socket, error) {
+ _Id := SocketID{
+ SourcePort: srcPort,
+ Cookie: [2]uint32{nl.TCPDIAG_NOCOOKIE, nl.TCPDIAG_NOCOOKIE},
+ }
+
+ sockReq := &SocketRequest{
+ Family: family,
+ Protocol: proto,
+ States: TCP_ALL,
+ ID: _Id,
+ }
+
+ return netlinkRequest(sockReq, family, proto, srcPort, dstPort, local, remote)
+}
+
+// SocketsDump returns the list of all connections from the kernel
+func SocketsDump(family uint8, proto uint8) ([]*Socket, error) {
+ sockReq := &SocketRequest{
+ Family: family,
+ Protocol: proto,
+ States: TCP_ALL,
+ }
+ return netlinkRequest(sockReq, 0, 0, 0, 0, nil, nil)
+}
+
+func netlinkRequest(sockReq *SocketRequest, family uint8, proto uint8, srcPort, dstPort uint16, local, remote net.IP) ([]*Socket, error) {
+ req := nl.NewNetlinkRequest(nl.SOCK_DIAG_BY_FAMILY, syscall.NLM_F_DUMP)
+ req.AddData(sockReq)
+ msgs, err := req.Execute(syscall.NETLINK_INET_DIAG, 0)
+ if err != nil {
+ return nil, err
+ }
+ if len(msgs) == 0 {
+ return nil, errors.New("Warning, no message nor error from netlink, or no connections found")
+ }
+ var sock []*Socket
+ for n, m := range msgs {
+ s := &Socket{}
+ if err = s.deserialize(m); err != nil {
+ log.Error("[%d] netlink socket error: %s, %d:%v -> %v:%d - %d:%v -> %v:%d",
+ n, TCPStatesMap[s.State],
+ srcPort, local, remote, dstPort,
+ s.ID.SourcePort, s.ID.Source, s.ID.Destination, s.ID.DestinationPort)
+ continue
+ }
+ if s.INode == 0 {
+ continue
+ }
+
+ sock = append([]*Socket{s}, sock...)
+ }
+ return sock, err
+}
--- /dev/null
+package netlink
+
+import (
+ "fmt"
+ "net"
+ "os"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+type Connection struct {
+ SrcIP net.IP
+ DstIP net.IP
+ Protocol string
+ SrcPort uint
+ DstPort uint
+ OutConn net.Conn
+ Listener net.Listener
+}
+
+func EstablishConnection(proto, dst string) (net.Conn, error) {
+ c, err := net.Dial(proto, dst)
+ if err != nil {
+ fmt.Println(err)
+ return nil, err
+ }
+ return c, nil
+}
+
+func ListenOnPort(proto, port string) (net.Listener, error) {
+ // TODO: UDP -> ListenUDP() or ListenPacket()
+ l, err := net.Listen(proto, port)
+ if err != nil {
+ fmt.Println(err)
+ return nil, err
+ }
+ return l, nil
+}
+
+func setupConnection(proto string, connChan chan *Connection) {
+ listnr, _ := ListenOnPort(proto, "127.0.0.1:55555")
+ conn, err := EstablishConnection(proto, "127.0.0.1:55555")
+ if err != nil {
+ connChan <- nil
+ return
+ }
+ laddr := strings.Split(conn.LocalAddr().String(), ":")
+ daddr := strings.Split(conn.RemoteAddr().String(), ":")
+ sport, _ := strconv.Atoi(laddr[1])
+ dport, _ := strconv.Atoi(daddr[1])
+
+ lconn := &Connection{
+ SrcPort: uint(sport),
+ DstPort: uint(dport),
+ SrcIP: net.ParseIP(laddr[0]),
+ DstIP: net.ParseIP(daddr[0]),
+ Protocol: "tcp",
+ Listener: listnr,
+ OutConn: conn,
+ }
+ connChan <- lconn
+}
+
+// TestNetlinkQueries tests queries to the kernel to get the inode of a connection.
+// When using ProcFS as monitor method, we need that value to get the PID of an application.
+// We also need it if for any reason auditd or ebpf doesn't return the PID of the application.
+// TODO: test all the cases described in the GetSocketInfo() description.
+func TestNetlinkTCPQueries(t *testing.T) {
+ // netlink tests disabled by default, they cause random failures on restricted
+ // environments.
+ if os.Getenv("NETLINK_TESTS") == "" {
+ t.Skip("Skipping netlink tests. Use NETLINK_TESTS=1 to launch these tests.")
+ }
+
+ connChan := make(chan *Connection)
+ go setupConnection("tcp", connChan)
+ conn := <-connChan
+ if conn == nil {
+ t.Error("TestParseTCPConnection, conn nil")
+ }
+
+ var inodes []int
+ uid := -1
+ t.Run("Test GetSocketInfo", func(t *testing.T) {
+ uid, inodes = GetSocketInfo("tcp", conn.SrcIP, conn.SrcPort, conn.DstIP, conn.DstPort)
+
+ if len(inodes) == 0 {
+ t.Error("inodes empty")
+ }
+ if uid != os.Getuid() {
+ t.Error("GetSocketInfo UID error:", uid, os.Getuid())
+ }
+ })
+
+ t.Run("Test GetSocketInfoByInode", func(t *testing.T) {
+ socket, err := GetSocketInfoByInode(fmt.Sprint(inodes[0]))
+ if err != nil {
+ t.Error("GetSocketInfoByInode error:", err)
+ }
+ if socket == nil {
+ t.Error("GetSocketInfoByInode inode not found")
+ }
+ if socket.ID.SourcePort != uint16(conn.SrcPort) {
+ t.Error("GetSocketInfoByInode dstPort error:", socket)
+ }
+ if socket.ID.DestinationPort != uint16(conn.DstPort) {
+ t.Error("GetSocketInfoByInode dstPort error:", socket)
+ }
+ if socket.UID != uint32(os.Getuid()) {
+ t.Error("GetSocketInfoByInode UID error:", socket, os.Getuid())
+ }
+ })
+
+ conn.Listener.Close()
+}
--- /dev/null
+package netstat
+
+import (
+ "net"
+)
+
+// Entry holds the information of a /proc/net/* entry.
+// For example, /proc/net/tcp:
+// sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
+// 0: 0100007F:13AD 00000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 18083222
+type Entry struct {
+ Proto string
+ SrcIP net.IP
+ SrcPort uint
+ DstIP net.IP
+ DstPort uint
+ UserId int
+ INode int
+}
+
+// NewEntry creates a new entry with values from /proc/net/
+func NewEntry(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint, userId int, iNode int) Entry {
+ return Entry{
+ Proto: proto,
+ SrcIP: srcIP,
+ SrcPort: srcPort,
+ DstIP: dstIP,
+ DstPort: dstPort,
+ UserId: userId,
+ INode: iNode,
+ }
+}
--- /dev/null
+package netstat
+
+import (
+ "net"
+ "strings"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// FindEntry looks for the connection in the list of known connections in ProcFS.
+func FindEntry(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) *Entry {
+ if entry := findEntryForProtocol(proto, srcIP, srcPort, dstIP, dstPort); entry != nil {
+ return entry
+ }
+
+ ipv6Suffix := "6"
+ if core.IPv6Enabled && strings.HasSuffix(proto, ipv6Suffix) == false {
+ otherProto := proto + ipv6Suffix
+ log.Debug("Searching for %s netstat entry instead of %s", otherProto, proto)
+ if entry := findEntryForProtocol(otherProto, srcIP, srcPort, dstIP, dstPort); entry != nil {
+ return entry
+ }
+ }
+
+ return &Entry{
+ Proto: proto,
+ SrcIP: srcIP,
+ SrcPort: srcPort,
+ DstIP: dstIP,
+ DstPort: dstPort,
+ UserId: -1,
+ INode: -1,
+ }
+}
+
+func findEntryForProtocol(proto string, srcIP net.IP, srcPort uint, dstIP net.IP, dstPort uint) *Entry {
+ entries, err := Parse(proto)
+ if err != nil {
+ log.Warning("Error while searching for %s netstat entry: %s", proto, err)
+ return nil
+ }
+
+ for _, entry := range entries {
+ if srcIP.Equal(entry.SrcIP) && srcPort == entry.SrcPort && dstIP.Equal(entry.DstIP) && dstPort == entry.DstPort {
+ return &entry
+ }
+ }
+
+ return nil
+}
--- /dev/null
+package netstat
+
+import (
+ "bufio"
+ "encoding/binary"
+ "fmt"
+ "net"
+ "os"
+ "regexp"
+ "strconv"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+var (
+ parser = regexp.MustCompile(`(?i)` +
+ `\d+:\s+` + // sl
+ `([a-f0-9]{8,32}):([a-f0-9]{4})\s+` + // local_address
+ `([a-f0-9]{8,32}):([a-f0-9]{4})\s+` + // rem_address
+ `[a-f0-9]{2}\s+` + // st
+ `[a-f0-9]{8}:[a-f0-9]{8}\s+` + // tx_queue rx_queue
+ `[a-f0-9]{2}:[a-f0-9]{8}\s+` + // tr tm->when
+ `[a-f0-9]{8}\s+` + // retrnsmt
+ `(\d+)\s+` + // uid
+ `\d+\s+` + // timeout
+ `(\d+)\s+` + // inode
+ `.+`) // stuff we don't care about
+)
+
+func decToInt(n string) int {
+ d, err := strconv.ParseInt(n, 10, 64)
+ if err != nil {
+ log.Fatal("Error while parsing %s to int: %s", n, err)
+ }
+ return int(d)
+}
+
+func hexToInt(h string) uint {
+ d, err := strconv.ParseUint(h, 16, 64)
+ if err != nil {
+ log.Fatal("Error while parsing %s to int: %s", h, err)
+ }
+ return uint(d)
+}
+
+func hexToInt2(h string) (uint, uint) {
+ if len(h) > 16 {
+ d, err := strconv.ParseUint(h[:16], 16, 64)
+ if err != nil {
+ log.Fatal("Error while parsing %s to int: %s", h[16:], err)
+ }
+ d2, err := strconv.ParseUint(h[16:], 16, 64)
+ if err != nil {
+ log.Fatal("Error while parsing %s to int: %s", h[16:], err)
+ }
+ return uint(d), uint(d2)
+ }
+
+ d, err := strconv.ParseUint(h, 16, 64)
+ if err != nil {
+ log.Fatal("Error while parsing %s to int: %s", h[16:], err)
+ }
+ return uint(d), 0
+}
+
+func hexToIP(h string) net.IP {
+ n, m := hexToInt2(h)
+ var ip net.IP
+ if m != 0 {
+ ip = make(net.IP, 16)
+ // TODO: Check if this depends on machine endianness?
+ binary.LittleEndian.PutUint32(ip, uint32(n>>32))
+ binary.LittleEndian.PutUint32(ip[4:], uint32(n))
+ binary.LittleEndian.PutUint32(ip[8:], uint32(m>>32))
+ binary.LittleEndian.PutUint32(ip[12:], uint32(m))
+ } else {
+ ip = make(net.IP, 4)
+ binary.LittleEndian.PutUint32(ip, uint32(n))
+ }
+ return ip
+}
+
+// Parse scans and retrieves the opened connections, from /proc/net/ files
+func Parse(proto string) ([]Entry, error) {
+ filename := fmt.Sprintf("/proc/net/%s", proto)
+ fd, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer fd.Close()
+
+ entries := make([]Entry, 0)
+ scanner := bufio.NewScanner(fd)
+ for lineno := 0; scanner.Scan(); lineno++ {
+ // skip column names
+ if lineno == 0 {
+ continue
+ }
+
+ line := core.Trim(scanner.Text())
+ m := parser.FindStringSubmatch(line)
+ if m == nil {
+ log.Warning("Could not parse netstat line from %s: %s", filename, line)
+ continue
+ }
+
+ entries = append(entries, NewEntry(
+ proto,
+ hexToIP(m[1]),
+ hexToInt(m[2]),
+ hexToIP(m[3]),
+ hexToInt(m[4]),
+ decToInt(m[5]),
+ decToInt(m[6]),
+ ))
+ }
+
+ return entries, nil
+}
--- /dev/null
+Name: opensnitch
+Version: 1.5.8
+Release: 1%{?dist}
+Summary: OpenSnitch is a GNU/Linux application firewall
+
+License: GPLv3+
+URL: https://github.com/evilsocket/%{name}
+Source0: https://github.com/evilsocket/%{name}/releases/download/v%{version}/%{name}_%{version}.orig.tar.gz
+#BuildArch: x86_64
+
+#BuildRequires: godep
+Requires(post): info
+Requires(preun): info
+
+%description
+Whenever a program makes a connection, it'll prompt the user to allow or deny
+it.
+
+The user can decide if block the outgoing connection based on properties of
+the connection: by port, by uid, by dst ip, by program or a combination
+of them.
+
+These rules can last forever, until the app restart or just one time.
+
+The GUI allows the user to view live outgoing connections, as well as search
+by process, user, host or port.
+
+%prep
+rm -rf %{buildroot}
+
+%setup
+
+%build
+mkdir -p go/src/github.com/evilsocket
+ln -s $(pwd) go/src/github.com/evilsocket/opensnitch
+export GOPATH=$(pwd)/go
+cd go/src/github.com/evilsocket/opensnitch/
+make protocol
+cd go/src/github.com/evilsocket/opensnitch/daemon/
+go mod vendor
+go build -o opensnitchd .
+
+%install
+mkdir -p %{buildroot}/usr/bin/ %{buildroot}/usr/lib/systemd/system/ %{buildroot}/etc/opensnitchd/rules %{buildroot}/etc/logrotate.d
+sed -i 's/\/usr\/local/\/usr/' daemon/opensnitchd.service
+install -m 755 daemon/opensnitchd %{buildroot}/usr/bin/opensnitchd
+install -m 644 daemon/opensnitchd.service %{buildroot}/usr/lib/systemd/system/opensnitch.service
+install -m 644 debian/opensnitch.logrotate %{buildroot}/etc/logrotate.d/opensnitch
+
+B=""
+if [ -f /etc/opensnitchd/default-config.json ]; then
+ B="-b"
+fi
+install -m 644 -b $B daemon/default-config.json %{buildroot}/etc/opensnitchd/default-config.json
+
+B=""
+if [ -f /etc/opensnitchd/system-fw.json ]; then
+ B="-b"
+fi
+install -m 644 -b $B daemon/system-fw.json %{buildroot}/etc/opensnitchd/system-fw.json
+
+install -m 644 ebpf_prog/opensnitch.o %{buildroot}/etc/opensnitchd/opensnitch.o
+
+# upgrade, uninstall
+%preun
+systemctl stop opensnitch.service || true
+
+%post
+if [ $1 -eq 1 ]; then
+ systemctl enable opensnitch.service
+fi
+systemctl start opensnitch.service
+
+# uninstall,upgrade
+%postun
+if [ $1 -eq 0 ]; then
+ systemctl disable opensnitch.service
+fi
+if [ $1 -eq 0 -a -f /etc/logrotate.d/opensnitch ]; then
+ rm /etc/logrotate.d/opensnitch
+fi
+
+# postun is the last step after reinstalling
+if [ $1 -eq 1 ]; then
+ systemctl start opensnitch.service
+fi
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%{_bindir}/opensnitchd
+/usr/lib/systemd/system/opensnitch.service
+%{_sysconfdir}/opensnitchd/default-config.json
+%{_sysconfdir}/opensnitchd/system-fw.json
+%{_sysconfdir}/opensnitchd/opensnitch.o
+%{_sysconfdir}/logrotate.d/opensnitch
--- /dev/null
+[Unit]
+Description=OpenSnitch is a GNU/Linux port of the Little Snitch application firewall.
+Documentation=https://github.com/gustavo-iniguez-goya/opensnitch/wiki
+Wants=network.target
+After=network.target
+
+[Service]
+Type=simple
+PermissionsStartOnly=true
+ExecStartPre=/bin/mkdir -p /etc/opensnitchd/rules
+ExecStart=/usr/local/bin/opensnitchd -rules-path /etc/opensnitchd/rules
+Restart=always
+RestartSec=30
+
+[Install]
+WantedBy=multi-user.target
--- /dev/null
+package procmon
+
+import (
+ "fmt"
+ "io/ioutil"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+type value struct {
+ Process *Process
+ //Starttime uniquely identifies a process, it is the 22nd value in /proc/<PID>/stat
+ //if another process starts with the same PID, it's Starttime will be unique
+ Starttime uint64
+}
+
+var (
+ activePids = make(map[uint64]value)
+ activePidsLock = sync.RWMutex{}
+)
+
+//MonitorActivePids checks that each process in activePids
+//is still running and if not running (or another process with the same pid is running),
+//removes the pid from activePids
+func MonitorActivePids() {
+ for {
+ time.Sleep(time.Second)
+ activePidsLock.Lock()
+ for k, v := range activePids {
+ data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", k))
+ if err != nil {
+ //file does not exists, pid has quit
+ delete(activePids, k)
+ pidsCache.delete(int(k))
+ continue
+ }
+ startTime, err := strconv.ParseInt(strings.Split(string(data), " ")[21], 10, 64)
+ if err != nil {
+ log.Error("Could not find or convert Starttime. This should never happen. Please report this incident to the Opensnitch developers: %v", err)
+ delete(activePids, k)
+ pidsCache.delete(int(k))
+ continue
+ }
+ if uint64(startTime) != v.Starttime {
+ //extremely unlikely: the original process has quit and another process
+ //was started with the same PID - all this in less than 1 second
+ log.Error("Same PID but different Starttime. Please report this incident to the Opensnitch developers.")
+ delete(activePids, k)
+ pidsCache.delete(int(k))
+ continue
+ }
+ }
+ activePidsLock.Unlock()
+ }
+}
+
+func findProcessInActivePidsCache(pid uint64) *Process {
+ activePidsLock.Lock()
+ defer activePidsLock.Unlock()
+ if value, ok := activePids[pid]; ok {
+ return value.Process
+ }
+ return nil
+}
+
+func addToActivePidsCache(pid uint64, proc *Process) {
+
+ data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", pid))
+ if err != nil {
+ //most likely the process has quit by now
+ return
+ }
+ startTime, err2 := strconv.ParseInt(strings.Split(string(data), " ")[21], 10, 64)
+ if err2 != nil {
+ log.Error("Could not find or convert Starttime. This should never happen. Please report this incident to the Opensnitch developers: %v", err)
+ return
+ }
+
+ activePidsLock.Lock()
+ activePids[pid] = value{
+ Process: proc,
+ Starttime: uint64(startTime),
+ }
+ activePidsLock.Unlock()
+}
--- /dev/null
+package procmon
+
+import (
+ "fmt"
+ "math/rand"
+ "os"
+ "os/exec"
+ "syscall"
+ "testing"
+ "time"
+)
+
+//TestMonitorActivePids starts helper processes, adds them to activePids
+//and then kills them and checks if monitorActivePids() removed the killed processes
+//from activePids
+func TestMonitorActivePids(t *testing.T) {
+
+ if os.Getenv("helperBinaryMode") == "on" {
+ //we are in the "helper binary" mode, we were started with helperCmd.Start() (see below)
+ //do nothing, just wait to be killed
+ time.Sleep(time.Second * 10)
+ os.Exit(1) //will never get here; but keep it here just in case
+ }
+
+ //we are in a normal "go test" mode
+ tmpDir := "/tmp/ostest_" + randString()
+ os.Mkdir(tmpDir, 0777)
+ fmt.Println("tmp dir", tmpDir)
+ defer os.RemoveAll(tmpDir)
+
+ go MonitorActivePids()
+
+ //build a "helper binary" with "go test -c -o /tmp/path" and put it into a tmp dir
+ helperBinaryPath := tmpDir + "/helper1"
+ goExecutable, _ := exec.LookPath("go")
+ cmd := exec.Command(goExecutable, "test", "-c", "-o", helperBinaryPath)
+ if err := cmd.Run(); err != nil {
+ t.Error("Error running go test -c", err)
+ }
+
+ var numberOfHelpers = 5
+ var helperProcs []*Process
+ //start helper binaries
+ for i := 0; i < numberOfHelpers; i++ {
+ var helperCmd *exec.Cmd
+ helperCmd = &exec.Cmd{
+ Path: helperBinaryPath,
+ Args: []string{helperBinaryPath},
+ Env: []string{"helperBinaryMode=on"},
+ }
+ if err := helperCmd.Start(); err != nil {
+ t.Error("Error starting helper binary", err)
+ }
+ go func() {
+ helperCmd.Wait() //must Wait(), otherwise the helper process becomes a zombie when kill()ed
+ }()
+
+ pid := helperCmd.Process.Pid
+ proc := NewProcess(pid, helperBinaryPath)
+ helperProcs = append(helperProcs, proc)
+ addToActivePidsCache(uint64(pid), proc)
+ }
+ //sleep to make sure all processes started before we proceed
+ time.Sleep(time.Second * 1)
+ //make sure all PIDS are in the cache
+ for i := 0; i < numberOfHelpers; i++ {
+ proc := helperProcs[i]
+ pid := proc.ID
+ foundProc := findProcessInActivePidsCache(uint64(pid))
+ if foundProc == nil {
+ t.Error("PID not found among active processes", pid)
+ }
+ if proc.Path != foundProc.Path || proc.ID != foundProc.ID {
+ t.Error("PID or path doesn't match with the found process")
+ }
+ }
+ //kill all helpers except for one
+ for i := 0; i < numberOfHelpers-1; i++ {
+ if err := syscall.Kill(helperProcs[i].ID, syscall.SIGTERM); err != nil {
+ t.Error("error in syscall.Kill", err)
+ }
+ }
+ //give the cache time to remove killed processes
+ time.Sleep(time.Second * 1)
+
+ //make sure only the alive process is in the cache
+ foundProc := findProcessInActivePidsCache(uint64(helperProcs[numberOfHelpers-1].ID))
+ if foundProc == nil {
+ t.Error("last alive PID is not found among active processes", foundProc)
+ }
+ if len(activePids) != 1 {
+ t.Error("more than 1 active PIDs left in cache")
+ }
+}
+
+func randString() string {
+ rand.Seed(time.Now().UnixNano())
+ var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ b := make([]rune, 10)
+ for i := range b {
+ b[i] = letterRunes[rand.Intn(len(letterRunes))]
+ }
+ return string(b)
+}
--- /dev/null
+// Package audit reads auditd events from the builtin af_unix plugin, and parses
+// the messages in order to proactively monitor pids which make connections.
+// Once a connection is made and redirected to us via NFQUEUE, we
+// lookup the connection inode in /proc, and add the corresponding PID with all
+// the information of the process to a list of known PIDs.
+//
+// TODO: Prompt the user to allow/deny a connection/program as soon as it's
+// started.
+//
+// Requisities:
+// - install auditd and audispd-plugins
+// - enable af_unix plugin /etc/audisp/plugins.d/af_unix.conf (active = yes)
+// - auditctl -a always,exit -F arch=b64 -S socket,connect,execve -k opensnitchd
+// - increase /etc/audisp/audispd.conf q_depth if there're dropped events
+// - set write_logs to no if you don't need/want audit logs to be stored in the disk.
+//
+// read messages from the pipe to verify that it's working:
+// socat unix-connect:/var/run/audispd_events stdio
+//
+// Audit event fields:
+// https://github.com/linux-audit/audit-documentation/blob/master/specs/fields/field-dictionary.csv
+// Record types:
+// https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Audit_Record_Types.html
+//
+// Documentation:
+// https://github.com/linux-audit/audit-documentation
+package audit
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "net"
+ "os"
+ "runtime"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// Event represents an audit event, which in our case can be an event of type
+// socket, execve, socketpair or connect.
+type Event struct {
+ Timestamp string // audit(xxxxxxx:nnnn)
+ Serial string
+ ProcName string // comm
+ ProcPath string // exe
+ ProcCmdLine string // proctitle
+ ProcDir string // cwd
+ ProcMode string // mode
+ TTY string
+ Pid int
+ UID int
+ Gid int
+ PPid int
+ EUid int
+ EGid int
+ OUid int
+ OGid int
+ UserName string // auid
+ DstHost net.IP
+ DstPort int
+ NetFamily string // inet, inet6, local
+ Success string
+ INode int
+ Dev string
+ Syscall int
+ Exit int
+ EventType string
+ RawEvent string
+ LastSeen time.Time
+}
+
+// MaxEventAge is the maximum minutes an audit process can live without network activity.
+const (
+ MaxEventAge = int(10)
+)
+
+var (
+ // Lock holds a mutex
+ Lock sync.RWMutex
+ ourPid = os.Getpid()
+ // cache of events
+ events []*Event
+ eventsCleaner *time.Ticker
+ eventsCleanerChan = (chan bool)(nil)
+ // TODO: EventChan is an output channel where incoming auditd events will be written.
+ // If a client opens it.
+ EventChan = (chan Event)(nil)
+ eventsExitChan = (chan bool)(nil)
+ auditConn net.Conn
+ // TODO: we may need arm arch
+ rule64 = []string{"exit,always", "-F", "arch=b64", "-F", fmt.Sprint("ppid!=", ourPid), "-F", fmt.Sprint("pid!=", ourPid), "-S", "socket,connect", "-k", "opensnitch"}
+ rule32 = []string{"exit,always", "-F", "arch=b32", "-F", fmt.Sprint("ppid!=", ourPid), "-F", fmt.Sprint("pid!=", ourPid), "-S", "socketcall", "-F", "a0=1", "-k", "opensnitch"}
+ audispdPath = "/var/run/audispd_events"
+)
+
+// OPENSNITCH_RULES_KEY is the mark we place on every event we are interested in.
+const (
+ OpensnitchRulesKey = "key=\"opensnitch\""
+)
+
+// GetEvents returns the list of processes which have opened a connection.
+func GetEvents() []*Event {
+ return events
+}
+
+// GetEventByPid returns an event given a pid.
+func GetEventByPid(pid int) *Event {
+ Lock.RLock()
+ defer Lock.RUnlock()
+
+ for _, event := range events {
+ if pid == event.Pid {
+ return event
+ }
+ }
+
+ return nil
+}
+
+// sortEvents sorts received events by time and elapsed time since latest network activity.
+// newest PIDs will be placed on top of the list.
+func sortEvents() {
+ sort.Slice(events, func(i, j int) bool {
+ now := time.Now()
+ elapsedTimeT := now.Sub(events[i].LastSeen)
+ elapsedTimeU := now.Sub(events[j].LastSeen)
+ t := events[i].LastSeen.UnixNano()
+ u := events[j].LastSeen.UnixNano()
+ return t > u && elapsedTimeT < elapsedTimeU
+ })
+}
+
+// cleanOldEvents deletes the PIDs which do not exist or that are too old to
+// live.
+// We start searching from the oldest to the newest.
+// If the last network activity of a PID has been greater than MaxEventAge,
+// then it'll be deleted.
+func cleanOldEvents() {
+ Lock.Lock()
+ defer Lock.Unlock()
+
+ for n := len(events) - 1; n >= 0; n-- {
+ now := time.Now()
+ elapsedTime := now.Sub(events[n].LastSeen)
+ if int(elapsedTime.Minutes()) >= MaxEventAge {
+ events = append(events[:n], events[n+1:]...)
+ continue
+ }
+ if core.Exists(fmt.Sprint("/proc/", events[n].Pid)) == false {
+ events = append(events[:n], events[n+1:]...)
+ }
+ }
+}
+
+func deleteEvent(pid int) {
+ for n := range events {
+ if events[n].Pid == pid || events[n].PPid == pid {
+ deleteEventByIndex(n)
+ break
+ }
+ }
+}
+
+func deleteEventByIndex(index int) {
+ Lock.Lock()
+ events = append(events[:index], events[index+1:]...)
+ Lock.Unlock()
+}
+
+// AddEvent adds new event to the list of PIDs which have generate network
+// activity.
+// If the PID is already in the list, the LastSeen field is updated, to keep
+// it alive.
+func AddEvent(aevent *Event) {
+ if aevent == nil {
+ return
+ }
+ Lock.Lock()
+ defer Lock.Unlock()
+
+ for n := 0; n < len(events); n++ {
+ if events[n].Pid == aevent.Pid && events[n].Syscall == aevent.Syscall {
+ if aevent.ProcCmdLine != "" || (aevent.ProcCmdLine == events[n].ProcCmdLine) {
+ events[n] = aevent
+ }
+ events[n].LastSeen = time.Now()
+
+ sortEvents()
+ return
+ }
+ }
+ aevent.LastSeen = time.Now()
+ events = append([]*Event{aevent}, events...)
+}
+
+// startEventsCleaner will review if the events in the cache need to be cleaned
+// every 5 minutes.
+func startEventsCleaner() {
+ for {
+ select {
+ case <-eventsCleanerChan:
+ goto Exit
+ case <-eventsCleaner.C:
+ cleanOldEvents()
+ }
+ }
+Exit:
+ log.Debug("audit: cleanerRoutine stopped")
+}
+
+func addRules() bool {
+ r64 := append([]string{"-A"}, rule64...)
+ r32 := append([]string{"-A"}, rule32...)
+ _, err64 := core.Exec("auditctl", r64)
+ _, err32 := core.Exec("auditctl", r32)
+ if err64 == nil && err32 == nil {
+ return true
+ }
+ log.Error("Error adding audit rule, err32=%v, err=%v", err32, err64)
+ return false
+}
+
+func configureSyscalls() {
+ // XXX: what about a i386 process running on a x86_64 system?
+ if runtime.GOARCH == "386" {
+ syscallSOCKET = "1"
+ syscallCONNECT = "3"
+ syscallSOCKETPAIR = "8"
+ }
+}
+
+func deleteRules() bool {
+ r64 := []string{"-D", "-k", "opensnitch"}
+ r32 := []string{"-D", "-k", "opensnitch"}
+ _, err64 := core.Exec("auditctl", r64)
+ _, err32 := core.Exec("auditctl", r32)
+ if err64 == nil && err32 == nil {
+ return true
+ }
+ log.Error("Error deleting audit rules, err32=%v, err64=%v", err32, err64)
+ return false
+}
+
+func checkRules() bool {
+ // TODO
+ return true
+}
+
+func checkStatus() bool {
+ // TODO
+ return true
+}
+
+// Reader reads events from audisd af_unix pipe plugin.
+// If the auditd daemon is stopped or restarted, the reader handle
+// is closed, so we need to restablished the connection.
+func Reader(r io.Reader, eventChan chan<- Event) {
+ if r == nil {
+ log.Error("Error reading auditd events. Is auditd running? is af_unix plugin enabled?")
+ return
+ }
+ reader := bufio.NewReader(r)
+ go startEventsCleaner()
+
+ for {
+ select {
+ case <-eventsExitChan:
+ goto Exit
+ default:
+ buf, _, err := reader.ReadLine()
+ if err != nil {
+ if err == io.EOF {
+ log.Error("AuditReader: auditd stopped, reconnecting in 30s %s", err)
+ if newReader, err := reconnect(); err == nil {
+ reader = bufio.NewReader(newReader)
+ log.Important("Auditd reconnected, continue reading")
+ }
+ continue
+ }
+ log.Warning("AuditReader: auditd error %s", err)
+ break
+ }
+
+ parseEvent(string(buf[0:len(buf)]), eventChan)
+ }
+ }
+Exit:
+ log.Debug("audit.Reader() closed")
+}
+
+// StartChannel creates a channel to receive events from Audit.
+// Launch audit.Reader() in a goroutine:
+// go audit.Reader(c, (chan<- audit.Event)(audit.EventChan))
+func StartChannel() {
+ EventChan = make(chan Event, 0)
+}
+
+func reconnect() (net.Conn, error) {
+ deleteRules()
+ time.Sleep(30 * time.Second)
+ return connect()
+}
+
+func connect() (net.Conn, error) {
+ addRules()
+ // TODO: make the unix socket path configurable
+ return net.Dial("unix", audispdPath)
+}
+
+// Stop stops listening for events from auditd and delete the auditd rules.
+func Stop() {
+ if auditConn != nil {
+ if err := auditConn.Close(); err != nil {
+ log.Warning("audit.Stop() error closing socket: %v", err)
+ }
+ }
+
+ if eventsCleaner != nil {
+ eventsCleaner.Stop()
+ }
+ if eventsExitChan != nil {
+ eventsExitChan <- true
+ close(eventsExitChan)
+ }
+ if eventsCleanerChan != nil {
+ eventsCleanerChan <- true
+ close(eventsCleanerChan)
+ }
+
+ deleteRules()
+ if EventChan != nil {
+ close(EventChan)
+ }
+}
+
+// Start makes a new connection to the audisp af_unix socket.
+func Start() (net.Conn, error) {
+ auditConn, err := connect()
+ if err != nil {
+ log.Error("auditd Start() connection error %v", err)
+ deleteRules()
+ return nil, err
+ }
+
+ configureSyscalls()
+ eventsCleaner = time.NewTicker(time.Minute * 5)
+ eventsCleanerChan = make(chan bool)
+ eventsExitChan = make(chan bool)
+ return auditConn, err
+}
--- /dev/null
+package audit
+
+import (
+ "encoding/hex"
+ "fmt"
+ "net"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var (
+ newEvent = false
+ netEvent = &Event{}
+
+ // RegExp for parse audit messages
+ // https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security_guide/sec-understanding_audit_log_files
+ auditRE, _ = regexp.Compile(`([a-zA-Z0-9\-_]+)=([a-zA-Z0-9:'\-\/\"\.\,_\(\)]+)`)
+ rawEvent = make(map[string]string)
+)
+
+// amd64 syscalls definition
+// if the platform is not amd64, it's redefined on Start()
+var (
+ syscallSOCKET = "41"
+ syscallCONNECT = "42"
+ syscallSOCKETPAIR = "53"
+ syscallEXECVE = "59"
+ syscallSOCKETCALL = "102"
+)
+
+// /usr/include/x86_64-linux-gnu/bits/socket_type.h
+const (
+ sockSTREAM = "1"
+ sockDGRAM = "2"
+ sockRAW = "3"
+ sockSEQPACKET = "5"
+ sockPACKET = "10"
+
+ // /usr/include/x86_64-linux-gnu/bits/socket.h
+ pfUNSPEC = "0"
+ pfLOCAL = "1" // PF_UNIX
+ pfINET = "2"
+ pfINET6 = "10"
+
+ // /etc/protocols
+ protoIP = "0"
+ protoTCP = "6"
+ protoUDP = "17"
+)
+
+// https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Audit_Record_Types.html
+const (
+ AuditTypePROCTITLE = "type=PROCTITLE"
+ AuditTypeCWD = "type=CWD"
+ AuditTypePATH = "type=PATH"
+ AuditTypeEXECVE = "type=EXECVE"
+ AuditTypeSOCKADDR = "type=SOCKADDR"
+ AuditTypeSOCKETCALL = "type=SOCKETCALL"
+ AuditTypeEOE = "type=EOE"
+)
+
+var (
+ syscallSOCKETstr = fmt.Sprint("syscall=", syscallSOCKET)
+ syscallCONNECTstr = fmt.Sprint("syscall=", syscallCONNECT)
+ syscallSOCKETPAIRstr = fmt.Sprint("syscall=", syscallSOCKETPAIR)
+ syscallEXECVEstr = fmt.Sprint("syscall=", syscallEXECVE)
+ syscallSOCKETCALLstr = fmt.Sprint("syscall=", syscallSOCKETCALL)
+)
+
+// parseNetLine parses a SOCKADDR message type of the form:
+// saddr string: inet6 host:2001:4860:4860::8888 serv:53
+func parseNetLine(line string, decode bool) (family string, dstHost net.IP, dstPort int) {
+
+ // 0:4 - type
+ // 4:8 - port
+ // 8:16 - ip
+ switch family := line[0:4]; family {
+ // local
+ // case "0100":
+ // ipv4
+ case "0200":
+ octet2 := decodeString(line[4:8])
+ octet := decodeString(line[8:16])
+ host := fmt.Sprint(octet[0], ".", octet[1], ".", octet[2], ".", octet[3])
+ fmt.Printf("dest ip: %s -- %s:%s\n", line[4:8], octet2, host)
+ // ipv6
+ //case "0A00":
+ }
+
+ if decode == true {
+ line = decodeString(line)
+ }
+ pieces := strings.Split(line, " ")
+ family = pieces[0]
+
+ if family[:4] != "inet" {
+ return family, dstHost, 0
+ }
+
+ if len(pieces) > 1 && pieces[1][:5] == "host:" {
+ dstHost = net.ParseIP(strings.Split(pieces[1], "host:")[1])
+ }
+ if len(pieces) > 2 && pieces[2][:5] == "serv:" {
+ _dstPort, err := strconv.Atoi(strings.Split(line, "serv:")[1])
+ if err != nil {
+ dstPort = -1
+ } else {
+ dstPort = _dstPort
+ }
+ }
+
+ return family, dstHost, dstPort
+}
+
+// decodeString will try to decode a string encoded in hexadecimal.
+// If the string can not be decoded, the original string will be returned.
+// In that case, usually it means that it's a non-encoded string.
+func decodeString(s string) string {
+ decoded, err := hex.DecodeString(s)
+ if err != nil {
+ return s
+ }
+ return fmt.Sprintf("%s", decoded)
+}
+
+// extractFields parsed an audit raw message, and extracts all the fields.
+func extractFields(rawMessage string, newEvent *map[string]string) {
+ Lock.Lock()
+ defer Lock.Unlock()
+
+ if auditRE == nil {
+ newEvent = nil
+ return
+ }
+ fieldList := auditRE.FindAllStringSubmatch(rawMessage, -1)
+ if fieldList == nil {
+ newEvent = nil
+ return
+ }
+ for _, field := range fieldList {
+ (*newEvent)[field[1]] = field[2]
+ }
+}
+
+// populateEvent populates our Event from a raw parsed message.
+func populateEvent(aevent *Event, eventFields *map[string]string) *Event {
+ if aevent == nil {
+ return nil
+ }
+ Lock.Lock()
+ defer Lock.Unlock()
+
+ for k, v := range *eventFields {
+ switch k {
+ //case "a0":
+ //case "a1":
+ //case "a2":
+ case "fam":
+ if v == "local" {
+ return nil
+ }
+ aevent.NetFamily = v
+ case "lport":
+ aevent.DstPort, _ = strconv.Atoi(v)
+ // TODO
+ /*case "addr":
+ fmt.Println("addr: ", v)
+ case "daddr":
+ fmt.Println("daddr: ", v)
+ case "laddr":
+ aevent.DstHost = net.ParseIP(v)
+ case "saddr":
+ parseNetLine(v, true)
+ fmt.Println("saddr:", v)
+ */
+ case "exe":
+ aevent.ProcPath = strings.Trim(decodeString(v), "\"")
+ case "comm":
+ aevent.ProcName = strings.Trim(decodeString(v), "\"")
+ // proctitle may be truncated to 128 characters, so don't rely on it, parse /proc/<pid>/instead
+ //case "proctitle":
+ // aevent.ProcCmdLine = strings.Trim(decodeString(v), "\"")
+ case "tty":
+ aevent.TTY = v
+ case "pid":
+ aevent.Pid, _ = strconv.Atoi(v)
+ case "ppid":
+ aevent.PPid, _ = strconv.Atoi(v)
+ case "uid":
+ aevent.UID, _ = strconv.Atoi(v)
+ case "gid":
+ aevent.Gid, _ = strconv.Atoi(v)
+ case "success":
+ aevent.Success = v
+ case "cwd":
+ aevent.ProcDir = strings.Trim(decodeString(v), "\"")
+ case "inode":
+ aevent.INode, _ = strconv.Atoi(v)
+ case "dev":
+ aevent.Dev = v
+ case "mode":
+ aevent.ProcMode = v
+ case "ouid":
+ aevent.OUid, _ = strconv.Atoi(v)
+ case "ogid":
+ aevent.OGid, _ = strconv.Atoi(v)
+ case "syscall":
+ aevent.Syscall, _ = strconv.Atoi(v)
+ case "exit":
+ aevent.Exit, _ = strconv.Atoi(v)
+ case "type":
+ aevent.EventType = v
+ case "msg":
+ parts := strings.Split(v[6:], ":")
+ aevent.Timestamp = parts[0]
+ aevent.Serial = parts[1][:len(parts[1])-1]
+ }
+ }
+
+ return aevent
+}
+
+// parseEvent parses an auditd event, discards the unwanted ones, and adds
+// the ones we're interested in to an array.
+// We're only interested in the socket,socketpair,connect and execve syscalls.
+// Events from us are excluded.
+//
+// When we received an event, we parse and add it to the list as soon as we can.
+// If the next messages of the set have additional information, we update the
+// event.
+func parseEvent(rawMessage string, eventChan chan<- Event) {
+ if newEvent == false && strings.Index(rawMessage, OpensnitchRulesKey) == -1 {
+ return
+ }
+
+ aEvent := make(map[string]string)
+ if strings.Index(rawMessage, syscallSOCKETstr) != -1 ||
+ strings.Index(rawMessage, syscallCONNECTstr) != -1 ||
+ strings.Index(rawMessage, syscallSOCKETPAIRstr) != -1 ||
+ strings.Index(rawMessage, syscallEXECVEstr) != -1 ||
+ strings.Index(rawMessage, syscallSOCKETCALLstr) != -1 {
+
+ extractFields(rawMessage, &aEvent)
+ if aEvent == nil {
+ return
+ }
+ newEvent = true
+ netEvent = &Event{}
+ netEvent = populateEvent(netEvent, &aEvent)
+ AddEvent(netEvent)
+ } else if newEvent == true && strings.Index(rawMessage, AuditTypePROCTITLE) != -1 {
+ extractFields(rawMessage, &aEvent)
+ if aEvent == nil {
+ return
+ }
+ netEvent = populateEvent(netEvent, &aEvent)
+ AddEvent(netEvent)
+ } else if newEvent == true && strings.Index(rawMessage, AuditTypeCWD) != -1 {
+ extractFields(rawMessage, &aEvent)
+ if aEvent == nil {
+ return
+ }
+ netEvent = populateEvent(netEvent, &aEvent)
+ AddEvent(netEvent)
+ } else if newEvent == true && strings.Index(rawMessage, AuditTypeEXECVE) != -1 {
+ extractFields(rawMessage, &aEvent)
+ if aEvent == nil {
+ return
+ }
+ netEvent = populateEvent(netEvent, &aEvent)
+ AddEvent(netEvent)
+ } else if newEvent == true && strings.Index(rawMessage, AuditTypePATH) != -1 {
+ extractFields(rawMessage, &aEvent)
+ if aEvent == nil {
+ return
+ }
+ netEvent = populateEvent(netEvent, &aEvent)
+ AddEvent(netEvent)
+ } else if newEvent == true && strings.Index(rawMessage, AuditTypeSOCKADDR) != -1 {
+ extractFields(rawMessage, &aEvent)
+ if aEvent == nil {
+ return
+ }
+
+ netEvent = populateEvent(netEvent, &aEvent)
+ AddEvent(netEvent)
+ if EventChan != nil {
+ eventChan <- *netEvent
+ }
+ } else if newEvent == true && strings.Index(rawMessage, AuditTypeEOE) != -1 {
+ newEvent = false
+ AddEvent(netEvent)
+ if EventChan != nil {
+ eventChan <- *netEvent
+ }
+ }
+}
--- /dev/null
+package procmon
+
+import (
+ "fmt"
+ "os"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+)
+
+// InodeItem represents an item of the InodesCache.
+type InodeItem struct {
+ sync.RWMutex
+
+ Pid int
+ FdPath string
+ LastSeen int64
+}
+
+// ProcItem represents an item of the pidsCache
+type ProcItem struct {
+ sync.RWMutex
+
+ Pid int
+ FdPath string
+ Descriptors []string
+ LastSeen int64
+}
+
+// CacheProcs holds the cache of processes that have established connections.
+type CacheProcs struct {
+ sync.RWMutex
+ items []*ProcItem
+}
+
+// CacheInodes holds the cache of Inodes.
+// The key is formed as follow:
+// inode+srcip+srcport+dstip+dstport
+type CacheInodes struct {
+ sync.RWMutex
+ items map[string]*InodeItem
+}
+
+var (
+ // cache of inodes, which help to not iterate over all the pidsCache and
+ // descriptors of /proc/<pid>/fd/
+ // 15-50us vs 50-80ms
+ // we hit this cache when:
+ // - we've blocked a connection and the process retries it several times until it gives up,
+ // - or when a process timeouts connecting to an IP/domain and it retries it again,
+ // - or when a process resolves a domain and then connects to the IP.
+ inodesCache = NewCacheOfInodes()
+ maxTTL = 3 // maximum 3 minutes of inactivity in cache. Really rare, usually they lasts less than a minute.
+
+ // 2nd cache of already known running pids, which also saves time by
+ // iterating only over a few pids' descriptors, (30us-20ms vs. 50-80ms)
+ // since it's more likely that most of the connections will be made by the
+ // same (running) processes.
+ // The cache is ordered by time, placing in the first places those PIDs with
+ // active connections.
+ pidsCache CacheProcs
+ pidsDescriptorsCache = make(map[int][]string)
+
+ cacheTicker = time.NewTicker(2 * time.Minute)
+)
+
+// CacheCleanerTask checks periodically if the inodes in the cache must be removed.
+func CacheCleanerTask() {
+ for {
+ select {
+ case <-cacheTicker.C:
+ inodesCache.cleanup()
+ }
+ }
+}
+
+// NewCacheOfInodes returns a new cache for inodes.
+func NewCacheOfInodes() *CacheInodes {
+ return &CacheInodes{
+ items: make(map[string]*InodeItem),
+ }
+}
+
+//******************************************************************************
+// items of the caches.
+
+func (i *InodeItem) updateTime() {
+ i.Lock()
+ i.LastSeen = time.Now().UnixNano()
+ i.Unlock()
+}
+
+func (i *InodeItem) getTime() int64 {
+ i.RLock()
+ defer i.RUnlock()
+ return i.LastSeen
+}
+
+func (p *ProcItem) updateTime() {
+ p.Lock()
+ p.LastSeen = time.Now().UnixNano()
+ p.Unlock()
+}
+
+func (p *ProcItem) updateDescriptors(descriptors []string) {
+ p.Lock()
+ p.Descriptors = descriptors
+ p.Unlock()
+}
+
+//******************************************************************************
+// cache of processes
+
+func (c *CacheProcs) add(fdPath string, fdList []string, pid int) {
+ c.Lock()
+ defer c.Unlock()
+ for n := range c.items {
+ item := c.items[n]
+ if item == nil {
+ continue
+ }
+ if item.Pid == pid {
+ item.updateTime()
+ return
+ }
+ }
+
+ procItem := &ProcItem{
+ Pid: pid,
+ FdPath: fdPath,
+ Descriptors: fdList,
+ LastSeen: time.Now().UnixNano(),
+ }
+
+ c.setItems([]*ProcItem{procItem}, c.items)
+}
+
+func (c *CacheProcs) sort(pid int) {
+ item := c.getItem(0)
+ if item != nil && item.Pid == pid {
+ return
+ }
+ c.RLock()
+ defer c.RUnlock()
+
+ sort.Slice(c.items, func(i, j int) bool {
+ t := c.items[i].LastSeen
+ u := c.items[j].LastSeen
+ return t > u || t == u
+ })
+}
+
+func (c *CacheProcs) delete(pid int) {
+ c.Lock()
+ defer c.Unlock()
+
+ for n, procItem := range c.items {
+ if procItem.Pid == pid {
+ c.deleteItem(n)
+ inodesCache.delete(pid)
+ break
+ }
+ }
+}
+
+func (c *CacheProcs) deleteItem(pos int) {
+ nItems := len(c.items)
+ if pos < nItems {
+ c.setItems(c.items[:pos], c.items[pos+1:])
+ }
+}
+
+func (c *CacheProcs) setItems(newItems []*ProcItem, oldItems []*ProcItem) {
+ c.items = append(newItems, oldItems...)
+}
+
+func (c *CacheProcs) getItem(index int) *ProcItem {
+ c.RLock()
+ defer c.RUnlock()
+
+ if index >= len(c.items) {
+ return nil
+ }
+
+ return c.items[index]
+}
+
+func (c *CacheProcs) getItems() []*ProcItem {
+ return c.items
+}
+
+func (c *CacheProcs) countItems() int {
+ c.RLock()
+ defer c.RUnlock()
+
+ return len(c.items)
+}
+
+// loop over the processes that have generated connections
+func (c *CacheProcs) getPid(inode int, inodeKey string, expect string) (int, int) {
+ c.Lock()
+ defer c.Unlock()
+
+ for n, procItem := range c.items {
+ if procItem == nil {
+ continue
+ }
+
+ if idxDesc, _ := getPidDescriptorsFromCache(procItem.FdPath, inodeKey, expect, &procItem.Descriptors, procItem.Pid); idxDesc != -1 {
+ procItem.updateTime()
+ return procItem.Pid, n
+ }
+
+ descriptors := lookupPidDescriptors(procItem.FdPath, procItem.Pid)
+ if descriptors == nil {
+ c.deleteItem(n)
+ continue
+ }
+
+ procItem.updateDescriptors(descriptors)
+ if idxDesc, _ := getPidDescriptorsFromCache(procItem.FdPath, inodeKey, expect, &descriptors, procItem.Pid); idxDesc != -1 {
+ procItem.updateTime()
+ return procItem.Pid, n
+ }
+ }
+
+ return -1, -1
+}
+
+//******************************************************************************
+// cache of inodes
+
+func (i *CacheInodes) add(key, descLink string, pid int) {
+ i.Lock()
+ defer i.Unlock()
+
+ if descLink == "" {
+ descLink = fmt.Sprint("/proc/", pid, "/exe")
+ }
+ i.items[key] = &InodeItem{
+ FdPath: descLink,
+ Pid: pid,
+ LastSeen: time.Now().UnixNano(),
+ }
+}
+
+func (i *CacheInodes) delete(pid int) {
+ i.Lock()
+ defer i.Unlock()
+
+ for k, inodeItem := range i.items {
+ if inodeItem.Pid == pid {
+ delete(i.items, k)
+ }
+ }
+}
+
+func (i *CacheInodes) getPid(inodeKey string) int {
+ if item, ok := i.isInCache(inodeKey); ok {
+ // sometimes the process may have disappeared at this point
+ if _, err := os.Lstat(item.FdPath); err == nil {
+ item.updateTime()
+ return item.Pid
+ }
+ pidsCache.delete(item.Pid)
+ i.delItem(inodeKey)
+ }
+
+ return -1
+}
+
+func (i *CacheInodes) delItem(inodeKey string) {
+ i.Lock()
+ defer i.Unlock()
+ delete(i.items, inodeKey)
+}
+
+func (i *CacheInodes) getItem(inodeKey string) *InodeItem {
+ i.RLock()
+ defer i.RUnlock()
+
+ return i.items[inodeKey]
+}
+
+func (i *CacheInodes) getItems() map[string]*InodeItem {
+ i.RLock()
+ defer i.RUnlock()
+
+ return i.items
+}
+
+func (i *CacheInodes) isInCache(inodeKey string) (*InodeItem, bool) {
+ i.RLock()
+ defer i.RUnlock()
+
+ if item, found := i.items[inodeKey]; found {
+ return item, true
+ }
+ return nil, false
+}
+
+func (i *CacheInodes) cleanup() {
+ now := time.Now()
+ i.Lock()
+ defer i.Unlock()
+ for k := range i.items {
+ if i.items[k] == nil {
+ continue
+ }
+ lastSeen := now.Sub(
+ time.Unix(0, i.items[k].getTime()),
+ )
+ if core.Exists(i.items[k].FdPath) == false || int(lastSeen.Minutes()) > maxTTL {
+ delete(i.items, k)
+ }
+ }
+}
+
+func getPidDescriptorsFromCache(fdPath, inodeKey, expect string, descriptors *[]string, pid int) (int, *[]string) {
+ for fdIdx := 0; fdIdx < len(*descriptors); fdIdx++ {
+ descLink := fmt.Sprint(fdPath, (*descriptors)[fdIdx])
+ if link, err := os.Readlink(descLink); err == nil && link == expect {
+ if fdIdx > 0 {
+ // reordering helps to reduce look up times by a factor of 10.
+ fd := (*descriptors)[fdIdx]
+ *descriptors = append((*descriptors)[:fdIdx], (*descriptors)[fdIdx+1:]...)
+ *descriptors = append([]string{fd}, *descriptors...)
+ }
+ if _, ok := inodesCache.isInCache(inodeKey); ok {
+ inodesCache.add(inodeKey, descLink, pid)
+ }
+ return fdIdx, descriptors
+ }
+ }
+
+ return -1, descriptors
+}
--- /dev/null
+package procmon
+
+import (
+ "fmt"
+ "testing"
+ "time"
+)
+
+func TestCacheProcs(t *testing.T) {
+ fdList := []string{"0", "1", "2"}
+ pidsCache.add(fmt.Sprint("/proc/", myPid, "/fd/"), fdList, myPid)
+ t.Log("Pids in cache: ", pidsCache.countItems())
+
+ t.Run("Test addProcEntry", func(t *testing.T) {
+ if pidsCache.countItems() != 1 {
+ t.Error("pidsCache should be 1")
+ }
+ })
+
+ oldPid := pidsCache.getItem(0)
+ pidsCache.add(fmt.Sprint("/proc/", myPid, "/fd/"), fdList, myPid)
+ t.Run("Test addProcEntry update", func(t *testing.T) {
+ if pidsCache.countItems() != 1 {
+ t.Error("pidsCache should still be 1!", pidsCache)
+ }
+ oldTime := time.Unix(0, oldPid.LastSeen)
+ newTime := time.Unix(0, pidsCache.getItem(0).LastSeen)
+ if oldTime.Equal(newTime) == false {
+ t.Error("pidsCache, time not updated: ", oldTime, newTime)
+ }
+ })
+
+ pidsCache.add("/proc/2/fd", fdList, 2)
+ pidsCache.delete(2)
+ t.Run("Test deleteProcEntry", func(t *testing.T) {
+ if pidsCache.countItems() != 1 {
+ t.Error("pidsCache should be 1:", pidsCache.countItems())
+ }
+ })
+
+ pid, _ := pidsCache.getPid(0, "", "/dev/null")
+ t.Run("Test getPidFromCache", func(t *testing.T) {
+ if pid != myPid {
+ t.Error("pid not found in cache", pidsCache.countItems())
+ }
+ })
+
+ // should not crash, and the number of items should still be 1
+ pidsCache.deleteItem(1)
+ t.Run("Test deleteItem check bounds", func(t *testing.T) {
+ if pidsCache.countItems() != 1 {
+ t.Error("deleteItem check bounds error", pidsCache.countItems())
+ }
+ })
+
+ pidsCache.deleteItem(0)
+ t.Run("Test deleteItem", func(t *testing.T) {
+ if pidsCache.countItems() != 0 {
+ t.Error("deleteItem error", pidsCache.countItems())
+ }
+ })
+ t.Log("items in cache:", pidsCache.countItems())
+
+ // the key of an inodeCache entry is formed as: inodeNumer + srcIP + srcPort + dstIP + dstPort
+ inodeKey := "000000000127.0.0.144444127.0.0.153"
+ // add() expects a path to the inode fd (/proc/<pid>/fd/12345), but as getPid() will check the path in order to retrieve the pid,
+ // we just set it to "" and it'll use /proc/<pid>/exe
+ inodesCache.add(inodeKey, "", myPid)
+ t.Run("Test addInodeEntry", func(t *testing.T) {
+ if _, found := inodesCache.items[inodeKey]; !found {
+ t.Error("inodesCache, inode not added:", len(inodesCache.items), inodesCache.items)
+ }
+ })
+
+ pid = inodesCache.getPid(inodeKey)
+ t.Run("Test getPidByInodeFromCache", func(t *testing.T) {
+ if pid != myPid {
+ t.Error("inode not found in cache", pid, inodeKey, len(inodesCache.items), inodesCache.items)
+ }
+ })
+
+ // should delete all inodes of a pid
+ inodesCache.delete(myPid)
+ t.Run("Test deleteInodeEntry", func(t *testing.T) {
+ if _, found := inodesCache.items[inodeKey]; found {
+ t.Error("inodesCache, key found in cache but it should not exist", inodeKey, len(inodesCache.items), inodesCache.items)
+ }
+ })
+}
+
+// Test getPidDescriptorsFromCache descriptors (inodes) reordering.
+// When an inode (descriptor) is found, if it's pushed to the top of the list,
+// the next time we look for it will cost -10x.
+// Without reordering, the inode 0 will always be found on the 10th position,
+// taking an average of 100us instead of 30.
+// Benchmark results with reordering: ~5600ns/op, without: ~56000ns/op.
+func BenchmarkGetPid(b *testing.B) {
+ fdList := []string{"10", "9", "8", "7", "6", "5", "4", "3", "2", "1", "0"}
+ pidsCache.add(fmt.Sprint("/proc/", myPid, "/fd/"), fdList, myPid)
+ for i := 0; i < b.N; i++ {
+ pidsCache.getPid(0, "", "/dev/null")
+ }
+}
--- /dev/null
+package procmon
+
+import (
+ "bufio"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/dns"
+ "github.com/evilsocket/opensnitch/daemon/netlink"
+)
+
+var socketsRegex, _ = regexp.Compile(`socket:\[([0-9]+)\]`)
+
+// GetInfo collects information of a process.
+func (p *Process) GetInfo() error {
+ if err := p.readPath(); err != nil {
+ return err
+ }
+ p.readCwd()
+ p.readCmdline()
+ p.readEnv()
+ p.readDescriptors()
+ p.readIOStats()
+ p.readStatus()
+ p.cleanPath()
+
+ return nil
+}
+
+func (p *Process) setCwd(cwd string) {
+ p.CWD = cwd
+}
+
+func (p *Process) readComm() error {
+ data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", p.ID))
+ if err != nil {
+ return err
+ }
+ p.Comm = core.Trim(string(data))
+ return nil
+}
+
+func (p *Process) readCwd() error {
+ link, err := os.Readlink(fmt.Sprintf("/proc/%d/cwd", p.ID))
+ if err != nil {
+ return err
+ }
+ p.CWD = link
+ return nil
+}
+
+// read and parse environment variables of a process.
+func (p *Process) readEnv() {
+ if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/environ", p.ID)); err == nil {
+ for _, s := range strings.Split(string(data), "\x00") {
+ parts := strings.SplitN(core.Trim(s), "=", 2)
+ if parts != nil && len(parts) == 2 {
+ key := core.Trim(parts[0])
+ val := core.Trim(parts[1])
+ p.Env[key] = val
+ }
+ }
+ }
+}
+
+func (p *Process) readPath() error {
+ linkName := fmt.Sprint("/proc/", p.ID, "/exe")
+ if _, err := os.Lstat(linkName); err != nil {
+ return err
+ }
+
+ if link, err := os.Readlink(linkName); err == nil {
+ p.Path = link
+ }
+
+ return nil
+}
+
+func (p *Process) readCmdline() {
+ if data, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cmdline", p.ID)); err == nil {
+ if len(data) == 0 {
+ return
+ }
+ for i, b := range data {
+ if b == 0x00 {
+ data[i] = byte(' ')
+ }
+ }
+
+ p.Args = make([]string, 0)
+
+ args := strings.Split(string(data), " ")
+ for _, arg := range args {
+ arg = core.Trim(arg)
+ if arg != "" {
+ p.Args = append(p.Args, arg)
+ }
+ }
+ }
+}
+
+func (p *Process) readDescriptors() {
+ f, err := os.Open(fmt.Sprint("/proc/", p.ID, "/fd/"))
+ if err != nil {
+ return
+ }
+ fDesc, err := f.Readdir(-1)
+ f.Close()
+ p.Descriptors = nil
+
+ for _, fd := range fDesc {
+ tempFd := &procDescriptors{
+ Name: fd.Name(),
+ }
+ if link, err := os.Readlink(fmt.Sprint("/proc/", p.ID, "/fd/", fd.Name())); err == nil {
+ tempFd.SymLink = link
+ socket := socketsRegex.FindStringSubmatch(link)
+ if len(socket) > 0 {
+ socketInfo, err := netlink.GetSocketInfoByInode(socket[1])
+ if err == nil {
+ tempFd.SymLink = fmt.Sprintf("socket:[%s] - %d:%s -> %s:%d, state: %s", fd.Name(),
+ socketInfo.ID.SourcePort,
+ socketInfo.ID.Source.String(),
+ dns.HostOr(socketInfo.ID.Destination, socketInfo.ID.Destination.String()),
+ socketInfo.ID.DestinationPort,
+ netlink.TCPStatesMap[socketInfo.State])
+ }
+ }
+
+ if linkInfo, err := os.Lstat(link); err == nil {
+ tempFd.Size = linkInfo.Size()
+ tempFd.ModTime = linkInfo.ModTime()
+ }
+ }
+ p.Descriptors = append(p.Descriptors, tempFd)
+ }
+}
+
+func (p *Process) readIOStats() {
+ f, err := os.Open(fmt.Sprint("/proc/", p.ID, "/io"))
+ if err != nil {
+ return
+ }
+ defer f.Close()
+
+ p.IOStats = &procIOstats{}
+
+ scanner := bufio.NewScanner(f)
+ for scanner.Scan() {
+ s := strings.Split(scanner.Text(), " ")
+ switch s[0] {
+ case "rchar:":
+ p.IOStats.RChar, _ = strconv.ParseInt(s[1], 10, 64)
+ case "wchar:":
+ p.IOStats.WChar, _ = strconv.ParseInt(s[1], 10, 64)
+ case "syscr:":
+ p.IOStats.SyscallRead, _ = strconv.ParseInt(s[1], 10, 64)
+ case "syscw:":
+ p.IOStats.SyscallWrite, _ = strconv.ParseInt(s[1], 10, 64)
+ case "read_bytes:":
+ p.IOStats.ReadBytes, _ = strconv.ParseInt(s[1], 10, 64)
+ case "write_bytes:":
+ p.IOStats.WriteBytes, _ = strconv.ParseInt(s[1], 10, 64)
+ }
+ }
+}
+
+func (p *Process) readStatus() {
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/status")); err == nil {
+ p.Status = string(data)
+ }
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/stat")); err == nil {
+ p.Stat = string(data)
+ }
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/stack")); err == nil {
+ p.Stack = string(data)
+ }
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/maps")); err == nil {
+ p.Maps = string(data)
+ }
+ if data, err := ioutil.ReadFile(fmt.Sprint("/proc/", p.ID, "/statm")); err == nil {
+ p.Statm = &procStatm{}
+ fmt.Sscanf(string(data), "%d %d %d %d %d %d %d", &p.Statm.Size, &p.Statm.Resident, &p.Statm.Shared, &p.Statm.Text, &p.Statm.Lib, &p.Statm.Data, &p.Statm.Dt)
+ }
+}
+
+func (p *Process) cleanPath() {
+ pathLen := len(p.Path)
+ if pathLen >= 10 && p.Path[pathLen-10:] == " (deleted)" {
+ p.Path = p.Path[:len(p.Path)-10]
+ }
+}
--- /dev/null
+package ebpf
+
+import (
+ "sync"
+ "time"
+)
+
+type ebpfCacheItem struct {
+ Key []byte
+ LastSeen int64
+ UID int
+ Pid int
+ Hits uint
+}
+
+type ebpfCacheType struct {
+ Items map[string]*ebpfCacheItem
+ sync.RWMutex
+}
+
+var (
+ maxTTL = 20 // Seconds
+ maxCacheItems = 5000
+ ebpfCache *ebpfCacheType
+ ebpfCacheTicker *time.Ticker
+)
+
+// NewEbpfCacheItem creates a new cache item.
+func NewEbpfCacheItem(key []byte, pid, uid int) *ebpfCacheItem {
+ return &ebpfCacheItem{
+ Key: key,
+ Hits: 1,
+ Pid: pid,
+ UID: uid,
+ LastSeen: time.Now().UnixNano(),
+ }
+}
+
+func (i *ebpfCacheItem) isValid() bool {
+ lastSeen := time.Now().Sub(
+ time.Unix(0, i.LastSeen),
+ )
+ return int(lastSeen.Seconds()) < maxTTL
+}
+
+// NewEbpfCache creates a new cache store.
+func NewEbpfCache() *ebpfCacheType {
+ ebpfCacheTicker = time.NewTicker(1 * time.Minute)
+ return &ebpfCacheType{
+ Items: make(map[string]*ebpfCacheItem, 0),
+ }
+}
+
+func (e *ebpfCacheType) addNewItem(key string, itemKey []byte, pid, uid int) {
+ e.Lock()
+ defer e.Unlock()
+
+ e.Items[key] = NewEbpfCacheItem(itemKey, pid, uid)
+}
+
+func (e *ebpfCacheType) isInCache(key string) (item *ebpfCacheItem, found bool) {
+ leng := e.Len()
+
+ e.Lock()
+ item, found = e.Items[key]
+ if found {
+ if item.isValid() {
+ e.update(key, item)
+ } else {
+ found = false
+ delete(e.Items, key)
+ }
+ }
+ e.Unlock()
+
+ if leng > maxCacheItems {
+ e.DeleteOldItems()
+ }
+ return
+}
+
+func (e *ebpfCacheType) update(key string, item *ebpfCacheItem) {
+ item.Hits++
+ item.LastSeen = time.Now().UnixNano()
+ e.Items[key] = item
+}
+
+func (e *ebpfCacheType) Len() int {
+ e.RLock()
+ defer e.RUnlock()
+ return len(e.Items)
+}
+
+func (e *ebpfCacheType) DeleteOldItems() {
+ length := e.Len()
+
+ e.Lock()
+ defer e.Unlock()
+
+ for k, item := range e.Items {
+ if length > maxCacheItems || !item.isValid() {
+ delete(e.Items, k)
+ }
+ }
+}
+
+func (e *ebpfCacheType) clear() {
+ if e == nil {
+ return
+ }
+ for k := range e.Items {
+ delete(e.Items, k)
+ }
+
+ if ebpfCacheTicker != nil {
+ ebpfCacheTicker.Stop()
+ }
+}
--- /dev/null
+package ebpf
+
+import (
+ "fmt"
+ "os/exec"
+ "strconv"
+ "syscall"
+ "unsafe"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+ daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink"
+ elf "github.com/iovisor/gobpf/elf"
+)
+
+// print map contents. used only for debugging
+func dumpMap(bpfmap *elf.Map, isIPv6 bool) {
+ var lookupKey []byte
+ var nextKey []byte
+ var value []byte
+ if !isIPv6 {
+ lookupKey = make([]byte, 12)
+ nextKey = make([]byte, 12)
+ value = make([]byte, 24)
+ } else {
+ lookupKey = make([]byte, 36)
+ nextKey = make([]byte, 36)
+ value = make([]byte, 24)
+ }
+ firstrun := true
+ i := 0
+ for {
+ i++
+ ok, err := m.LookupNextElement(bpfmap, unsafe.Pointer(&lookupKey[0]),
+ unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value[0]))
+ if err != nil {
+ log.Error("eBPF LookupNextElement error: %v", err)
+ return
+ }
+ if firstrun {
+ // on first run lookupKey is a dummy, nothing to delete
+ firstrun = false
+ copy(lookupKey, nextKey)
+ continue
+ }
+ fmt.Println("key, value", lookupKey, value)
+
+ if !ok { //reached end of map
+ break
+ }
+ copy(lookupKey, nextKey)
+ }
+}
+
+//PrintEverything prints all the stats. used only for debugging
+func PrintEverything() {
+ bash, _ := exec.LookPath("bash")
+ //get the number of the first map
+ out, err := exec.Command(bash, "-c", "bpftool map show | head -n 1 | cut -d ':' -f1").Output()
+ if err != nil {
+ fmt.Println("bpftool map dump name tcpMap ", err)
+ }
+ i, _ := strconv.Atoi(string(out[:len(out)-1]))
+ fmt.Println("i is", i)
+
+ //dump all maps for analysis
+ for j := i; j < i+14; j++ {
+ _, _ = exec.Command(bash, "-c", "bpftool map dump id "+strconv.Itoa(j)+" > dump"+strconv.Itoa(j)).Output()
+ }
+
+ alreadyEstablished.RLock()
+ for sock1, v := range alreadyEstablished.TCP {
+ fmt.Println(*sock1, v)
+ }
+
+ fmt.Println("---------------------")
+ for sock1, v := range alreadyEstablished.TCPv6 {
+ fmt.Println(*sock1, v)
+ }
+ alreadyEstablished.RUnlock()
+
+ fmt.Println("---------------------")
+ sockets, _ := daemonNetlink.SocketsDump(syscall.AF_INET, syscall.IPPROTO_TCP)
+ for idx := range sockets {
+ fmt.Println("socket tcp: ", sockets[idx])
+ }
+ fmt.Println("---------------------")
+ sockets, _ = daemonNetlink.SocketsDump(syscall.AF_INET6, syscall.IPPROTO_TCP)
+ for idx := range sockets {
+ fmt.Println("socket tcp6: ", sockets[idx])
+ }
+ fmt.Println("---------------------")
+ sockets, _ = daemonNetlink.SocketsDump(syscall.AF_INET, syscall.IPPROTO_UDP)
+ for idx := range sockets {
+ fmt.Println("socket udp: ", sockets[idx])
+ }
+ fmt.Println("---------------------")
+ sockets, _ = daemonNetlink.SocketsDump(syscall.AF_INET6, syscall.IPPROTO_UDP)
+ for idx := range sockets {
+ fmt.Println("socket udp6: ", sockets[idx])
+ }
+
+}
--- /dev/null
+package ebpf
+
+import (
+ "encoding/binary"
+ "fmt"
+ "net"
+ "sync"
+ "syscall"
+ "unsafe"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink"
+ "github.com/evilsocket/opensnitch/daemon/procmon"
+ elf "github.com/iovisor/gobpf/elf"
+)
+
+//contains pointers to ebpf maps for a given protocol (tcp/udp/v6)
+type ebpfMapsForProto struct {
+ counterMap *elf.Map
+ bpfmap *elf.Map
+}
+
+//Not in use, ~4usec faster lookup compared to m.LookupElement()
+
+//mimics union bpf_attr's anonymous struct used by BPF_MAP_*_ELEM commands
+//from <linux_headers>/include/uapi/linux/bpf.h
+type bpf_lookup_elem_t struct {
+ map_fd uint64 //even though in bpf.h its type is __u32, we must make it 8 bytes long
+ //because "key" is of type __aligned_u64, i.e. "key" must be aligned on an 8-byte boundary
+ key uintptr
+ value uintptr
+}
+
+type alreadyEstablishedConns struct {
+ TCP map[*daemonNetlink.Socket]int
+ TCPv6 map[*daemonNetlink.Socket]int
+ sync.RWMutex
+}
+
+var (
+ m *elf.Module
+ lock = sync.RWMutex{}
+ mapSize = uint(12000)
+ ebpfMaps map[string]*ebpfMapsForProto
+ //connections which were established at the time when opensnitch started
+ alreadyEstablished = alreadyEstablishedConns{
+ TCP: make(map[*daemonNetlink.Socket]int),
+ TCPv6: make(map[*daemonNetlink.Socket]int),
+ }
+
+ //stop == true is a signal for all goroutines to stop
+ stop = false
+
+ // list of local addresses of this machine
+ localAddresses []net.IP
+
+ hostByteOrder binary.ByteOrder
+)
+
+//Start installs ebpf kprobes
+func Start() error {
+
+ if err := mountDebugFS(); err != nil {
+ log.Error("ebpf.Start -> mount debugfs error. Report on github please: %s", err)
+ return err
+ }
+
+ m = elf.NewModule("/etc/opensnitchd/opensnitch.o")
+ if err := m.Load(nil); err != nil {
+ log.Error("eBPF Failed to load /etc/opensnitchd/opensnitch.o: %v", err)
+ return err
+ }
+
+ // if previous shutdown was unclean, then we must remove the dangling kprobe
+ // and install it again (close the module and load it again)
+ if err := m.EnableKprobes(0); err != nil {
+ m.Close()
+ if err := m.Load(nil); err != nil {
+ log.Error("eBPF failed to load /etc/opensnitchd/opensnitch.o (2): %v", err)
+ return err
+ }
+ if err := m.EnableKprobes(0); err != nil {
+ log.Error("eBPF error when enabling kprobes: %v", err)
+ return err
+ }
+ }
+
+ // init all connection counters to 0
+ zeroKey := make([]byte, 4)
+ zeroValue := make([]byte, 8)
+ for _, name := range []string{"tcpcounter", "tcpv6counter", "udpcounter", "udpv6counter"} {
+ err := m.UpdateElement(m.Map(name), unsafe.Pointer(&zeroKey[0]), unsafe.Pointer(&zeroValue[0]), 0)
+ if err != nil {
+ log.Error("eBPF could not init counters to zero: %v", err)
+ return err
+ }
+ }
+ ebpfCache = NewEbpfCache()
+
+ lock.Lock()
+ //determine host byte order
+ buf := [2]byte{}
+ *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD)
+ switch buf {
+ case [2]byte{0xCD, 0xAB}:
+ hostByteOrder = binary.LittleEndian
+ case [2]byte{0xAB, 0xCD}:
+ hostByteOrder = binary.BigEndian
+ default:
+ log.Error("Could not determine host byte order.")
+ }
+ lock.Unlock()
+
+ ebpfMaps = map[string]*ebpfMapsForProto{
+ "tcp": {
+ counterMap: m.Map("tcpcounter"),
+ bpfmap: m.Map("tcpMap")},
+ "tcp6": {
+ counterMap: m.Map("tcpv6counter"),
+ bpfmap: m.Map("tcpv6Map")},
+ "udp": {
+ counterMap: m.Map("udpcounter"),
+ bpfmap: m.Map("udpMap")},
+ "udp6": {
+ counterMap: m.Map("udpv6counter"),
+ bpfmap: m.Map("udpv6Map")},
+ }
+
+ saveEstablishedConnections(uint8(syscall.AF_INET))
+ if core.IPv6Enabled {
+ saveEstablishedConnections(uint8(syscall.AF_INET6))
+ }
+
+ go monitorCache()
+ go monitorMaps()
+ go monitorLocalAddresses()
+ go monitorAlreadyEstablished()
+ return nil
+}
+
+func saveEstablishedConnections(commDomain uint8) error {
+ // save already established connections
+ socketListTCP, err := daemonNetlink.SocketsDump(commDomain, uint8(syscall.IPPROTO_TCP))
+ if err != nil {
+ log.Debug("eBPF could not dump TCP (%d) sockets via netlink: %v", commDomain, err)
+ return err
+ }
+ for _, sock := range socketListTCP {
+ inode := int((*sock).INode)
+ pid := procmon.GetPIDFromINode(inode, fmt.Sprint(inode,
+ (*sock).ID.Source, (*sock).ID.SourcePort, (*sock).ID.Destination, (*sock).ID.DestinationPort))
+ alreadyEstablished.Lock()
+ alreadyEstablished.TCP[sock] = pid
+ alreadyEstablished.Unlock()
+ }
+
+ return nil
+}
+
+// Stop stops monitoring connections using kprobes
+func Stop() {
+ lock.Lock()
+ stop = true
+ lock.Unlock()
+ if m != nil {
+ m.Close()
+ }
+ ebpfCache.clear()
+}
+
+func isStopped() bool {
+ lock.RLock()
+ defer lock.RUnlock()
+
+ return stop
+}
+
+//make bpf() syscall with bpf_lookup prepared by the caller
+func makeBpfSyscall(bpf_lookup *bpf_lookup_elem_t) uintptr {
+ BPF_MAP_LOOKUP_ELEM := 1 //cmd number
+ syscall_BPF := 321 //syscall number
+ sizeOfStruct := 24 //sizeof bpf_lookup_elem_t struct
+
+ r1, _, _ := syscall.Syscall(uintptr(syscall_BPF), uintptr(BPF_MAP_LOOKUP_ELEM),
+ uintptr(unsafe.Pointer(bpf_lookup)), uintptr(sizeOfStruct))
+ return r1
+}
--- /dev/null
+package ebpf
+
+import (
+ "encoding/binary"
+ "fmt"
+ "net"
+ "unsafe"
+
+ daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink"
+)
+
+// we need to manually remove old connections from a bpf map
+
+// GetPid looks up process pid in a bpf map. If not found there, then it searches
+// already-established TCP connections.
+func GetPid(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (int, int, error) {
+ if hostByteOrder == nil {
+ return -1, -1, fmt.Errorf("eBPF monitoring method not initialized yet")
+ }
+
+ if pid, uid := getPidFromEbpf(proto, srcPort, srcIP, dstIP, dstPort); pid != -1 {
+ return pid, uid, nil
+ }
+ //check if it comes from already established TCP
+ if proto == "tcp" || proto == "tcp6" {
+ if pid, uid, err := findInAlreadyEstablishedTCP(proto, srcPort, srcIP, dstIP, dstPort); err == nil {
+ return pid, uid, nil
+ }
+ }
+ //using netlink.GetSocketInfo to check if UID is 0 (in-kernel connection)
+ if uid, _ := daemonNetlink.GetSocketInfo(proto, srcIP, srcPort, dstIP, dstPort); uid == 0 {
+ return -100, -100, nil
+ }
+ if !findAddressInLocalAddresses(srcIP) {
+ // systemd-resolved sometimes makes a TCP Fast Open connection to a DNS server (8.8.8.8 on my machine)
+ // and we get a packet here with **source** (not detination!!!) IP 8.8.8.8
+ // Maybe it's an in-kernel response with spoofed IP because wireshark does not show neither
+ // resolved's TCP Fast Open packet, nor the response
+ // Until this is better understood, we simply do not allow this machine to make connections with
+ // arbitrary source IPs
+ return -1, -1, fmt.Errorf("eBPF packet with unknown source IP: %s", srcIP)
+ }
+ return -1, -1, nil
+}
+
+// getPidFromEbpf looks up a connection in bpf map and returns PID if found
+// the lookup keys and values are defined in opensnitch.c , e.g.
+//
+// struct tcp_key_t {
+// u16 sport;
+// u32 daddr;
+// u16 dport;
+// u32 saddr;
+// }__attribute__((packed));
+
+// struct tcp_value_t{
+// u64 pid;
+// u64 uid;
+// u64 counter;
+// }__attribute__((packed));;
+
+func getPidFromEbpf(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (pid int, uid int) {
+ if hostByteOrder == nil {
+ return -1, -1
+ }
+ // Some connections, like broadcasts, are only seen in eBPF once,
+ // but some applications send 1 connection per network interface.
+ // If we delete the eBPF entry the first time we see it, we won't find
+ // the connection the next times.
+ delItemIfFound := true
+
+ var key []byte
+ var value []byte
+ var isIP4 bool = (proto == "tcp") || (proto == "udp") || (proto == "udplite")
+
+ if isIP4 {
+ key = make([]byte, 12)
+ value = make([]byte, 24)
+ copy(key[2:6], dstIP)
+ binary.BigEndian.PutUint16(key[6:8], uint16(dstPort))
+ copy(key[8:12], srcIP)
+ } else { // IPv6
+ key = make([]byte, 36)
+ value = make([]byte, 24)
+ copy(key[2:18], dstIP)
+ binary.BigEndian.PutUint16(key[18:20], uint16(dstPort))
+ copy(key[20:36], srcIP)
+ }
+ hostByteOrder.PutUint16(key[0:2], uint16(srcPort))
+
+ k := fmt.Sprint(proto, srcPort, srcIP.String(), dstIP.String(), dstPort)
+ cacheItem, isInCache := ebpfCache.isInCache(k)
+ if isInCache {
+ deleteEbpfEntry(proto, unsafe.Pointer(&key[0]))
+ return cacheItem.Pid, cacheItem.UID
+ }
+
+ err := m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value[0]))
+ if err != nil {
+ // key not found
+ // sometimes srcIP is 0.0.0.0. Happens especially with UDP sendto()
+ // for example: 57621:10.0.3.1 -> 10.0.3.255:57621 , reported as: 0.0.0.0 -> 10.0.3.255
+ if isIP4 {
+ zeroes := make([]byte, 4)
+ copy(key[8:12], zeroes)
+ } else {
+ zeroes := make([]byte, 16)
+ copy(key[20:36], zeroes)
+ }
+ err = m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value[0]))
+ if err == nil {
+ delItemIfFound = false
+ }
+ }
+ if err != nil && proto == "udp" && srcIP.String() == dstIP.String() {
+ // very rarely I see this connection. It has srcIP and dstIP == 0.0.0.0 in ebpf map
+ // it is a localhost to localhost connection
+ // srcIP was already set to 0, set dstIP to zero also
+ // TODO try to reproduce it and look for srcIP/dstIP in other kernel structures
+ zeroes := make([]byte, 4)
+ copy(key[2:6], zeroes)
+ err = m.LookupElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&key[0]), unsafe.Pointer(&value[0]))
+ }
+
+ if err != nil {
+ // key not found in bpf maps
+ return -1, -1
+ }
+ pid = int(hostByteOrder.Uint32(value[0:4]))
+ uid = int(hostByteOrder.Uint32(value[8:12]))
+
+ ebpfCache.addNewItem(k, key, pid, uid)
+ if delItemIfFound {
+ deleteEbpfEntry(proto, unsafe.Pointer(&key[0]))
+ }
+ return pid, uid
+}
+
+// FindInAlreadyEstablishedTCP searches those TCP connections which were already established at the time
+// when opensnitch started
+func findInAlreadyEstablishedTCP(proto string, srcPort uint, srcIP net.IP, dstIP net.IP, dstPort uint) (int, int, error) {
+ alreadyEstablished.RLock()
+ defer alreadyEstablished.RUnlock()
+
+ var _alreadyEstablished map[*daemonNetlink.Socket]int
+ if proto == "tcp" {
+ _alreadyEstablished = alreadyEstablished.TCP
+ } else if proto == "tcp6" {
+ _alreadyEstablished = alreadyEstablished.TCPv6
+ }
+
+ for sock, v := range _alreadyEstablished {
+ if (*sock).ID.SourcePort == uint16(srcPort) && (*sock).ID.Source.Equal(srcIP) &&
+ (*sock).ID.Destination.Equal(dstIP) && (*sock).ID.DestinationPort == uint16(dstPort) {
+ return v, int((*sock).UID), nil
+ }
+ }
+ return -1, -1, fmt.Errorf("eBPF inode not found")
+}
+
+//returns true if addr is in the list of this machine's addresses
+func findAddressInLocalAddresses(addr net.IP) bool {
+ lock.Lock()
+ defer lock.Unlock()
+ for _, a := range localAddresses {
+ if addr.String() == a.String() {
+ return true
+ }
+ }
+ return false
+}
--- /dev/null
+package ebpf
+
+import (
+ "syscall"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ daemonNetlink "github.com/evilsocket/opensnitch/daemon/netlink"
+ "github.com/vishvananda/netlink"
+)
+
+// we need to manually remove old connections from a bpf map
+// since when a bpf map is full it doesn't allow any more insertions
+func monitorMaps() {
+ for {
+ if isStopped() {
+ return
+ }
+ time.Sleep(time.Second * 5)
+ for name := range ebpfMaps {
+ // using a pointer to the map doesn't delete the items.
+ // bpftool still counts them.
+ if items := getItems(name, name == "tcp6" || name == "udp6"); items > 500 {
+ deleted := deleteOldItems(name, name == "tcp6" || name == "udp6", items/2)
+ log.Debug("[ebpf] old items deleted: %d", deleted)
+ }
+ }
+ }
+}
+
+func monitorCache() {
+ for {
+ select {
+ case <-ebpfCacheTicker.C:
+ if isStopped() {
+ return
+ }
+ ebpfCache.DeleteOldItems()
+ }
+ }
+}
+
+// maintains a list of this machine's local addresses
+// TODO: use netlink.AddrSubscribeWithOptions()
+func monitorLocalAddresses() {
+ for {
+ addr, err := netlink.AddrList(nil, netlink.FAMILY_ALL)
+ if err != nil {
+ log.Error("eBPF error looking up this machine's addresses via netlink: %v", err)
+ continue
+ }
+ lock.Lock()
+ localAddresses = nil
+ for _, a := range addr {
+ localAddresses = append(localAddresses, a.IP)
+ }
+ lock.Unlock()
+ time.Sleep(time.Second * 1)
+ if isStopped() {
+ return
+ }
+ }
+}
+
+// monitorAlreadyEstablished makes sure that when an already-established connection is closed
+// it will be removed from alreadyEstablished. If we don't do this and keep the alreadyEstablished entry forever,
+// then after the genuine process quits,a malicious process may reuse PID-srcPort-srcIP-dstPort-dstIP
+func monitorAlreadyEstablished() {
+ for {
+ time.Sleep(time.Second * 1)
+ if isStopped() {
+ return
+ }
+ socketListTCP, err := daemonNetlink.SocketsDump(uint8(syscall.AF_INET), uint8(syscall.IPPROTO_TCP))
+ if err != nil {
+ log.Debug("eBPF error in dumping TCP sockets via netlink")
+ continue
+ }
+ alreadyEstablished.Lock()
+ for aesock := range alreadyEstablished.TCP {
+ found := false
+ for _, sock := range socketListTCP {
+ if socketsAreEqual(aesock, sock) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ delete(alreadyEstablished.TCP, aesock)
+ }
+ }
+ alreadyEstablished.Unlock()
+
+ if core.IPv6Enabled {
+ socketListTCPv6, err := daemonNetlink.SocketsDump(uint8(syscall.AF_INET6), uint8(syscall.IPPROTO_TCP))
+ if err != nil {
+ log.Debug("eBPF error in dumping TCPv6 sockets via netlink: %s", err)
+ continue
+ }
+ alreadyEstablished.Lock()
+ for aesock := range alreadyEstablished.TCPv6 {
+ found := false
+ for _, sock := range socketListTCPv6 {
+ if socketsAreEqual(aesock, sock) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ delete(alreadyEstablished.TCPv6, aesock)
+ }
+ }
+ alreadyEstablished.Unlock()
+ }
+ }
+}
+
+func socketsAreEqual(aSocket, bSocket *daemonNetlink.Socket) bool {
+ return ((*aSocket).INode == (*bSocket).INode &&
+ //inodes are unique enough, so the matches below will never have to be checked
+ (*aSocket).ID.SourcePort == (*bSocket).ID.SourcePort &&
+ (*aSocket).ID.Source.Equal((*bSocket).ID.Source) &&
+ (*aSocket).ID.Destination.Equal((*bSocket).ID.Destination) &&
+ (*aSocket).ID.DestinationPort == (*bSocket).ID.DestinationPort &&
+ (*aSocket).UID == (*bSocket).UID)
+}
--- /dev/null
+package ebpf
+
+import (
+ "fmt"
+ "unsafe"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+func mountDebugFS() error {
+ debugfsPath := "/sys/kernel/debug/"
+ kprobesPath := fmt.Sprint(debugfsPath, "tracing/kprobe_events")
+ if core.Exists(kprobesPath) == false {
+ if _, err := core.Exec("mount", []string{"-t", "debugfs", "none", debugfsPath}); err != nil {
+ log.Warning("eBPF debugfs error: %s", err)
+ return err
+ }
+ }
+
+ return nil
+}
+
+func deleteEbpfEntry(proto string, key unsafe.Pointer) bool {
+ if err := m.DeleteElement(ebpfMaps[proto].bpfmap, key); err != nil {
+ return false
+ }
+ return true
+}
+
+func getItems(proto string, isIPv6 bool) (items uint) {
+ isDup := make(map[string]uint8)
+ var lookupKey []byte
+ var nextKey []byte
+ var value []byte
+ if !isIPv6 {
+ lookupKey = make([]byte, 12)
+ nextKey = make([]byte, 12)
+ } else {
+ lookupKey = make([]byte, 36)
+ nextKey = make([]byte, 36)
+ }
+ value = make([]byte, 24)
+ firstrun := true
+
+ for {
+ ok, err := m.LookupNextElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&lookupKey[0]),
+ unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value[0]))
+ if !ok || err != nil { //reached end of map
+ log.Debug("[ebpf] %s map: %d active items", proto, items)
+ return
+ }
+ if firstrun {
+ // on first run lookupKey is a dummy, nothing to delete
+ firstrun = false
+ copy(lookupKey, nextKey)
+ continue
+ }
+ if counter, duped := isDup[string(lookupKey)]; duped && counter > 1 {
+ deleteEbpfEntry(proto, unsafe.Pointer(&lookupKey[0]))
+ continue
+ }
+ isDup[string(lookupKey)]++
+ copy(lookupKey, nextKey)
+ items++
+ }
+
+ return items
+}
+
+// deleteOldItems deletes maps' elements in order to keep them below maximum capacity.
+// If ebpf maps are full they don't allow any more insertions, ending up lossing events.
+func deleteOldItems(proto string, isIPv6 bool, maxToDelete uint) (deleted uint) {
+ isDup := make(map[string]uint8)
+ var lookupKey []byte
+ var nextKey []byte
+ var value []byte
+ if !isIPv6 {
+ lookupKey = make([]byte, 12)
+ nextKey = make([]byte, 12)
+ } else {
+ lookupKey = make([]byte, 36)
+ nextKey = make([]byte, 36)
+ }
+ value = make([]byte, 24)
+ firstrun := true
+ i := uint(0)
+
+ for {
+ i++
+ if i > maxToDelete {
+ return
+ }
+ ok, err := m.LookupNextElement(ebpfMaps[proto].bpfmap, unsafe.Pointer(&lookupKey[0]),
+ unsafe.Pointer(&nextKey[0]), unsafe.Pointer(&value[0]))
+ if !ok || err != nil { //reached end of map
+ return
+ }
+ if counter, duped := isDup[string(lookupKey)]; duped && counter > 1 {
+ if deleteEbpfEntry(proto, unsafe.Pointer(&lookupKey[0])) {
+ deleted++
+ copy(lookupKey, nextKey)
+ continue
+ }
+ return
+ }
+
+ if firstrun {
+ // on first run lookupKey is a dummy, nothing to delete
+ firstrun = false
+ copy(lookupKey, nextKey)
+ continue
+ }
+
+ if !deleteEbpfEntry(proto, unsafe.Pointer(&lookupKey[0])) {
+ return
+ }
+ deleted++
+ isDup[string(lookupKey)]++
+ copy(lookupKey, nextKey)
+ }
+
+ return
+}
--- /dev/null
+package procmon
+
+import (
+ "fmt"
+ "os"
+ "sort"
+ "strconv"
+)
+
+func sortPidsByTime(fdList []os.FileInfo) []os.FileInfo {
+ sort.Slice(fdList, func(i, j int) bool {
+ t := fdList[i].ModTime().UnixNano()
+ u := fdList[j].ModTime().UnixNano()
+ return t > u
+ })
+ return fdList
+}
+
+// inodeFound searches for the given inode in /proc/<pid>/fd/ or
+// /proc/<pid>/task/<tid>/fd/ and gets the symbolink link it points to,
+// in order to compare it against the given inode.
+//
+// If the inode is found, the cache is updated ans sorted.
+func inodeFound(pidsPath, expect, inodeKey string, inode, pid int) bool {
+ fdPath := fmt.Sprint(pidsPath, pid, "/fd/")
+ fdList := lookupPidDescriptors(fdPath, pid)
+ if fdList == nil {
+ return false
+ }
+
+ for idx := 0; idx < len(fdList); idx++ {
+ descLink := fmt.Sprint(fdPath, fdList[idx])
+ if link, err := os.Readlink(descLink); err == nil && link == expect {
+ inodesCache.add(inodeKey, descLink, pid)
+ pidsCache.add(fdPath, fdList, pid)
+ return true
+ }
+ }
+
+ return false
+}
+
+// lookupPidInProc searches for an inode in /proc.
+// First it gets the running PIDs and obtains the opened sockets.
+// TODO: If the inode is not found, search again in the task/threads
+// of every PID (costly).
+func lookupPidInProc(pidsPath, expect, inodeKey string, inode int) int {
+ pidList := getProcPids(pidsPath)
+ for _, pid := range pidList {
+ if inodeFound(pidsPath, expect, inodeKey, inode, pid) {
+ return pid
+ }
+ }
+ return -1
+}
+
+// lookupPidDescriptors returns the list of descriptors inside
+// /proc/<pid>/fd/
+// TODO: search in /proc/<pid>/task/<tid>/fd/ .
+func lookupPidDescriptors(fdPath string, pid int) []string {
+ f, err := os.Open(fdPath)
+ if err != nil {
+ return nil
+ }
+ // This is where most of the time is wasted when looking for PIDs.
+ // long running processes like firefox/chrome tend to have a lot of descriptor
+ // references that points to non existent files on disk, but that remains in
+ // memory (those with " (deleted)").
+ // This causes to have to iterate over 300 to 700 items, that are not sockets.
+ fdList, err := f.Readdir(-1)
+ f.Close()
+ if err != nil {
+ return nil
+ }
+ fdList = sortPidsByTime(fdList)
+
+ s := make([]string, len(fdList))
+ for n, f := range fdList {
+ s[n] = f.Name()
+ }
+
+ return s
+}
+
+// getProcPids returns the list of running PIDs, /proc or /proc/<pid>/task/ .
+func getProcPids(pidsPath string) (pidList []int) {
+ f, err := os.Open(pidsPath)
+ if err != nil {
+ return pidList
+ }
+ ls, err := f.Readdir(-1)
+ f.Close()
+ if err != nil {
+ return pidList
+ }
+ ls = sortPidsByTime(ls)
+
+ for _, f := range ls {
+ if f.IsDir() == false {
+ continue
+ }
+ if pid, err := strconv.Atoi(f.Name()); err == nil {
+ pidList = append(pidList, []int{pid}...)
+ }
+ }
+
+ return pidList
+}
--- /dev/null
+package procmon
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestGetProcPids(t *testing.T) {
+ pids := getProcPids("/proc")
+
+ if len(pids) == 0 {
+ t.Error("getProcPids() should not be 0", pids)
+ }
+}
+
+func TestLookupPidDescriptors(t *testing.T) {
+ pidsFd := lookupPidDescriptors(fmt.Sprint("/proc/", myPid, "/fd/"), myPid)
+ if len(pidsFd) == 0 {
+ t.Error("getProcPids() should not be 0", pidsFd)
+ }
+}
+
+func TestLookupPidInProc(t *testing.T) {
+ // we expect that the inode 1 points to /dev/null
+ expect := "/dev/null"
+ foundPid := lookupPidInProc("/proc/", expect, "", myPid)
+ if foundPid == -1 {
+ t.Error("lookupPidInProc() should not return -1")
+ }
+}
+
+func BenchmarkGetProcs(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ getProcPids("/proc")
+ }
+}
+
+func BenchmarkLookupPidDescriptors(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ lookupPidDescriptors(fmt.Sprint("/proc/", myPid, "/fd/"), myPid)
+ }
+}
--- /dev/null
+package monitor
+
+import (
+ "net"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/evilsocket/opensnitch/daemon/procmon"
+ "github.com/evilsocket/opensnitch/daemon/procmon/audit"
+ "github.com/evilsocket/opensnitch/daemon/procmon/ebpf"
+)
+
+var (
+ cacheMonitorsRunning = false
+)
+
+// ReconfigureMonitorMethod configures a new method for parsing connections.
+func ReconfigureMonitorMethod(newMonitorMethod string) error {
+
+ if procmon.GetMonitorMethod() == newMonitorMethod {
+ return nil
+ }
+
+ oldMethod := procmon.GetMonitorMethod()
+ End()
+ procmon.SetMonitorMethod(newMonitorMethod)
+ // if the new monitor method fails to start, rollback the change and exit
+ // without saving the configuration. Otherwise we can end up with the wrong
+ // monitor method configured and saved to file.
+ if err := Init(); err != nil {
+ procmon.SetMonitorMethod(oldMethod)
+ return err
+ }
+
+ return nil
+}
+
+// End stops the way of parsing new connections.
+func End() {
+ if procmon.MethodIsAudit() {
+ audit.Stop()
+ } else if procmon.MethodIsEbpf() {
+ ebpf.Stop()
+ }
+}
+
+// Init starts parsing connections using the method specified.
+func Init() (err error) {
+ if cacheMonitorsRunning == false {
+ go procmon.MonitorActivePids()
+ go procmon.CacheCleanerTask()
+ cacheMonitorsRunning = true
+ }
+
+ if procmon.MethodIsEbpf() {
+ err = ebpf.Start()
+ if err == nil {
+ log.Info("Process monitor method ebpf")
+ return nil
+ }
+ // we need to stop this method even if it has failed to start, in order to clean up the kprobes
+ // It helps with the error "cannot write...kprobe_events: file exists".
+ ebpf.Stop()
+ log.Warning("error starting ebpf monitor method: %v", err)
+ } else if procmon.MethodIsAudit() {
+ var auditConn net.Conn
+ auditConn, err = audit.Start()
+ if err == nil {
+ log.Info("Process monitor method audit")
+ go audit.Reader(auditConn, (chan<- audit.Event)(audit.EventChan))
+ return nil
+ }
+ log.Warning("error starting audit monitor method: %v", err)
+ }
+
+ // if any of the above methods have failed, fallback to proc
+ log.Info("Process monitor method /proc")
+ procmon.SetMonitorMethod(procmon.MethodProc)
+ return err
+}
--- /dev/null
+package procmon
+
+import (
+ "fmt"
+ "os"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/evilsocket/opensnitch/daemon/procmon/audit"
+)
+
+func getPIDFromAuditEvents(inode int, inodeKey string, expect string) (int, int) {
+ audit.Lock.RLock()
+ defer audit.Lock.RUnlock()
+
+ auditEvents := audit.GetEvents()
+ for n := 0; n < len(auditEvents); n++ {
+ pid := auditEvents[n].Pid
+ if inodeFound("/proc/", expect, inodeKey, inode, pid) {
+ return pid, n
+ }
+ }
+ for n := 0; n < len(auditEvents); n++ {
+ ppid := auditEvents[n].PPid
+ if inodeFound("/proc/", expect, inodeKey, inode, ppid) {
+ return ppid, n
+ }
+ }
+ return -1, -1
+}
+
+// GetPIDFromINode tries to get the PID from a socket inode following these steps:
+// 1. Get the PID from the cache of Inodes.
+// 2. Get the PID from the cache of PIDs.
+// 3. Look for the PID using one of these methods:
+// - audit: listening for socket creation from auditd.
+// - proc: search /proc
+//
+// If the PID is not found by one of the 2 first methods, it'll try it using /proc.
+func GetPIDFromINode(inode int, inodeKey string) int {
+ found := -1
+ if inode <= 0 {
+ return found
+ }
+ start := time.Now()
+
+ expect := fmt.Sprintf("socket:[%d]", inode)
+ if cachedPidInode := inodesCache.getPid(inodeKey); cachedPidInode != -1 {
+ log.Debug("Inode found in cache: %v %v %v %v", time.Since(start), inodesCache.getPid(inodeKey), inode, inodeKey)
+ return cachedPidInode
+ }
+
+ cachedPid, pos := pidsCache.getPid(inode, inodeKey, expect)
+ if cachedPid != -1 {
+ log.Debug("Socket found in known pids %v, pid: %d, inode: %d, pos: %d, pids in cache: %d", time.Since(start), cachedPid, inode, pos, pidsCache.countItems())
+ pidsCache.sort(cachedPid)
+ inodesCache.add(inodeKey, "", cachedPid)
+ return cachedPid
+ }
+
+ if MethodIsAudit() {
+ if aPid, pos := getPIDFromAuditEvents(inode, inodeKey, expect); aPid != -1 {
+ log.Debug("PID found via audit events: %v, position: %d", time.Since(start), pos)
+ return aPid
+ }
+ }
+ if found == -1 || methodIsProc() {
+ found = lookupPidInProc("/proc/", expect, inodeKey, inode)
+ }
+ log.Debug("new pid lookup took (%d): %v", found, time.Since(start))
+
+ return found
+}
+
+// FindProcess checks if a process exists given a PID.
+// If it exists in /proc, a new Process{} object is returned with the details
+// to identify a process (cmdline, name, environment variables, etc).
+func FindProcess(pid int, interceptUnknown bool) *Process {
+ if interceptUnknown && pid < 0 {
+ return NewProcess(0, "")
+ }
+
+ if proc := findProcessInActivePidsCache(uint64(pid)); proc != nil {
+ return proc
+ }
+
+ if MethodIsAudit() {
+ if aevent := audit.GetEventByPid(pid); aevent != nil {
+ audit.Lock.RLock()
+ proc := NewProcess(pid, aevent.ProcPath)
+ proc.readCmdline()
+ proc.setCwd(aevent.ProcDir)
+ audit.Lock.RUnlock()
+ // if the proc dir contains non alhpa-numeric chars the field is empty
+ if proc.CWD == "" {
+ proc.readCwd()
+ }
+ proc.readEnv()
+ proc.cleanPath()
+
+ addToActivePidsCache(uint64(pid), proc)
+ return proc
+ }
+ }
+ // if the PID dir doesn't exist, the process may have exited or be a kernel connection
+ // XXX: can a kernel connection exist without an entry in ProcFS?
+ if core.Exists(fmt.Sprint("/proc/", pid)) == false {
+ log.Debug("PID can't be read /proc/ %d", pid)
+ return nil
+ }
+
+ linkName := fmt.Sprint("/proc/", pid, "/exe")
+ link, err := os.Readlink(linkName)
+ proc := NewProcess(pid, link)
+ proc.readCmdline()
+ proc.readCwd()
+ proc.readEnv()
+ proc.cleanPath()
+
+ if len(proc.Args) == 0 {
+ proc.readComm()
+ proc.Args = make([]string, 0)
+ proc.Args = append(proc.Args, proc.Comm)
+ }
+
+ // If the link to the binary can't be read, the PID may be of a kernel task
+ if err != nil || proc.Path == "" {
+ proc.Path = "Kernel connection"
+ }
+
+ addToActivePidsCache(uint64(pid), proc)
+ return proc
+}
--- /dev/null
+package procmon
+
+import (
+ "sync"
+ "time"
+)
+
+var (
+ cacheMonitorsRunning = false
+ lock = sync.RWMutex{}
+ monitorMethod = MethodProc
+)
+
+// monitor method supported types
+const (
+ MethodProc = "proc"
+ MethodAudit = "audit"
+ MethodEbpf = "ebpf"
+)
+
+// man 5 proc; man procfs
+type procIOstats struct {
+ RChar int64
+ WChar int64
+ SyscallRead int64
+ SyscallWrite int64
+ ReadBytes int64
+ WriteBytes int64
+}
+
+type procDescriptors struct {
+ Name string
+ SymLink string
+ Size int64
+ ModTime time.Time
+}
+
+type procStatm struct {
+ Size int64
+ Resident int64
+ Shared int64
+ Text int64
+ Lib int64
+ Data int64 // data + stack
+ Dt int
+}
+
+// Process holds the details of a process.
+type Process struct {
+ ID int
+ Comm string
+ Path string
+ Args []string
+ Env map[string]string
+ CWD string
+ Descriptors []*procDescriptors
+ IOStats *procIOstats
+ Status string
+ Stat string
+ Statm *procStatm
+ Stack string
+ Maps string
+}
+
+// NewProcess returns a new Process structure.
+func NewProcess(pid int, path string) *Process {
+ return &Process{
+ ID: pid,
+ Path: path,
+ Args: make([]string, 0),
+ Env: make(map[string]string),
+ }
+}
+
+// SetMonitorMethod configures a new method for parsing connections.
+func SetMonitorMethod(newMonitorMethod string) {
+ lock.Lock()
+ defer lock.Unlock()
+
+ monitorMethod = newMonitorMethod
+}
+
+// GetMonitorMethod configures a new method for parsing connections.
+func GetMonitorMethod() string {
+ lock.Lock()
+ defer lock.Unlock()
+
+ return monitorMethod
+}
+
+// MethodIsEbpf returns if the process monitor method is eBPF.
+func MethodIsEbpf() bool {
+ lock.RLock()
+ defer lock.RUnlock()
+
+ return monitorMethod == MethodEbpf
+}
+
+// MethodIsAudit returns if the process monitor method is eBPF.
+func MethodIsAudit() bool {
+ lock.RLock()
+ defer lock.RUnlock()
+
+ return monitorMethod == MethodAudit
+}
+
+func methodIsProc() bool {
+ lock.RLock()
+ defer lock.RUnlock()
+
+ return monitorMethod == MethodProc
+}
--- /dev/null
+package procmon
+
+import (
+ "os"
+ "testing"
+)
+
+var (
+ myPid = os.Getpid()
+ proc = NewProcess(myPid, "/fake/path")
+)
+
+func TestNewProcess(t *testing.T) {
+ if proc.ID != myPid {
+ t.Error("NewProcess PID not equal to ", myPid)
+ }
+ if proc.Path != "/fake/path" {
+ t.Error("NewProcess path not equal to /fake/path")
+ }
+}
+
+func TestProcPath(t *testing.T) {
+ if err := proc.readPath(); err != nil {
+ t.Error("Proc path error:", err)
+ }
+ if proc.Path == "/fake/path" {
+ t.Error("Proc path equal to /fake/path, should be different:", proc.Path)
+ }
+}
+
+func TestProcCwd(t *testing.T) {
+ err := proc.readCwd()
+
+ if proc.CWD == "" {
+ t.Error("Proc readCwd() not read:", err)
+ }
+
+ proc.setCwd("/home")
+ if proc.CWD != "/home" {
+ t.Error("Proc setCwd() should be /home:", proc.CWD)
+ }
+}
+
+func TestProcCmdline(t *testing.T) {
+ proc.readCmdline()
+
+ if len(proc.Args) == 0 {
+ t.Error("Proc Args should not be empty:", proc.Args)
+ }
+}
+
+func TestProcDescriptors(t *testing.T) {
+ proc.readDescriptors()
+
+ if len(proc.Descriptors) == 0 {
+ t.Error("Proc Descriptors should not be empty:", proc.Descriptors)
+ }
+}
+
+func TestProcEnv(t *testing.T) {
+ proc.readEnv()
+
+ if len(proc.Env) == 0 {
+ t.Error("Proc Env should not be empty:", proc.Env)
+ }
+}
+
+func TestProcIOStats(t *testing.T) {
+ proc.readIOStats()
+
+ if proc.IOStats.RChar == 0 {
+ t.Error("Proc.IOStats.RChar should not be 0:", proc.IOStats)
+ }
+ if proc.IOStats.WChar == 0 {
+ t.Error("Proc.IOStats.WChar should not be 0:", proc.IOStats)
+ }
+ if proc.IOStats.SyscallRead == 0 {
+ t.Error("Proc.IOStats.SyscallRead should not be 0:", proc.IOStats)
+ }
+ if proc.IOStats.SyscallWrite == 0 {
+ t.Error("Proc.IOStats.SyscallWrite should not be 0:", proc.IOStats)
+ }
+ /*if proc.IOStats.ReadBytes == 0 {
+ t.Error("Proc.IOStats.ReadBytes should not be 0:", proc.IOStats)
+ }
+ if proc.IOStats.WriteBytes == 0 {
+ t.Error("Proc.IOStats.WriteBytes should not be 0:", proc.IOStats)
+ }*/
+}
+
+func TestProcStatus(t *testing.T) {
+ proc.readStatus()
+
+ if proc.Status == "" {
+ t.Error("Proc Status should not be empty:", proc)
+ }
+ if proc.Stat == "" {
+ t.Error("Proc Stat should not be empty:", proc)
+ }
+ /*if proc.Stack == "" {
+ t.Error("Proc Stack should not be empty:", proc)
+ }*/
+ if proc.Maps == "" {
+ t.Error("Proc Maps should not be empty:", proc)
+ }
+ if proc.Statm.Size == 0 {
+ t.Error("Proc Statm Size should not be 0:", proc.Statm)
+ }
+ if proc.Statm.Resident == 0 {
+ t.Error("Proc Statm Resident should not be 0:", proc.Statm)
+ }
+ if proc.Statm.Shared == 0 {
+ t.Error("Proc Statm Shared should not be 0:", proc.Statm)
+ }
+ if proc.Statm.Text == 0 {
+ t.Error("Proc Statm Text should not be 0:", proc.Statm)
+ }
+ if proc.Statm.Lib != 0 {
+ t.Error("Proc Statm Lib should not be 0:", proc.Statm)
+ }
+ if proc.Statm.Data == 0 {
+ t.Error("Proc Statm Data should not be 0:", proc.Statm)
+ }
+ if proc.Statm.Dt != 0 {
+ t.Error("Proc Statm Dt should not be 0:", proc.Statm)
+ }
+}
+
+func TestProcCleanPath(t *testing.T) {
+ proc.Path = "/fake/path/binary (deleted)"
+ proc.cleanPath()
+ if proc.Path != "/fake/path/binary" {
+ t.Error("Proc cleanPath() not cleaned:", proc.Path)
+ }
+}
--- /dev/null
+package rule
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "path/filepath"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/conman"
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+
+ "github.com/fsnotify/fsnotify"
+)
+
+// Loader is the object that holds the rules loaded from disk, as well as the
+// rules watcher.
+type Loader struct {
+ sync.RWMutex
+ path string
+ rules map[string]*Rule
+ rulesKeys []string
+ watcher *fsnotify.Watcher
+ liveReload bool
+ liveReloadRunning bool
+}
+
+// NewLoader loads rules from disk, and watches for changes made to the rules files
+// on disk.
+func NewLoader(liveReload bool) (*Loader, error) {
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ return nil, err
+ }
+ return &Loader{
+ path: "",
+ rules: make(map[string]*Rule),
+ liveReload: liveReload,
+ watcher: watcher,
+ liveReloadRunning: false,
+ }, nil
+}
+
+// NumRules returns he number of loaded rules.
+func (l *Loader) NumRules() int {
+ l.RLock()
+ defer l.RUnlock()
+ return len(l.rules)
+}
+
+// GetAll returns the loaded rules.
+func (l *Loader) GetAll() map[string]*Rule {
+ l.RLock()
+ defer l.RUnlock()
+ return l.rules
+}
+
+// Load loads rules files from disk.
+func (l *Loader) Load(path string) error {
+ if core.Exists(path) == false {
+ return fmt.Errorf("Path '%s' does not exist", path)
+ }
+
+ expr := filepath.Join(path, "*.json")
+ matches, err := filepath.Glob(expr)
+ if err != nil {
+ return fmt.Errorf("Error globbing '%s': %s", expr, err)
+ }
+
+ l.path = path
+ if len(l.rules) == 0 {
+ l.rules = make(map[string]*Rule)
+ }
+
+ for _, fileName := range matches {
+ log.Debug("Reading rule from %s", fileName)
+
+ if err := l.loadRule(fileName); err != nil {
+ log.Warning("%s", err)
+ continue
+ }
+ }
+
+ if l.liveReload && l.liveReloadRunning == false {
+ go l.liveReloadWorker()
+ }
+
+ return nil
+}
+
+func (l *Loader) loadRule(fileName string) error {
+ raw, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ return fmt.Errorf("Error while reading %s: %s", fileName, err)
+ }
+ l.Lock()
+ defer l.Unlock()
+
+ var r Rule
+ err = json.Unmarshal(raw, &r)
+ if err != nil {
+ return fmt.Errorf("Error parsing rule from %s: %s", fileName, err)
+ }
+ raw = nil
+
+ if oldRule, found := l.rules[r.Name]; found {
+ l.cleanListsRule(oldRule)
+ }
+
+ if r.Enabled {
+ if err := r.Operator.Compile(); err != nil {
+ log.Warning("Operator.Compile() error: %s: %s", err, r.Operator.Data)
+ return fmt.Errorf("(1) Error compiling rule: %s", err)
+ }
+ if r.Operator.Type == List {
+ for i := 0; i < len(r.Operator.List); i++ {
+ if err := r.Operator.List[i].Compile(); err != nil {
+ log.Warning("Operator.Compile() error: %s: ", err)
+ return fmt.Errorf("(1) Error compiling list rule: %s", err)
+ }
+ }
+ }
+ }
+ if oldRule, found := l.rules[r.Name]; found {
+ l.deleteOldRuleFromDisk(oldRule, &r)
+ }
+
+ log.Debug("Loaded rule from %s: %s", fileName, r.String())
+ l.rules[r.Name] = &r
+ l.sortRules()
+
+ if l.isTemporary(&r) {
+ err = l.scheduleTemporaryRule(r)
+ }
+
+ return nil
+}
+
+// deleteRule deletes a rule from memory if it has been deleted from disk.
+// This is only called if fsnotify's Remove event is fired, thus it doesn't
+// have to delete temporary rules (!Always).
+func (l *Loader) deleteRule(filePath string) {
+ fileName := filepath.Base(filePath)
+ ruleName := fileName[:len(fileName)-5]
+
+ l.RLock()
+ rule, found := l.rules[ruleName]
+ delRule := found && rule.Duration == Always
+ l.RUnlock()
+ if delRule {
+ l.Delete(ruleName)
+ }
+}
+
+func (l *Loader) deleteRuleFromDisk(ruleName string) error {
+ path := fmt.Sprint(l.path, "/", ruleName, ".json")
+ return os.Remove(path)
+}
+
+// deleteOldRuleFromDisk deletes a rule from disk if the Duration changes
+// from Always (saved on disk), to !Always (temporary).
+func (l *Loader) deleteOldRuleFromDisk(oldRule, newRule *Rule) {
+ if oldRule.Duration == Always && newRule.Duration != Always {
+ if err := l.deleteRuleFromDisk(oldRule.Name); err != nil {
+ log.Error("Error deleting old rule from disk: %s", oldRule.Name)
+ }
+ }
+}
+
+// cleanListsRule erases the list of domains of an Operator of type Lists
+func (l *Loader) cleanListsRule(oldRule *Rule) {
+ if oldRule.Operator.Type == Lists {
+ oldRule.Operator.StopMonitoringLists()
+ } else if oldRule.Operator.Type == List {
+ for i := 0; i < len(oldRule.Operator.List); i++ {
+ if oldRule.Operator.List[i].Type == Lists {
+ oldRule.Operator.List[i].StopMonitoringLists()
+ break
+ }
+ }
+ }
+}
+
+func (l *Loader) liveReloadWorker() {
+ l.liveReloadRunning = true
+
+ log.Debug("Rules watcher started on path %s ...", l.path)
+ if err := l.watcher.Add(l.path); err != nil {
+ log.Error("Could not watch path: %s", err)
+ l.liveReloadRunning = false
+ return
+ }
+
+ for {
+ select {
+ case event := <-l.watcher.Events:
+ // a new rule json file has been created or updated
+ if event.Op&fsnotify.Write == fsnotify.Write {
+ if strings.HasSuffix(event.Name, ".json") {
+ log.Important("Ruleset changed due to %s, reloading ...", path.Base(event.Name))
+ if err := l.loadRule(event.Name); err != nil {
+ log.Warning("%s", err)
+ }
+ }
+ } else if event.Op&fsnotify.Remove == fsnotify.Remove {
+ if strings.HasSuffix(event.Name, ".json") {
+ log.Important("Rule deleted %s", path.Base(event.Name))
+ // we only need to delete from memory rules of type Always,
+ // because the Remove event is of a file, i.e.: Duration == Always
+ l.deleteRule(event.Name)
+ }
+ }
+ case err := <-l.watcher.Errors:
+ log.Error("File system watcher error: %s", err)
+ }
+ }
+}
+
+func (l *Loader) isTemporary(r *Rule) bool {
+ return r.Duration != Restart && r.Duration != Always && r.Duration != Once
+}
+
+func (l *Loader) isUniqueName(name string) bool {
+ _, found := l.rules[name]
+ return !found
+}
+
+func (l *Loader) setUniqueName(rule *Rule) {
+ l.Lock()
+ defer l.Unlock()
+
+ idx := 1
+ base := rule.Name
+ for l.isUniqueName(rule.Name) == false {
+ idx++
+ rule.Name = fmt.Sprintf("%s-%d", base, idx)
+ }
+}
+
+func (l *Loader) sortRules() {
+ l.rulesKeys = make([]string, 0, len(l.rules))
+ for k := range l.rules {
+ l.rulesKeys = append(l.rulesKeys, k)
+ }
+ sort.Strings(l.rulesKeys)
+}
+
+func (l *Loader) addUserRule(rule *Rule) {
+ if rule.Duration == Once {
+ return
+ }
+
+ l.setUniqueName(rule)
+ l.replaceUserRule(rule)
+}
+
+func (l *Loader) replaceUserRule(rule *Rule) (err error) {
+ l.Lock()
+ oldRule, found := l.rules[rule.Name]
+ l.Unlock()
+
+ if found {
+ // If the rule has changed from Always (saved on disk) to !Always (temporary),
+ // we need to delete the rule from disk and keep it in memory.
+ l.deleteOldRuleFromDisk(oldRule, rule)
+
+ // delete loaded lists, if this is a rule of type Lists
+ l.cleanListsRule(oldRule)
+ }
+
+ if rule.Enabled {
+ if err := rule.Operator.Compile(); err != nil {
+ log.Warning("Operator.Compile() error: %s: %s", err, rule.Operator.Data)
+ return fmt.Errorf("(2) Error compiling rule: %s", err)
+ }
+
+ if rule.Operator.Type == List {
+ // TODO: use List protobuf object instead of un/marshalling to/from json
+ if err = json.Unmarshal([]byte(rule.Operator.Data), &rule.Operator.List); err != nil {
+ return fmt.Errorf("Error loading rule of type list: %s", err)
+ }
+
+ for i := 0; i < len(rule.Operator.List); i++ {
+ if err := rule.Operator.List[i].Compile(); err != nil {
+ log.Warning("Operator.Compile() error: %s: ", err)
+ return fmt.Errorf("(2) Error compiling list rule: %s", err)
+ }
+ }
+ }
+ }
+ l.Lock()
+ l.rules[rule.Name] = rule
+ l.sortRules()
+ l.Unlock()
+
+ if l.isTemporary(rule) {
+ err = l.scheduleTemporaryRule(*rule)
+ }
+
+ return err
+}
+
+func (l *Loader) scheduleTemporaryRule(rule Rule) error {
+ tTime, err := time.ParseDuration(string(rule.Duration))
+ if err != nil {
+ return err
+ }
+
+ time.AfterFunc(tTime, func() {
+ l.Lock()
+ defer l.Unlock()
+
+ log.Info("Temporary rule expired: %s - %s", rule.Name, rule.Duration)
+ if newRule, found := l.rules[rule.Name]; found {
+ if newRule.Duration != rule.Duration {
+ log.Debug("%s temporary rule expired, but has new Duration, old: %s, new: %s", rule.Name, rule.Duration, newRule.Duration)
+ return
+ }
+ delete(l.rules, rule.Name)
+ l.sortRules()
+ }
+ })
+ return nil
+}
+
+// Add adds a rule to the list of rules, and optionally saves it to disk.
+func (l *Loader) Add(rule *Rule, saveToDisk bool) error {
+ l.addUserRule(rule)
+ if saveToDisk {
+ fileName := filepath.Join(l.path, fmt.Sprintf("%s.json", rule.Name))
+ return l.Save(rule, fileName)
+ }
+ return nil
+}
+
+// Replace adds a rule to the list of rules, and optionally saves it to disk.
+func (l *Loader) Replace(rule *Rule, saveToDisk bool) error {
+ if err := l.replaceUserRule(rule); err != nil {
+ return err
+ }
+ if saveToDisk {
+ l.Lock()
+ defer l.Unlock()
+
+ fileName := filepath.Join(l.path, fmt.Sprintf("%s.json", rule.Name))
+ return l.Save(rule, fileName)
+ }
+ return nil
+}
+
+// Save a rule to disk.
+func (l *Loader) Save(rule *Rule, path string) error {
+ rule.Updated = time.Now()
+ raw, err := json.MarshalIndent(rule, "", " ")
+ if err != nil {
+ return fmt.Errorf("Error while saving rule %s to %s: %s", rule, path, err)
+ }
+
+ if err = ioutil.WriteFile(path, raw, 0644); err != nil {
+ return fmt.Errorf("Error while saving rule %s to %s: %s", rule, path, err)
+ }
+
+ return nil
+}
+
+// Delete deletes a rule from the list by name.
+// If the duration is Always (i.e: saved on disk), it'll attempt to delete
+// it from disk.
+func (l *Loader) Delete(ruleName string) error {
+ l.Lock()
+ defer l.Unlock()
+
+ rule := l.rules[ruleName]
+ if rule == nil {
+ return nil
+ }
+ l.cleanListsRule(rule)
+
+ delete(l.rules, ruleName)
+ l.sortRules()
+
+ if rule.Duration != Always {
+ return nil
+ }
+
+ log.Info("Delete() rule: %s", rule)
+ return l.deleteRuleFromDisk(ruleName)
+}
+
+// FindFirstMatch will try match the connection against the existing rule set.
+func (l *Loader) FindFirstMatch(con *conman.Connection) (match *Rule) {
+ l.RLock()
+ defer l.RUnlock()
+
+ for _, idx := range l.rulesKeys {
+ rule, _ := l.rules[idx]
+ if rule.Enabled == false {
+ continue
+ }
+ if rule.Match(con) {
+ // We have a match.
+ // Save the rule in order to don't ask the user to take action,
+ // and keep iterating until a Deny or a Priority rule appears.
+ match = rule
+ if rule.Action == Reject || rule.Action == Deny || rule.Precedence == true {
+ return rule
+ }
+ }
+ }
+
+ return match
+}
--- /dev/null
+package rule
+
+import (
+ "io"
+ "math/rand"
+ "os"
+ "testing"
+ "time"
+)
+
+var tmpDir string
+
+func TestMain(m *testing.M) {
+ tmpDir = "/tmp/ostest_" + randString()
+ os.Mkdir(tmpDir, 0777)
+ defer os.RemoveAll(tmpDir)
+ os.Exit(m.Run())
+}
+
+func TestRuleLoader(t *testing.T) {
+ t.Parallel()
+ t.Log("Test rules loader")
+
+ var list []Operator
+ dur1s := Duration("1s")
+ dummyOper, _ := NewOperator(Simple, false, OpTrue, "", list)
+ dummyOper.Compile()
+ inMem1sRule := Create("000-xxx-name", true, false, Allow, dur1s, dummyOper)
+ inMemUntilRestartRule := Create("000-aaa-name", true, false, Allow, Restart, dummyOper)
+
+ l, err := NewLoader(false)
+ if err != nil {
+ t.Fail()
+ }
+ if err = l.Load("/non/existent/path/"); err == nil {
+ t.Error("non existent path test: err should not be nil")
+ }
+
+ if err = l.Load("testdata/"); err != nil {
+ t.Error("Error loading test rules: ", err)
+ }
+
+ testNumRules(t, l, 2)
+
+ if err = l.Add(inMem1sRule, false); err != nil {
+ t.Error("Error adding temporary rule")
+ }
+ testNumRules(t, l, 3)
+
+ // test auto deletion of temporary rule
+ time.Sleep(time.Second * 2)
+ testNumRules(t, l, 2)
+
+ if err = l.Add(inMemUntilRestartRule, false); err != nil {
+ t.Error("Error adding temporary rule (2)")
+ }
+ testNumRules(t, l, 3)
+ testRulesOrder(t, l)
+ testSortRules(t, l)
+ testFindMatch(t, l)
+ testFindEnabled(t, l)
+ testDurationChange(t, l)
+}
+
+func TestRuleLoaderInvalidRegexp(t *testing.T) {
+ t.Parallel()
+ t.Log("Test rules loader: invalid regexp")
+
+ l, err := NewLoader(true)
+ if err != nil {
+ t.Fail()
+ }
+ t.Run("loadRule() from disk test (simple)", func(t *testing.T) {
+ if err := l.loadRule("testdata/invalid-regexp.json"); err == nil {
+ t.Error("invalid regexp rule loaded: loadRule()")
+ }
+ })
+
+ t.Run("loadRule() from disk test (list)", func(t *testing.T) {
+ if err := l.loadRule("testdata/invalid-regexp-list.json"); err == nil {
+ t.Error("invalid regexp rule loaded: loadRule()")
+ }
+ })
+
+ var list []Operator
+ dur30m := Duration("30m")
+ opListData := `[{"type": "regexp", "operand": "process.path", "sensitive": false, "data": "^(/di(rmngr)$"}, {"type": "simple", "operand": "dest.port", "data": "53", "sensitive": false}]`
+ invalidRegexpOp, _ := NewOperator(List, false, OpList, opListData, list)
+ invalidRegexpRule := Create("invalid-regexp", true, false, Allow, dur30m, invalidRegexpOp)
+
+ t.Run("replaceUserRule() test list", func(t *testing.T) {
+ if err := l.replaceUserRule(invalidRegexpRule); err == nil {
+ t.Error("invalid regexp rule loaded: replaceUserRule()")
+ }
+ })
+}
+
+func TestLiveReload(t *testing.T) {
+ t.Parallel()
+ t.Log("Test rules loader with live reload")
+ l, err := NewLoader(true)
+ if err != nil {
+ t.Fail()
+ }
+ if err = Copy("testdata/000-allow-chrome.json", tmpDir+"/000-allow-chrome.json"); err != nil {
+ t.Error("Error copying rule into a temp dir")
+ }
+ if err = Copy("testdata/001-deny-chrome.json", tmpDir+"/001-deny-chrome.json"); err != nil {
+ t.Error("Error copying rule into a temp dir")
+ }
+ if err = l.Load(tmpDir); err != nil {
+ t.Error("Error loading test rules: ", err)
+ }
+ //wait for watcher to activate
+ time.Sleep(time.Second)
+ if err = Copy("testdata/live_reload/test-live-reload-remove.json", tmpDir+"/test-live-reload-remove.json"); err != nil {
+ t.Error("Error copying rules into temp dir")
+ }
+ if err = Copy("testdata/live_reload/test-live-reload-delete.json", tmpDir+"/test-live-reload-delete.json"); err != nil {
+ t.Error("Error copying rules into temp dir")
+ }
+ //wait for watcher to pick up the changes
+ time.Sleep(time.Second)
+ testNumRules(t, l, 4)
+ if err = os.Remove(tmpDir + "/test-live-reload-remove.json"); err != nil {
+ t.Error("Error Remove()ing file from temp dir")
+ }
+ if err = l.Delete("test-live-reload-delete"); err != nil {
+ t.Error("Error Delete()ing file from temp dir")
+ }
+ //wait for watcher to pick up the changes
+ time.Sleep(time.Second)
+ testNumRules(t, l, 2)
+}
+
+func randString() string {
+ rand.Seed(time.Now().UnixNano())
+ var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+ b := make([]rune, 10)
+ for i := range b {
+ b[i] = letterRunes[rand.Intn(len(letterRunes))]
+ }
+ return string(b)
+}
+
+func Copy(src, dst string) error {
+ in, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer in.Close()
+
+ out, err := os.Create(dst)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ _, err = io.Copy(out, in)
+ if err != nil {
+ return err
+ }
+ return out.Close()
+}
+
+func testNumRules(t *testing.T, l *Loader, num int) {
+ if l.NumRules() != num {
+ t.Error("rules number should be (2): ", num)
+ }
+}
+
+func testRulesOrder(t *testing.T, l *Loader) {
+ if l.rulesKeys[0] != "000-aaa-name" {
+ t.Error("Rules not in order (0): ", l.rulesKeys)
+ }
+ if l.rulesKeys[1] != "000-allow-chrome" {
+ t.Error("Rules not in order (1): ", l.rulesKeys)
+ }
+ if l.rulesKeys[2] != "001-deny-chrome" {
+ t.Error("Rules not in order (2): ", l.rulesKeys)
+ }
+}
+
+func testSortRules(t *testing.T, l *Loader) {
+ l.rulesKeys[1] = "001-deny-chrome"
+ l.rulesKeys[2] = "000-allow-chrome"
+ l.sortRules()
+ if l.rulesKeys[1] != "000-allow-chrome" {
+ t.Error("Rules not in order (1): ", l.rulesKeys)
+ }
+ if l.rulesKeys[2] != "001-deny-chrome" {
+ t.Error("Rules not in order (2): ", l.rulesKeys)
+ }
+}
+
+func testFindMatch(t *testing.T, l *Loader) {
+ conn.Process.Path = "/opt/google/chrome/chrome"
+
+ testFindPriorityMatch(t, l)
+ testFindDenyMatch(t, l)
+ testFindAllowMatch(t, l)
+
+ restoreConnection()
+}
+
+func testFindPriorityMatch(t *testing.T, l *Loader) {
+ match := l.FindFirstMatch(conn)
+ if match == nil {
+ t.Error("FindPriorityMatch didn't match")
+ }
+ // test 000-allow-chrome, priority == true
+ if match.Name != "000-allow-chrome" {
+ t.Error("findPriorityMatch: priority rule failed: ", match)
+ }
+
+}
+
+func testFindDenyMatch(t *testing.T, l *Loader) {
+ l.rules["000-allow-chrome"].Precedence = false
+ // test 000-allow-chrome, priority == false
+ // 001-deny-chrome must match
+ match := l.FindFirstMatch(conn)
+ if match == nil {
+ t.Error("FindDenyMatch deny didn't match")
+ }
+ if match.Name != "001-deny-chrome" {
+ t.Error("findDenyMatch: deny rule failed: ", match)
+ }
+}
+
+func testFindAllowMatch(t *testing.T, l *Loader) {
+ l.rules["000-allow-chrome"].Precedence = false
+ l.rules["001-deny-chrome"].Action = Allow
+ // test 000-allow-chrome, priority == false
+ // 001-deny-chrome must match
+ match := l.FindFirstMatch(conn)
+ if match == nil {
+ t.Error("FindAllowMatch allow didn't match")
+ }
+ if match.Name != "001-deny-chrome" {
+ t.Error("findAllowMatch: allow rule failed: ", match)
+ }
+}
+
+func testFindEnabled(t *testing.T, l *Loader) {
+ l.rules["000-allow-chrome"].Precedence = false
+ l.rules["001-deny-chrome"].Action = Allow
+ l.rules["001-deny-chrome"].Enabled = false
+ // test 000-allow-chrome, priority == false
+ // 001-deny-chrome must match
+ match := l.FindFirstMatch(conn)
+ if match == nil {
+ t.Error("FindEnabledMatch, match nil")
+ }
+ if match.Name == "001-deny-chrome" {
+ t.Error("findEnabledMatch: deny rule shouldn't have matched: ", match)
+ }
+}
+
+// test that changing the Duration of a temporary rule doesn't delete
+// the new one, ignoring the old timer.
+func testDurationChange(t *testing.T, l *Loader) {
+ l.rules["000-aaa-name"].Duration = "2s"
+ if err := l.replaceUserRule(l.rules["000-aaa-name"]); err != nil {
+ t.Error("testDurationChange, error replacing rule: ", err)
+ }
+ l.rules["000-aaa-name"].Duration = "1h"
+ if err := l.replaceUserRule(l.rules["000-aaa-name"]); err != nil {
+ t.Error("testDurationChange, error replacing rule: ", err)
+ }
+ time.Sleep(time.Second * 4)
+ if _, found := l.rules["000-aaa-name"]; !found {
+ t.Error("testDurationChange, error: rule has been deleted")
+ }
+}
--- /dev/null
+package rule
+
+import (
+ "fmt"
+ "net"
+ "reflect"
+ "regexp"
+ "strings"
+ "sync"
+
+ "github.com/evilsocket/opensnitch/daemon/conman"
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+// Type is the type of rule.
+// Every type has its own way of checking the user data against connections.
+type Type string
+
+// Sensitive defines if a rule is case-sensitive or not. By default no.
+type Sensitive bool
+
+// Operand is what we check on a connection.
+type Operand string
+
+// Available types
+const (
+ Simple = Type("simple")
+ Regexp = Type("regexp")
+ Complex = Type("complex") // for future use
+ List = Type("list")
+ Network = Type("network")
+ Lists = Type("lists")
+)
+
+// Available operands
+const (
+ OpTrue = Operand("true")
+ OpProcessID = Operand("process.id")
+ OpProcessPath = Operand("process.path")
+ OpProcessCmd = Operand("process.command")
+ OpProcessEnvPrefix = Operand("process.env.")
+ OpProcessEnvPrefixLen = 12
+ OpUserID = Operand("user.id")
+ OpDstIP = Operand("dest.ip")
+ OpDstHost = Operand("dest.host")
+ OpDstPort = Operand("dest.port")
+ OpDstNetwork = Operand("dest.network")
+ OpProto = Operand("protocol")
+ OpList = Operand("list")
+ OpDomainsLists = Operand("lists.domains")
+ OpDomainsRegexpLists = Operand("lists.domains_regexp")
+ OpIPLists = Operand("lists.ips")
+ OpNetLists = Operand("lists.nets")
+)
+
+type opCallback func(value interface{}) bool
+
+// Operator represents what we want to filter of a connection, and how.
+type Operator struct {
+ Type Type `json:"type"`
+ Operand Operand `json:"operand"`
+ Sensitive Sensitive `json:"sensitive"`
+ Data string `json:"data"`
+ List []Operator `json:"list"`
+
+ sync.RWMutex
+ cb opCallback
+ re *regexp.Regexp
+ netMask *net.IPNet
+ isCompiled bool
+ lists map[string]interface{}
+ listsMonitorRunning bool
+ exitMonitorChan chan (bool)
+}
+
+// NewOperator returns a new operator object
+func NewOperator(t Type, s Sensitive, o Operand, data string, list []Operator) (*Operator, error) {
+ op := Operator{
+ Type: t,
+ Sensitive: s,
+ Operand: o,
+ Data: data,
+ List: list,
+ }
+ return &op, nil
+}
+
+// Compile translates the operator type field to its callback counterpart
+func (o *Operator) Compile() error {
+ if o.isCompiled {
+ return nil
+ }
+ if o.Type == Simple {
+ o.cb = o.simpleCmp
+ } else if o.Type == Regexp {
+ o.cb = o.reCmp
+ if o.Sensitive == false {
+ o.Data = strings.ToLower(o.Data)
+ }
+ re, err := regexp.Compile(o.Data)
+ if err != nil {
+ return err
+ }
+ o.re = re
+ } else if o.Operand == OpDomainsLists {
+ if o.Data == "" {
+ return fmt.Errorf("Operand lists is empty, nothing to load: %s", o)
+ }
+ o.loadLists()
+ o.cb = o.domainsListCmp
+ } else if o.Operand == OpDomainsRegexpLists {
+ if o.Data == "" {
+ return fmt.Errorf("Operand regexp lists is empty, nothing to load: %s", o)
+ }
+ o.loadLists()
+ o.cb = o.reListCmp
+ } else if o.Operand == OpIPLists {
+ if o.Data == "" {
+ return fmt.Errorf("Operand ip lists is empty, nothing to load: %s", o)
+ }
+ o.loadLists()
+ o.cb = o.ipListCmp
+ } else if o.Operand == OpNetLists {
+ if o.Data == "" {
+ return fmt.Errorf("Operand net lists is empty, nothing to load: %s", o)
+ }
+ o.loadLists()
+ o.cb = o.ipNetCmp
+ } else if o.Type == List {
+ o.Operand = OpList
+ } else if o.Type == Network {
+ var err error
+ _, o.netMask, err = net.ParseCIDR(o.Data)
+ if err != nil {
+ return err
+ }
+ o.cb = o.cmpNetwork
+ }
+ log.Debug("Operator compiled: %s", o)
+ o.isCompiled = true
+
+ return nil
+}
+
+func (o *Operator) String() string {
+ how := "is"
+ if o.Type == Regexp {
+ how = "matches"
+ }
+ return fmt.Sprintf("%s %s '%s'", log.Bold(string(o.Operand)), how, log.Yellow(string(o.Data)))
+}
+
+func (o *Operator) simpleCmp(v interface{}) bool {
+ if o.Sensitive == false {
+ return strings.EqualFold(v.(string), o.Data)
+ }
+ return v == o.Data
+}
+
+func (o *Operator) reCmp(v interface{}) bool {
+ if vt := reflect.ValueOf(v).Kind(); vt != reflect.String {
+ log.Warning("Operator.reCmp() bad interface type: %T", v)
+ return false
+ }
+ if o.Sensitive == false {
+ v = strings.ToLower(v.(string))
+ }
+ return o.re.MatchString(v.(string))
+}
+
+func (o *Operator) cmpNetwork(destIP interface{}) bool {
+ // 192.0.2.1/24, 2001:db8:a0b:12f0::1/32
+ if o.netMask == nil {
+ log.Warning("cmpNetwork() NULL: %s", destIP)
+ return false
+ }
+ return o.netMask.Contains(destIP.(net.IP))
+}
+
+func (o *Operator) domainsListCmp(v interface{}) bool {
+ dstHost := v.(string)
+ if dstHost == "" {
+ return false
+ }
+ if o.Sensitive == false {
+ dstHost = strings.ToLower(dstHost)
+ }
+ o.RLock()
+ defer o.RUnlock()
+
+ if _, found := o.lists[dstHost]; found {
+ log.Debug("%s: %s, %s", log.Red("domain list match"), dstHost, o.lists[dstHost])
+ return true
+ }
+ return false
+}
+
+func (o *Operator) ipListCmp(v interface{}) bool {
+ dstIP := v.(string)
+ if dstIP == "" {
+ return false
+ }
+ o.RLock()
+ defer o.RUnlock()
+
+ if _, found := o.lists[dstIP]; found {
+ log.Debug("%s: %s, %s", log.Red("IP list match"), dstIP, o.lists[dstIP].(string))
+ return true
+ }
+ return false
+}
+
+func (o *Operator) ipNetCmp(dstIP interface{}) bool {
+ o.RLock()
+ defer o.RUnlock()
+
+ for host, netMask := range o.lists {
+ n := netMask.(*net.IPNet)
+ if n.Contains(dstIP.(net.IP)) {
+ log.Debug("%s: %s, %s", log.Red("Net list match"), dstIP, host)
+ return true
+ }
+ }
+ return false
+}
+
+func (o *Operator) reListCmp(v interface{}) bool {
+ dstHost := v.(string)
+ if dstHost == "" {
+ return false
+ }
+ if o.Sensitive == false {
+ dstHost = strings.ToLower(dstHost)
+ }
+ o.RLock()
+ defer o.RUnlock()
+
+ for file, re := range o.lists {
+ r := re.(*regexp.Regexp)
+ if r.MatchString(dstHost) {
+ log.Debug("%s: %s, %s", log.Red("Regexp list match"), dstHost, file)
+ return true
+ }
+ }
+ return false
+}
+
+func (o *Operator) listMatch(con interface{}) bool {
+ res := true
+ for i := 0; i < len(o.List); i++ {
+ res = res && o.List[i].Match(con.(*conman.Connection))
+ }
+ return res
+}
+
+// Match tries to match parts of a connection with the given operator.
+func (o *Operator) Match(con *conman.Connection) bool {
+
+ if o.Operand == OpTrue {
+ return true
+ } else if o.Operand == OpList {
+ return o.listMatch(con)
+ } else if o.Operand == OpProcessPath {
+ return o.cb(con.Process.Path)
+ } else if o.Operand == OpProcessCmd {
+ return o.cb(strings.Join(con.Process.Args, " "))
+ } else if o.Operand == OpDstHost && con.DstHost != "" {
+ return o.cb(con.DstHost)
+ } else if o.Operand == OpDstIP {
+ return o.cb(con.DstIP.String())
+ } else if o.Operand == OpDstPort {
+ return o.cb(fmt.Sprintf("%d", con.DstPort))
+ } else if o.Operand == OpUserID {
+ return o.cb(fmt.Sprintf("%d", con.Entry.UserId))
+ } else if o.Operand == OpProcessID {
+ return o.cb(fmt.Sprint(con.Process.ID))
+ } else if o.Operand == OpDomainsLists {
+ return o.cb(con.DstHost)
+ } else if o.Operand == OpIPLists {
+ return o.cb(con.DstIP.String())
+ } else if o.Operand == OpDstNetwork {
+ return o.cb(con.DstIP)
+ } else if o.Operand == OpNetLists {
+ return o.cb(con.DstIP)
+ } else if o.Operand == OpDomainsRegexpLists {
+ return o.cb(con.DstHost)
+ } else if o.Operand == OpProto {
+ return o.cb(con.Protocol)
+ } else if strings.HasPrefix(string(o.Operand), string(OpProcessEnvPrefix)) {
+ envVarName := core.Trim(string(o.Operand[OpProcessEnvPrefixLen:]))
+ envVarValue, _ := con.Process.Env[envVarName]
+ return o.cb(envVarValue)
+ }
+
+ return false
+}
--- /dev/null
+package rule
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net"
+ "path/filepath"
+ "regexp"
+ "runtime/debug"
+ "strings"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+)
+
+func (o *Operator) monitorLists() {
+ log.Info("monitor lists started: %s", o.Data)
+
+ modTimes := make(map[string]time.Time)
+ totalFiles := 0
+ needReload := false
+ numFiles := 0
+
+ expr := filepath.Join(o.Data, "/*.*")
+ for {
+ select {
+ case <-o.exitMonitorChan:
+ goto Exit
+ default:
+ fileList, err := filepath.Glob(expr)
+ if err != nil {
+ log.Warning("Error reading directory of domains list: %s, %s", o.Data, err)
+ goto Exit
+ }
+ numFiles = 0
+
+ for _, filename := range fileList {
+ // ignore hidden files
+ name := filepath.Base(filename)
+ if name[:1] == "." {
+ delete(modTimes, filename)
+ continue
+ }
+ // an overwrite operation performs two tasks: truncate the file and save the new content,
+ // causing the file time to be modified twice.
+ modTime, err := core.GetFileModTime(filename)
+ if err != nil {
+ log.Debug("deleting saved mod time due to error reading the list, %s", filename)
+ delete(modTimes, filename)
+ } else if lastModTime, found := modTimes[filename]; found {
+ if lastModTime.Equal(modTime) == false {
+ log.Debug("list changed: %s, %s, %s", lastModTime, modTime, filename)
+ needReload = true
+ }
+ }
+ modTimes[filename] = modTime
+ numFiles++
+ }
+ fileList = nil
+
+ if numFiles != totalFiles {
+ needReload = true
+ }
+ totalFiles = numFiles
+
+ if needReload {
+ // we can't reload a single list, because the domains of all lists are added to the same map.
+ // we could have the domains separated by lists/files, but then we'd need to iterate the map in order
+ // to match a domain. Reloading the lists shoud only occur once a day.
+ if err := o.readLists(); err != nil {
+ log.Warning("%s", err)
+ }
+ needReload = false
+ }
+ time.Sleep(4 * time.Second)
+ }
+ }
+
+Exit:
+ modTimes = nil
+ o.ClearLists()
+ log.Info("lists monitor stopped")
+}
+
+// ClearLists deletes all the entries of a list
+func (o *Operator) ClearLists() {
+ o.Lock()
+ defer o.Unlock()
+
+ log.Info("clearing domains lists: %d - %s", len(o.lists), o.Data)
+ for k := range o.lists {
+ delete(o.lists, k)
+ }
+ debug.FreeOSMemory()
+}
+
+// StopMonitoringLists stops the monitoring lists goroutine.
+func (o *Operator) StopMonitoringLists() {
+ if o.listsMonitorRunning == true {
+ o.exitMonitorChan <- true
+ o.exitMonitorChan = nil
+ o.listsMonitorRunning = false
+ }
+}
+
+func (o *Operator) readDomainsList(raw, fileName string) (dups uint64) {
+ log.Debug("Loading domains list: %s, size: %d", fileName, len(raw))
+ lines := strings.Split(string(raw), "\n")
+ for _, domain := range lines {
+ if len(domain) < 9 {
+ continue
+ }
+ // exclude not valid lines
+ if domain[:7] != "0.0.0.0" && domain[:9] != "127.0.0.1" {
+ continue
+ }
+ host := domain[8:]
+ // exclude localhost entries
+ if domain[:9] == "127.0.0.1" {
+ host = domain[10:]
+ }
+ if host == "local" || host == "localhost" || host == "localhost.localdomain" || host == "broadcasthost" {
+ continue
+ }
+
+ host = core.Trim(host)
+ if _, found := o.lists[host]; found {
+ dups++
+ continue
+ }
+ o.lists[host] = fileName
+ }
+ lines = nil
+ log.Info("%d domains loaded, %s", len(o.lists), fileName)
+
+ return dups
+}
+
+func (o *Operator) readNetList(raw, fileName string) (dups uint64) {
+ log.Debug("Loading nets list: %s, size: %d", fileName, len(raw))
+ lines := strings.Split(string(raw), "\n")
+ for _, line := range lines {
+ if line == "" || line[0] == '#' {
+ continue
+ }
+ host := core.Trim(line)
+ if _, found := o.lists[host]; found {
+ dups++
+ continue
+ }
+ _, netMask, err := net.ParseCIDR(host)
+ if err != nil {
+ log.Warning("Error parsing net from list: %s, (%s)", err, fileName)
+ continue
+ }
+ o.lists[host] = netMask
+ }
+ lines = nil
+ log.Info("%d nets loaded, %s", len(o.lists), fileName)
+
+ return dups
+}
+
+func (o *Operator) readRegexpList(raw, fileName string) (dups uint64) {
+ log.Debug("Loading regexp list: %s, size: %d", fileName, len(raw))
+ lines := strings.Split(string(raw), "\n")
+ for n, line := range lines {
+ if line == "" || line[0] == '#' {
+ continue
+ }
+ host := core.Trim(line)
+ if _, found := o.lists[host]; found {
+ dups++
+ continue
+ }
+ re, err := regexp.Compile(line)
+ if err != nil {
+ log.Warning("Error compiling regexp from list: %s, (%d:%s)", err, n, fileName)
+ continue
+ }
+ o.lists[line] = re
+ }
+ lines = nil
+ log.Info("%d regexps loaded, %s", len(o.lists), fileName)
+
+ return dups
+}
+
+func (o *Operator) readIPList(raw, fileName string) (dups uint64) {
+ log.Debug("Loading IPs list: %s, size: %d", fileName, len(raw))
+ lines := strings.Split(string(raw), "\n")
+ for _, line := range lines {
+ if line == "" || line[0] == '#' {
+ continue
+ }
+ ip := core.Trim(line)
+ if _, found := o.lists[ip]; found {
+ dups++
+ continue
+ }
+ o.lists[ip] = fileName
+ }
+ lines = nil
+ log.Info("%d IPs loaded, %s", len(o.lists), fileName)
+
+ return dups
+}
+
+func (o *Operator) readLists() error {
+ o.ClearLists()
+
+ var dups uint64
+ // this list is particular to this operator and rule
+ o.Lock()
+ defer o.Unlock()
+ o.lists = make(map[string]interface{})
+
+ expr := filepath.Join(o.Data, "*.*")
+ fileList, err := filepath.Glob(expr)
+ if err != nil {
+ return fmt.Errorf("Error loading domains lists '%s': %s", expr, err)
+ }
+
+ for _, fileName := range fileList {
+ // ignore hidden files
+ name := filepath.Base(fileName)
+ if name[:1] == "." {
+ continue
+ }
+
+ raw, err := ioutil.ReadFile(fileName)
+ if err != nil {
+ log.Warning("Error reading list of IPs (%s): %s", fileName, err)
+ continue
+ }
+
+ if o.Operand == OpDomainsLists {
+ dups += o.readDomainsList(string(raw), fileName)
+ } else if o.Operand == OpDomainsRegexpLists {
+ dups += o.readRegexpList(string(raw), fileName)
+ } else if o.Operand == OpNetLists {
+ dups += o.readNetList(string(raw), fileName)
+ } else if o.Operand == OpIPLists {
+ dups += o.readIPList(string(raw), fileName)
+ } else {
+ log.Warning("Unknown lists operand type: %s", o.Operand)
+ }
+ }
+ log.Info("%d lists loaded, %d domains, %d duplicated", len(fileList), len(o.lists), dups)
+ return nil
+}
+
+func (o *Operator) loadLists() {
+ log.Info("loading domains lists: %s, %s, %s", o.Type, o.Operand, o.Data)
+
+ // when loading from disk, we don't use the Operator's constructor, so we need to create this channel
+ if o.exitMonitorChan == nil {
+ o.exitMonitorChan = make(chan bool)
+ o.listsMonitorRunning = true
+ go o.monitorLists()
+ }
+}
--- /dev/null
+package rule
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+ "testing"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/conman"
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/netstat"
+ "github.com/evilsocket/opensnitch/daemon/procmon"
+)
+
+var (
+ defaultProcPath = "/usr/bin/opensnitchd"
+ defaultProcArgs = "-rules-path /etc/opensnitchd/rules/"
+ defaultDstHost = "opensnitch.io"
+ defaultDstPort = uint(443)
+ defaultDstIP = "185.53.178.14"
+ defaultUserID = 666
+
+ netEntry = &netstat.Entry{
+ UserId: defaultUserID,
+ }
+
+ proc = &procmon.Process{
+ ID: 12345,
+ Path: defaultProcPath,
+ Args: []string{"-rules-path", "/etc/opensnitchd/rules/"},
+ }
+
+ conn = &conman.Connection{
+ Protocol: "TCP",
+ SrcPort: 66666,
+ SrcIP: net.ParseIP("192.168.1.111"),
+ DstIP: net.ParseIP(defaultDstIP),
+ DstPort: defaultDstPort,
+ DstHost: defaultDstHost,
+ Process: proc,
+ Entry: netEntry,
+ }
+)
+
+func compileListOperators(list *[]Operator, t *testing.T) {
+ op := *list
+ for i := 0; i < len(*list); i++ {
+ if err := op[i].Compile(); err != nil {
+ t.Error("NewOperator List, Compile() subitem error:", err)
+ }
+ }
+}
+
+func unmarshalListData(data string, t *testing.T) (op *[]Operator) {
+ if err := json.Unmarshal([]byte(data), &op); err != nil {
+ t.Error("Error unmarshalling list data:", err, data)
+ return nil
+ }
+ return op
+}
+
+func restoreConnection() {
+ conn.Process.Path = defaultProcPath
+ conn.DstHost = defaultDstHost
+ conn.DstPort = defaultDstPort
+ conn.Entry.UserId = defaultUserID
+}
+
+func TestNewOperatorSimple(t *testing.T) {
+ t.Log("Test NewOperator() simple")
+ var list []Operator
+
+ opSimple, err := NewOperator(Simple, false, OpTrue, "", list)
+ if err != nil {
+ t.Error("NewOperator simple.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Fail()
+ }
+ if opSimple.Match(nil) == false {
+ t.Error("Test NewOperator() simple.case-insensitive doesn't match")
+ t.Fail()
+ }
+
+ t.Run("Operator Simple proc.id", func(t *testing.T) {
+ // proc.id not sensitive
+ opSimple, err = NewOperator(Simple, false, OpProcessID, "12345", list)
+ if err != nil {
+ t.Error("NewOperator simple.case-insensitive.proc.id err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Error("NewOperator simple.case-insensitive.proc.id Compile() err:", err)
+ t.Fail()
+ }
+ if opSimple.Match(conn) == false {
+ t.Error("Test NewOperator() simple proc.id doesn't match")
+ t.Fail()
+ }
+ })
+
+ opSimple, err = NewOperator(Simple, false, OpProcessPath, defaultProcPath, list)
+ t.Run("Operator Simple proc.path case-insensitive", func(t *testing.T) {
+ // proc path not sensitive
+ if err != nil {
+ t.Error("NewOperator simple proc.path err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Error("NewOperator simple.case-insensitive.proc.path Compile() err:", err)
+ t.Fail()
+ }
+ if opSimple.Match(conn) == false {
+ t.Error("Test NewOperator() simple proc.path doesn't match")
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator Simple proc.path sensitive", func(t *testing.T) {
+ // proc path sensitive
+ opSimple.Sensitive = true
+ conn.Process.Path = "/usr/bin/OpenSnitchd"
+ if opSimple.Match(conn) == true {
+ t.Error("Test NewOperator() simple proc.path sensitive match")
+ t.Fail()
+ }
+ })
+
+ opSimple, err = NewOperator(Simple, false, OpDstHost, defaultDstHost, list)
+ t.Run("Operator Simple con.dstHost case-insensitive", func(t *testing.T) {
+ // proc dst host not sensitive
+ if err != nil {
+ t.Error("NewOperator simple proc.path err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Error("NewOperator simple.case-insensitive.dstHost Compile() err:", err)
+ t.Fail()
+ }
+ if opSimple.Match(conn) == false {
+ t.Error("Test NewOperator() simple.conn.dstHost.not-sensitive doesn't match")
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator Simple con.dstHost case-insensitive different host", func(t *testing.T) {
+ conn.DstHost = "www.opensnitch.io"
+ if opSimple.Match(conn) == true {
+ t.Error("Test NewOperator() simple.conn.dstHost.not-sensitive doesn't MATCH")
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator Simple con.dstHost sensitive", func(t *testing.T) {
+ // proc dst host sensitive
+ opSimple, err = NewOperator(Simple, true, OpDstHost, "OpEnsNitCh.io", list)
+ if err != nil {
+ t.Error("NewOperator simple.dstHost.sensitive err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Error("NewOperator simple.dstHost.sensitive Compile() err:", err)
+ t.Fail()
+ }
+ conn.DstHost = "OpEnsNitCh.io"
+ if opSimple.Match(conn) == false {
+ t.Error("Test NewOperator() simple.dstHost.sensitive doesn't match")
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator Simple proc.args case-insensitive", func(t *testing.T) {
+ // proc args case-insensitive
+ opSimple, err = NewOperator(Simple, false, OpProcessCmd, defaultProcArgs, list)
+ if err != nil {
+ t.Error("NewOperator simple proc.args err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Error("NewOperator simple proc.args Compile() err: ", err)
+ t.Fail()
+ }
+ if opSimple.Match(conn) == false {
+ t.Error("Test NewOperator() simple proc.args doesn't match")
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator Simple con.dstIp case-insensitive", func(t *testing.T) {
+ // proc dstIp case-insensitive
+ opSimple, err = NewOperator(Simple, false, OpDstIP, defaultDstIP, list)
+ if err != nil {
+ t.Error("NewOperator simple conn.dstip.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Error("NewOperator simple con.dstIp Compile() err: ", err)
+ t.Fail()
+ }
+ if opSimple.Match(conn) == false {
+ t.Error("Test NewOperator() simple conn.dstip doesn't match")
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator Simple UserId case-insensitive", func(t *testing.T) {
+ // conn.uid case-insensitive
+ opSimple, err = NewOperator(Simple, false, OpUserID, fmt.Sprint(defaultUserID), list)
+ if err != nil {
+ t.Error("NewOperator simple conn.userid.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Error("NewOperator simple UserId Compile() err: ", err)
+ t.Fail()
+ }
+ if opSimple.Match(conn) == false {
+ t.Error("Test NewOperator() simple conn.userid doesn't match")
+ t.Fail()
+ }
+ })
+
+ restoreConnection()
+}
+
+func TestNewOperatorNetwork(t *testing.T) {
+ t.Log("Test NewOperator() network")
+ var dummyList []Operator
+
+ opSimple, err := NewOperator(Network, false, OpDstNetwork, "185.53.178.14/24", dummyList)
+ if err != nil {
+ t.Error("NewOperator network.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Fail()
+ }
+ if opSimple.Match(conn) == false {
+ t.Error("Test NewOperator() network doesn't match")
+ t.Fail()
+ }
+
+ opSimple, err = NewOperator(Network, false, OpDstNetwork, "8.8.8.8/24", dummyList)
+ if err != nil {
+ t.Error("NewOperator network.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opSimple.Compile(); err != nil {
+ t.Fail()
+ }
+ if opSimple.Match(conn) == true {
+ t.Error("Test NewOperator() network doesn't match:", conn.DstIP)
+ t.Fail()
+ }
+
+ restoreConnection()
+}
+
+func TestNewOperatorRegexp(t *testing.T) {
+ t.Log("Test NewOperator() regexp")
+ var dummyList []Operator
+
+ opRE, err := NewOperator(Regexp, false, OpProto, "^TCP$", dummyList)
+ if err != nil {
+ t.Error("NewOperator regexp.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opRE.Compile(); err != nil {
+ t.Fail()
+ }
+ if opRE.Match(conn) == false {
+ t.Error("Test NewOperator() regexp doesn't match")
+ t.Fail()
+ }
+
+ restoreConnection()
+}
+
+func TestNewOperatorInvalidRegexp(t *testing.T) {
+ t.Log("Test NewOperator() invalid regexp")
+ var dummyList []Operator
+
+ opRE, err := NewOperator(Regexp, false, OpProto, "^TC(P$", dummyList)
+ if err != nil {
+ t.Error("NewOperator regexp.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opRE.Compile(); err == nil {
+ t.Error("NewOperator() invalid regexp. It should fail: ", err)
+ t.Fail()
+ }
+
+ restoreConnection()
+}
+
+func TestNewOperatorRegexpSensitive(t *testing.T) {
+ t.Log("Test NewOperator() regexp sensitive")
+ var dummyList []Operator
+
+ var sensitive Sensitive
+ sensitive = true
+
+ conn.Process.Path = "/tmp/cUrL"
+
+ opRE, err := NewOperator(Regexp, sensitive, OpProcessPath, "^/tmp/cUrL$", dummyList)
+ if err != nil {
+ t.Error("NewOperator regexp.case-sensitive.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opRE.Compile(); err != nil {
+ t.Fail()
+ }
+ if opRE.Match(conn) == false {
+ t.Error("Test NewOperator() RE sensitive doesn't match:", conn.Process.Path)
+ t.Fail()
+ }
+
+ t.Run("Operator regexp proc.path case-sensitive", func(t *testing.T) {
+ conn.Process.Path = "/tmp/curl"
+ if opRE.Match(conn) == true {
+ t.Error("Test NewOperator() RE sensitive match:", conn.Process.Path)
+ t.Fail()
+ }
+ })
+
+ opRE, err = NewOperator(Regexp, !sensitive, OpProcessPath, "^/tmp/cUrL$", dummyList)
+ if err != nil {
+ t.Error("NewOperator regexp.case-insensitive.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opRE.Compile(); err != nil {
+ t.Fail()
+ }
+ if opRE.Match(conn) == false {
+ t.Error("Test NewOperator() RE not sensitive match:", conn.Process.Path)
+ t.Fail()
+ }
+
+ restoreConnection()
+}
+
+func TestNewOperatorList(t *testing.T) {
+ t.Log("Test NewOperator() List")
+ var list []Operator
+ listData := `[{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]`
+
+ // simple list
+ opList, err := NewOperator(List, false, OpProto, listData, list)
+ t.Run("Operator List simple case-insensitive", func(t *testing.T) {
+ if err != nil {
+ t.Error("NewOperator list.regexp.err should be nil: ", err)
+ t.Fail()
+ }
+ if err = opList.Compile(); err != nil {
+ t.Fail()
+ }
+ opList.List = *unmarshalListData(opList.Data, t)
+ compileListOperators(&opList.List, t)
+ if opList.Match(conn) == false {
+ t.Error("Test NewOperator() list simple doesn't match")
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator List regexp case-insensitive", func(t *testing.T) {
+ // list with regexp, case-insensitive
+ listData = `[{"type": "regexp", "operand": "process.path", "data": "^/usr/bin/.*", "sensitive": false},{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]`
+ opList.List = *unmarshalListData(listData, t)
+ compileListOperators(&opList.List, t)
+ if err = opList.Compile(); err != nil {
+ t.Fail()
+ }
+ if opList.Match(conn) == false {
+ t.Error("Test NewOperator() list regexp doesn't match")
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator List regexp case-sensitive", func(t *testing.T) {
+ // list with regexp, case-sensitive
+ // "data": "^/usr/BiN/.*" must match conn.Process.Path (sensitive)
+ listData = `[{"type": "regexp", "operand": "process.path", "data": "^/usr/BiN/.*", "sensitive": false},{"type": "simple", "operand": "dest.ip", "data": "185.53.178.14", "sensitive": false}, {"type": "simple", "operand": "dest.port", "data": "443", "sensitive": false}]`
+ opList.List = *unmarshalListData(listData, t)
+ compileListOperators(&opList.List, t)
+ conn.Process.Path = "/usr/BiN/opensnitchd"
+ opList.Sensitive = true
+ if err = opList.Compile(); err != nil {
+ t.Fail()
+ }
+ if opList.Match(conn) == false {
+ t.Error("Test NewOperator() list.regexp.sensitive doesn't match:", conn.Process.Path)
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator List regexp case-insensitive 2", func(t *testing.T) {
+ // "data": "^/usr/BiN/.*" must not match conn.Process.Path (insensitive)
+ opList.Sensitive = false
+ conn.Process.Path = "/USR/BiN/opensnitchd"
+ if err = opList.Compile(); err != nil {
+ t.Fail()
+ }
+ if opList.Match(conn) == false {
+ t.Error("Test NewOperator() list.regexp.insensitive match:", conn.Process.Path)
+ t.Fail()
+ }
+ })
+
+ t.Run("Operator List regexp case-insensitive 3", func(t *testing.T) {
+ // "data": "^/usr/BiN/.*" must match conn.Process.Path (insensitive)
+ opList.Sensitive = false
+ conn.Process.Path = "/USR/bin/opensnitchd"
+ if err = opList.Compile(); err != nil {
+ t.Fail()
+ }
+ if opList.Match(conn) == false {
+ t.Error("Test NewOperator() list.regexp.insensitive match:", conn.Process.Path)
+ t.Fail()
+ }
+ })
+
+ restoreConnection()
+}
+
+func TestNewOperatorListsSimple(t *testing.T) {
+ t.Log("Test NewOperator() Lists simple")
+ var dummyList []Operator
+
+ opLists, err := NewOperator(Lists, false, OpDomainsLists, "testdata/lists/domains/", dummyList)
+ if err != nil {
+ t.Error("NewOperator Lists, shouldn't be nil: ", err)
+ t.Fail()
+ }
+ if err = opLists.Compile(); err != nil {
+ t.Error("NewOperator Lists, Compile() error:", err)
+ }
+ time.Sleep(time.Second)
+ t.Log("testing Lists, DstHost:", conn.DstHost)
+ // The list contains 4 lines, 1 is a comment and there's a domain duplicated.
+ // We should only load lines that start with 0.0.0.0 or 127.0.0.1
+ if len(opLists.lists) != 2 {
+ t.Error("NewOperator Lists, number of domains error:", opLists.lists, len(opLists.lists))
+ }
+ if opLists.Match(conn) == false {
+ t.Error("Test NewOperator() lists doesn't match")
+ }
+
+ opLists.StopMonitoringLists()
+ time.Sleep(time.Second)
+ opLists.Lock()
+ if len(opLists.lists) != 0 {
+ t.Error("NewOperator Lists, number should be 0 after stop:", opLists.lists, len(opLists.lists))
+ }
+ opLists.Unlock()
+
+ restoreConnection()
+}
+
+func TestNewOperatorListsIPs(t *testing.T) {
+ t.Log("Test NewOperator() Lists domains_regexp")
+
+ var subOp *Operator
+ var list []Operator
+ listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.ips", "data": "testdata/lists/ips/", "sensitive": false}]`
+
+ opLists, err := NewOperator(List, false, OpList, listData, list)
+ if err != nil {
+ t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err)
+ t.Fail()
+ }
+ if err := opLists.Compile(); err != nil {
+ t.Error("NewOperator Lists domains_regexp, Compile() error:", err)
+ }
+ opLists.List = *unmarshalListData(opLists.Data, t)
+ for i := 0; i < len(opLists.List); i++ {
+ if err := opLists.List[i].Compile(); err != nil {
+ t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err)
+ }
+ if opLists.List[i].Type == Lists {
+ subOp = &opLists.List[i]
+ }
+ }
+
+ time.Sleep(time.Second)
+ if opLists.Match(conn) == false {
+ t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
+ }
+
+ subOp.Lock()
+ listslen := len(subOp.lists)
+ subOp.Unlock()
+ if listslen != 2 {
+ t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists)
+ }
+
+ //t.Log("checking lists.domains_regexp:", tries, conn.DstHost)
+ if opLists.Match(conn) == false {
+ // we don't care about if it matches, we're testing race conditions
+ t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
+ }
+
+ subOp.StopMonitoringLists()
+ time.Sleep(time.Second)
+ subOp.Lock()
+ if len(subOp.lists) != 0 {
+ t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
+ }
+ subOp.Unlock()
+
+ restoreConnection()
+}
+
+func TestNewOperatorListsNETs(t *testing.T) {
+ t.Log("Test NewOperator() Lists domains_regexp")
+
+ var subOp *Operator
+ var list []Operator
+ listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.nets", "data": "testdata/lists/nets/", "sensitive": false}]`
+
+ opLists, err := NewOperator(List, false, OpList, listData, list)
+ if err != nil {
+ t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err)
+ t.Fail()
+ }
+ if err := opLists.Compile(); err != nil {
+ t.Error("NewOperator Lists domains_regexp, Compile() error:", err)
+ }
+ opLists.List = *unmarshalListData(opLists.Data, t)
+ for i := 0; i < len(opLists.List); i++ {
+ if err := opLists.List[i].Compile(); err != nil {
+ t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err)
+ }
+ if opLists.List[i].Type == Lists {
+ subOp = &opLists.List[i]
+ }
+ }
+
+ time.Sleep(time.Second)
+ if opLists.Match(conn) == false {
+ t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
+ }
+
+ subOp.Lock()
+ listslen := len(subOp.lists)
+ subOp.Unlock()
+ if listslen != 2 {
+ t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists)
+ }
+
+ //t.Log("checking lists.domains_regexp:", tries, conn.DstHost)
+ if opLists.Match(conn) == false {
+ // we don't care about if it matches, we're testing race conditions
+ t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
+ }
+
+ subOp.StopMonitoringLists()
+ time.Sleep(time.Second)
+ subOp.Lock()
+ if len(subOp.lists) != 0 {
+ t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
+ }
+ subOp.Unlock()
+
+ restoreConnection()
+}
+
+func TestNewOperatorListsComplex(t *testing.T) {
+ t.Log("Test NewOperator() Lists complex")
+ var subOp *Operator
+ var list []Operator
+ listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains", "data": "testdata/lists/domains/", "sensitive": false}]`
+
+ opLists, err := NewOperator(List, false, OpList, listData, list)
+ if err != nil {
+ t.Error("NewOperator Lists complex, shouldn't be nil: ", err)
+ t.Fail()
+ }
+ if err := opLists.Compile(); err != nil {
+ t.Error("NewOperator Lists complex, Compile() error:", err)
+ }
+ opLists.List = *unmarshalListData(opLists.Data, t)
+ for i := 0; i < len(opLists.List); i++ {
+ if err := opLists.List[i].Compile(); err != nil {
+ t.Error("NewOperator Lists complex, Compile() subitem error:", err)
+ }
+ if opLists.List[i].Type == Lists {
+ subOp = &opLists.List[i]
+ }
+ }
+ time.Sleep(time.Second)
+ subOp.Lock()
+ if len(subOp.lists) != 2 {
+ t.Error("NewOperator Lists complex, number of domains error:", subOp.lists)
+ }
+ subOp.Unlock()
+ if opLists.Match(conn) == false {
+ t.Error("Test NewOperator() Lists complex, doesn't match")
+ }
+
+ subOp.StopMonitoringLists()
+ time.Sleep(time.Second)
+ subOp.Lock()
+ if len(subOp.lists) != 0 {
+ t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
+ }
+ subOp.Unlock()
+
+ restoreConnection()
+}
+
+func TestNewOperatorListsDomainsRegexp(t *testing.T) {
+ t.Log("Test NewOperator() Lists domains_regexp")
+
+ var subOp *Operator
+ var list []Operator
+ listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains_regexp", "data": "testdata/lists/regexp/", "sensitive": false}]`
+
+ opLists, err := NewOperator(List, false, OpList, listData, list)
+ if err != nil {
+ t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err)
+ t.Fail()
+ }
+ if err := opLists.Compile(); err != nil {
+ t.Error("NewOperator Lists domains_regexp, Compile() error:", err)
+ }
+ opLists.List = *unmarshalListData(opLists.Data, t)
+ for i := 0; i < len(opLists.List); i++ {
+ if err := opLists.List[i].Compile(); err != nil {
+ t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err)
+ }
+ if opLists.List[i].Type == Lists {
+ subOp = &opLists.List[i]
+ }
+ }
+
+ time.Sleep(time.Second)
+ if opLists.Match(conn) == false {
+ t.Error("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
+ }
+
+ subOp.Lock()
+ listslen := len(subOp.lists)
+ subOp.Unlock()
+ if listslen != 2 {
+ t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists)
+ }
+
+ //t.Log("checking lists.domains_regexp:", tries, conn.DstHost)
+ if opLists.Match(conn) == false {
+ // we don't care about if it matches, we're testing race conditions
+ t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
+ }
+
+ subOp.StopMonitoringLists()
+ time.Sleep(time.Second)
+ subOp.Lock()
+ if len(subOp.lists) != 0 {
+ t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
+ }
+ subOp.Unlock()
+
+ restoreConnection()
+}
+
+// Must be launched with -race to test that we don't cause leaks
+// Race occured on operator.go:241 reListCmp().MathString()
+// fixed here: 53419fe
+func TestRaceNewOperatorListsDomainsRegexp(t *testing.T) {
+ t.Log("Test NewOperator() Lists domains_regexp")
+
+ var subOp *Operator
+ var list []Operator
+ listData := `[{"type": "simple", "operand": "user.id", "data": "666", "sensitive": false}, {"type": "lists", "operand": "lists.domains_regexp", "data": "testdata/lists/regexp/", "sensitive": false}]`
+
+ opLists, err := NewOperator(List, false, OpList, listData, list)
+ if err != nil {
+ t.Error("NewOperator Lists domains_regexp, shouldn't be nil: ", err)
+ t.Fail()
+ }
+ if err := opLists.Compile(); err != nil {
+ t.Error("NewOperator Lists domains_regexp, Compile() error:", err)
+ }
+ opLists.List = *unmarshalListData(opLists.Data, t)
+ for i := 0; i < len(opLists.List); i++ {
+ if err := opLists.List[i].Compile(); err != nil {
+ t.Error("NewOperator Lists domains_regexp, Compile() subitem error:", err)
+ }
+ if opLists.List[i].Type == Lists {
+ subOp = &opLists.List[i]
+ }
+ }
+
+ // touch domains list in background, to force a reload.
+ go func() {
+ touches := 1000
+ for {
+ if touches < 0 {
+ break
+ }
+ core.Exec("/bin/touch", []string{"testdata/lists/regexp/domainsregexp.txt"})
+ touches--
+ time.Sleep(100 * time.Millisecond)
+ //t.Log("touching:", touches)
+ }
+ }()
+
+ time.Sleep(time.Second)
+
+ subOp.Lock()
+ listslen := len(subOp.lists)
+ subOp.Unlock()
+ if listslen != 2 {
+ t.Error("NewOperator Lists domains_regexp, number of domains error:", subOp.lists)
+ }
+
+ tries := 10000
+ for {
+ if tries < 0 {
+ break
+ }
+ //t.Log("checking lists.domains_regexp:", tries, conn.DstHost)
+ if opLists.Match(conn) == false {
+ // we don't care about if it matches, we're testing race conditions
+ t.Log("Test NewOperator() Lists domains_regexp, doesn't match:", conn.DstHost)
+ }
+
+ tries--
+ time.Sleep(10 * time.Millisecond)
+ }
+
+ subOp.StopMonitoringLists()
+ time.Sleep(time.Second)
+ subOp.Lock()
+ if len(subOp.lists) != 0 {
+ t.Error("NewOperator Lists number should be 0:", subOp.lists, len(subOp.lists))
+ }
+ subOp.Unlock()
+
+ restoreConnection()
+}
--- /dev/null
+package rule
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/conman"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/evilsocket/opensnitch/daemon/ui/protocol"
+)
+
+// Action of a rule
+type Action string
+
+// Actions of rules
+const (
+ Allow = Action("allow")
+ Deny = Action("deny")
+ Reject = Action("reject")
+)
+
+// Duration of a rule
+type Duration string
+
+// daemon possible durations
+const (
+ Once = Duration("once")
+ Restart = Duration("until restart")
+ Always = Duration("always")
+)
+
+// Rule represents an action on a connection.
+// The fields match the ones saved as json to disk.
+// If a .json rule file is modified on disk, it's reloaded automatically.
+type Rule struct {
+ Created time.Time `json:"created"`
+ Updated time.Time `json:"updated"`
+ Name string `json:"name"`
+ Enabled bool `json:"enabled"`
+ Precedence bool `json:"precedence"`
+ Action Action `json:"action"`
+ Duration Duration `json:"duration"`
+ Operator Operator `json:"operator"`
+}
+
+// Create creates a new rule object with the specified parameters.
+func Create(name string, enabled bool, precedence bool, action Action, duration Duration, op *Operator) *Rule {
+ return &Rule{
+ Created: time.Now(),
+ Enabled: enabled,
+ Precedence: precedence,
+ Name: name,
+ Action: action,
+ Duration: duration,
+ Operator: *op,
+ }
+}
+
+func (r *Rule) String() string {
+ return fmt.Sprintf("%s: if(%s){ %s %s }", r.Name, r.Operator.String(), r.Action, r.Duration)
+}
+
+// Match performs on a connection the checks a Rule has, to determine if it
+// must be allowed or denied.
+func (r *Rule) Match(con *conman.Connection) bool {
+ return r.Operator.Match(con)
+}
+
+// Deserialize translates back the rule received to a Rule object
+func Deserialize(reply *protocol.Rule) (*Rule, error) {
+ if reply.Operator == nil {
+ log.Warning("Deserialize rule, Operator nil")
+ return nil, fmt.Errorf("invalid operator")
+ }
+ operator, err := NewOperator(
+ Type(reply.Operator.Type),
+ Sensitive(reply.Operator.Sensitive),
+ Operand(reply.Operator.Operand),
+ reply.Operator.Data,
+ make([]Operator, 0),
+ )
+ if err != nil {
+ log.Warning("Deserialize rule, NewOperator() error: %s", err)
+ return nil, err
+ }
+
+ return Create(
+ reply.Name,
+ reply.Enabled,
+ reply.Precedence,
+ Action(reply.Action),
+ Duration(reply.Duration),
+ operator,
+ ), nil
+}
+
+// Serialize translates a Rule to the protocol object
+func (r *Rule) Serialize() *protocol.Rule {
+ if r == nil {
+ return nil
+ }
+ return &protocol.Rule{
+ Name: string(r.Name),
+ Enabled: bool(r.Enabled),
+ Precedence: bool(r.Precedence),
+ Action: string(r.Action),
+ Duration: string(r.Duration),
+ Operator: &protocol.Operator{
+ Type: string(r.Operator.Type),
+ Sensitive: bool(r.Operator.Sensitive),
+ Operand: string(r.Operator.Operand),
+ Data: string(r.Operator.Data),
+ },
+ }
+}
--- /dev/null
+package rule
+
+import "testing"
+
+func TestCreate(t *testing.T) {
+ t.Log("Test: Create rule")
+
+ var list []Operator
+ oper, _ := NewOperator(Simple, false, OpTrue, "", list)
+ r := Create("000-test-name", true, false, Allow, Once, oper)
+ t.Run("New rule must not be nil", func(t *testing.T) {
+ if r == nil {
+ t.Error("Create() returned nil")
+ t.Fail()
+ }
+ })
+ t.Run("Rule name must be 000-test-name", func(t *testing.T) {
+ if r.Name != "000-test-name" {
+ t.Error("Rule name error:", r.Name)
+ t.Fail()
+ }
+ })
+ t.Run("Rule must be enabled", func(t *testing.T) {
+ if r.Enabled == false {
+ t.Error("Rule Enabled is false:", r)
+ t.Fail()
+ }
+ })
+ t.Run("Rule Precedence must be false", func(t *testing.T) {
+ if r.Precedence == true {
+ t.Error("Rule Precedence is true:", r)
+ t.Fail()
+ }
+ })
+ t.Run("Rule Action must be Allow", func(t *testing.T) {
+ if r.Action != Allow {
+ t.Error("Rule Action is not Allow:", r.Action)
+ t.Fail()
+ }
+ })
+ t.Run("Rule Duration should be Once", func(t *testing.T) {
+ if r.Duration != Once {
+ t.Error("Rule Duration is not Once:", r.Duration)
+ t.Fail()
+ }
+ })
+}
--- /dev/null
+{
+ "created": "2020-12-13T18:06:52.209804547+01:00",
+ "updated": "2020-12-13T18:06:52.209857713+01:00",
+ "name": "000-allow-chrome",
+ "enabled": true,
+ "precedence": true,
+ "action": "allow",
+ "duration": "always",
+ "operator": {
+ "type": "simple",
+ "operand": "process.path",
+ "sensitive": false,
+ "data": "/opt/google/chrome/chrome",
+ "list": []
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "created": "2020-12-13T17:54:49.067148304+01:00",
+ "updated": "2020-12-13T17:54:49.067213602+01:00",
+ "name": "001-deny-chrome",
+ "enabled": true,
+ "precedence": false,
+ "action": "deny",
+ "duration": "always",
+ "operator": {
+ "type": "simple",
+ "operand": "process.path",
+ "sensitive": false,
+ "data": "/opt/google/chrome/chrome",
+ "list": []
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "created": "2020-12-13T18:06:52.209804547+01:00",
+ "updated": "2020-12-13T18:06:52.209857713+01:00",
+ "name": "invalid-regexp-list",
+ "enabled": true,
+ "precedence": true,
+ "action": "allow",
+ "duration": "always",
+ "operator": {
+ "type": "list",
+ "operand": "list",
+ "sensitive": false,
+ "data": "[{\"type\": \"regexp\", \"operand\": \"process.path\", \"sensitive\": false, \"data\": \"^(/di(rmngr$\"}, {\"type\": \"simple\", \"operand\": \"dest.port\", \"data\": \"53\", \"sensitive\": false}]",
+ "list": [
+ {
+ "type": "regexp",
+ "operand": "process.path",
+ "sensitive": false,
+ "data": "^(/di(rmngr)$",
+ "list": null
+ },
+ {
+ "type": "simple",
+ "operand": "dest.port",
+ "sensitive": false,
+ "data": "53",
+ "list": null
+ }
+ ]
+ }
+}
--- /dev/null
+{
+ "created": "2020-12-13T18:06:52.209804547+01:00",
+ "updated": "2020-12-13T18:06:52.209857713+01:00",
+ "name": "invalid-regexp",
+ "enabled": true,
+ "precedence": true,
+ "action": "allow",
+ "duration": "always",
+ "operator": {
+ "type": "regexp",
+ "operand": "process.path",
+ "sensitive": false,
+ "data": "/opt/((.*)google/chrome/chrome",
+ "list": []
+ }
+}
--- /dev/null
+# this line must be ignored, 0.0.0.0 www.test.org
+0.0.0.0 www.test.org
+127.0.0.1 www.test.org
+0.0.0.0 opensnitch.io
--- /dev/null
+# this line must be ignored, 0.0.0.0 www.test.org
+
+# empty lines are also ignored
+1.1.1.1
+185.53.178.14
+# duplicated entries should be ignored
+1.1.1.1
--- /dev/null
+# this line must be ignored, 0.0.0.0 www.test.org
+
+# empty lines are also ignored
+1.1.1.0/24
+185.53.178.0/24
+# duplicated entries should be ignored
+1.1.1.0/24
+
--- /dev/null
+# this line must be ignored, 0.0.0.0 www.test.org
+www.test.org
+www.test.org
+opensnitch.io
--- /dev/null
+{
+ "created": "2020-12-13T18:06:52.209804547+01:00",
+ "updated": "2020-12-13T18:06:52.209857713+01:00",
+ "name": "test-live-reload-delete",
+ "enabled": true,
+ "precedence": true,
+ "action": "deny",
+ "duration": "always",
+ "operator": {
+ "type": "simple",
+ "operand": "process.path",
+ "sensitive": false,
+ "data": "/usr/bin/curl",
+ "list": []
+ }
+ }
\ No newline at end of file
--- /dev/null
+{
+ "created": "2020-12-13T18:06:52.209804547+01:00",
+ "updated": "2020-12-13T18:06:52.209857713+01:00",
+ "name": "test-live-reload-remove",
+ "enabled": true,
+ "precedence": true,
+ "action": "deny",
+ "duration": "always",
+ "operator": {
+ "type": "simple",
+ "operand": "process.path",
+ "sensitive": false,
+ "data": "/usr/bin/curl",
+ "list": []
+ }
+ }
\ No newline at end of file
--- /dev/null
+package statistics
+
+import (
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/conman"
+ "github.com/evilsocket/opensnitch/daemon/rule"
+ "github.com/evilsocket/opensnitch/daemon/ui/protocol"
+)
+
+type Event struct {
+ Time time.Time
+ Connection *conman.Connection
+ Rule *rule.Rule
+}
+
+func NewEvent(con *conman.Connection, match *rule.Rule) *Event {
+ return &Event{
+ Time: time.Now(),
+ Connection: con,
+ Rule: match,
+ }
+}
+
+func (e *Event) Serialize() *protocol.Event {
+ return &protocol.Event{
+ Time: e.Time.Format("2006-01-02 15:04:05"),
+ Connection: e.Connection.Serialize(),
+ Rule: e.Rule.Serialize(),
+ Unixnano: e.Time.UnixNano(),
+ }
+}
--- /dev/null
+package statistics
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/conman"
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/evilsocket/opensnitch/daemon/rule"
+ "github.com/evilsocket/opensnitch/daemon/ui/protocol"
+)
+
+// StatsConfig holds the stats confguration
+type StatsConfig struct {
+ MaxEvents int `json:"MaxEvents"`
+ MaxStats int `json:"MaxStats"`
+}
+
+type conEvent struct {
+ con *conman.Connection
+ match *rule.Rule
+ wasMissed bool
+}
+
+// Statistics holds the connections and statistics the daemon intercepts.
+// The connections are stored in the Events slice.
+type Statistics struct {
+ sync.RWMutex
+
+ Started time.Time
+ DNSResponses int
+ Connections int
+ Ignored int
+ Accepted int
+ Dropped int
+ RuleHits int
+ RuleMisses int
+ Events []*Event
+ ByProto map[string]uint64
+ ByAddress map[string]uint64
+ ByHost map[string]uint64
+ ByPort map[string]uint64
+ ByUID map[string]uint64
+ ByExecutable map[string]uint64
+
+ rules *rule.Loader
+ jobs chan conEvent
+ // max number of events to keep in the buffer
+ maxEvents int
+ // max number of entries for each By* map
+ maxStats int
+}
+
+// New returns a new Statistics object and initializes the go routines to update the stats.
+func New(rules *rule.Loader) (stats *Statistics) {
+ stats = &Statistics{
+ Started: time.Now(),
+ Events: make([]*Event, 0),
+ ByProto: make(map[string]uint64),
+ ByAddress: make(map[string]uint64),
+ ByHost: make(map[string]uint64),
+ ByPort: make(map[string]uint64),
+ ByUID: make(map[string]uint64),
+ ByExecutable: make(map[string]uint64),
+
+ rules: rules,
+ jobs: make(chan conEvent),
+ maxEvents: 150,
+ maxStats: 25,
+ }
+
+ go stats.eventWorker(0)
+ go stats.eventWorker(1)
+ go stats.eventWorker(2)
+ go stats.eventWorker(3)
+
+ return stats
+}
+
+// SetConfig configures the max events to keep in the backlog before sending
+// the stats to the UI, or while the UI is not connected.
+// if the backlog is full, it'll be shifted by one.
+func (s *Statistics) SetConfig(config StatsConfig) {
+ if config.MaxEvents > 0 {
+ s.maxEvents = config.MaxEvents
+ }
+ if config.MaxStats > 0 {
+ s.maxStats = config.MaxStats
+ }
+}
+
+// OnDNSResponse increases the counter of dns and accepted connections.
+func (s *Statistics) OnDNSResponse() {
+ s.Lock()
+ defer s.Unlock()
+ s.DNSResponses++
+ s.Accepted++
+}
+
+// OnIgnored increases the counter of ignored and accepted connections.
+func (s *Statistics) OnIgnored() {
+ s.Lock()
+ defer s.Unlock()
+ s.Ignored++
+ s.Accepted++
+}
+
+func (s *Statistics) incMap(m *map[string]uint64, key string) {
+ if val, found := (*m)[key]; found == false {
+ // do we have enough space left?
+ nElems := len(*m)
+ if nElems >= s.maxStats {
+ // find the element with less hits
+ nMin := uint64(9999999999)
+ minKey := ""
+ for k, v := range *m {
+ if v < nMin {
+ minKey = k
+ nMin = v
+ }
+ }
+ // remove it
+ if minKey != "" {
+ delete(*m, minKey)
+ }
+ }
+
+ (*m)[key] = 1
+ } else {
+ (*m)[key] = val + 1
+ }
+}
+
+func (s *Statistics) eventWorker(id int) {
+ log.Debug("Stats worker #%d started.", id)
+
+ for true {
+ select {
+ case job := <-s.jobs:
+ s.onConnection(job.con, job.match, job.wasMissed)
+ }
+ }
+}
+
+func (s *Statistics) onConnection(con *conman.Connection, match *rule.Rule, wasMissed bool) {
+ s.Lock()
+ defer s.Unlock()
+
+ s.Connections++
+
+ if wasMissed {
+ s.RuleMisses++
+ } else {
+ s.RuleHits++
+ }
+
+ if wasMissed == false && match.Action == rule.Allow {
+ s.Accepted++
+ } else {
+ s.Dropped++
+ }
+
+ s.incMap(&s.ByProto, con.Protocol)
+ s.incMap(&s.ByAddress, con.DstIP.String())
+ if con.DstHost != "" {
+ s.incMap(&s.ByHost, con.DstHost)
+ }
+ s.incMap(&s.ByPort, fmt.Sprintf("%d", con.DstPort))
+ s.incMap(&s.ByUID, fmt.Sprintf("%d", con.Entry.UserId))
+ s.incMap(&s.ByExecutable, con.Process.Path)
+
+ // if we reached the limit, shift everything back
+ // by one position
+ nEvents := len(s.Events)
+ if nEvents == s.maxEvents {
+ s.Events = s.Events[1:]
+ }
+ if wasMissed {
+ return
+ }
+ s.Events = append(s.Events, NewEvent(con, match))
+}
+
+// OnConnectionEvent sends the details of a new connection throughout a channel,
+// in order to add the connection to the stats.
+func (s *Statistics) OnConnectionEvent(con *conman.Connection, match *rule.Rule, wasMissed bool) {
+ s.jobs <- conEvent{
+ con: con,
+ match: match,
+ wasMissed: wasMissed,
+ }
+}
+
+func (s *Statistics) serializeEvents() []*protocol.Event {
+ nEvents := len(s.Events)
+ serialized := make([]*protocol.Event, nEvents)
+
+ for i, e := range s.Events {
+ serialized[i] = e.Serialize()
+ }
+
+ return serialized
+}
+
+// emptyStats empties the stats once we've sent them to the GUI.
+// We don't need them anymore here.
+func (s *Statistics) emptyStats() {
+ s.Lock()
+ if len(s.Events) > 0 {
+ s.Events = make([]*Event, 0)
+ }
+ s.Unlock()
+}
+
+// Serialize returns the collected statistics.
+// After return the stats, the Events are emptied, to keep collecting more stats
+// and not miss connections.
+func (s *Statistics) Serialize() *protocol.Statistics {
+ s.Lock()
+ defer s.emptyStats()
+ defer s.Unlock()
+
+ return &protocol.Statistics{
+ DaemonVersion: core.Version,
+ Rules: uint64(s.rules.NumRules()),
+ Uptime: uint64(time.Since(s.Started).Seconds()),
+ DnsResponses: uint64(s.DNSResponses),
+ Connections: uint64(s.Connections),
+ Ignored: uint64(s.Ignored),
+ Accepted: uint64(s.Accepted),
+ Dropped: uint64(s.Dropped),
+ RuleHits: uint64(s.RuleHits),
+ RuleMisses: uint64(s.RuleMisses),
+ Events: s.serializeEvents(),
+ ByProto: s.ByProto,
+ ByAddress: s.ByAddress,
+ ByHost: s.ByHost,
+ ByPort: s.ByPort,
+ ByUid: s.ByUID,
+ ByExecutable: s.ByExecutable,
+ }
+}
--- /dev/null
+{
+ "SystemRules": [
+ {
+ "Rule": {
+ "Description": "Allow icmp",
+ "Table": "mangle",
+ "Chain": "OUTPUT",
+ "Parameters": "-p icmp",
+ "Target": "ACCEPT",
+ "TargetParameters": ""
+ }
+ }
+ ]
+}
--- /dev/null
+package ui
+
+import (
+ "fmt"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/conman"
+ "github.com/evilsocket/opensnitch/daemon/firewall/iptables"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/evilsocket/opensnitch/daemon/rule"
+ "github.com/evilsocket/opensnitch/daemon/statistics"
+ "github.com/evilsocket/opensnitch/daemon/ui/protocol"
+
+ "github.com/fsnotify/fsnotify"
+ "golang.org/x/net/context"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/connectivity"
+ "google.golang.org/grpc/keepalive"
+)
+
+var (
+ configFile = "/etc/opensnitchd/default-config.json"
+ dummyOperator, _ = rule.NewOperator(rule.Simple, false, rule.OpTrue, "", make([]rule.Operator, 0))
+ clientDisconnectedRule = rule.Create("ui.client.disconnected", true, false, rule.Allow, rule.Once, dummyOperator)
+ // While the GUI is connected, deny by default everything until the user takes an action.
+ clientConnectedRule = rule.Create("ui.client.connected", true, false, rule.Deny, rule.Once, dummyOperator)
+ clientErrorRule = rule.Create("ui.client.error", true, false, rule.Allow, rule.Once, dummyOperator)
+ config Config
+)
+
+type serverConfig struct {
+ Address string `json:"Address"`
+ LogFile string `json:"LogFile"`
+}
+
+// Config holds the values loaded from configFile
+type Config struct {
+ sync.RWMutex
+ Server serverConfig `json:"Server"`
+ DefaultAction string `json:"DefaultAction"`
+ DefaultDuration string `json:"DefaultDuration"`
+ InterceptUnknown bool `json:"InterceptUnknown"`
+ ProcMonitorMethod string `json:"ProcMonitorMethod"`
+ LogLevel *uint32 `json:"LogLevel"`
+ Firewall string `json:"Firewall"`
+ Stats statistics.StatsConfig `json:"Stats"`
+}
+
+// Client holds the connection information of a client.
+type Client struct {
+ sync.RWMutex
+ clientCtx context.Context
+ clientCancel context.CancelFunc
+
+ stats *statistics.Statistics
+ rules *rule.Loader
+ socketPath string
+ isUnixSocket bool
+ con *grpc.ClientConn
+ client protocol.UIClient
+ configWatcher *fsnotify.Watcher
+ streamNotifications protocol.UI_NotificationsClient
+ //isAsking is set to true if the client is awaiting a decision from the GUI
+ isAsking bool
+}
+
+// NewClient creates and configures a new client.
+func NewClient(socketPath string, stats *statistics.Statistics, rules *rule.Loader) *Client {
+ c := &Client{
+ stats: stats,
+ rules: rules,
+ isUnixSocket: false,
+ isAsking: false,
+ }
+ c.clientCtx, c.clientCancel = context.WithCancel(context.Background())
+
+ if watcher, err := fsnotify.NewWatcher(); err == nil {
+ c.configWatcher = watcher
+ }
+ c.loadDiskConfiguration(false)
+ if socketPath != "" {
+ c.setSocketPath(c.getSocketPath(socketPath))
+ }
+
+ go c.poller()
+ return c
+}
+
+// Close cancels the running tasks: pinging the server and (re)connection poller.
+func (c *Client) Close() {
+ c.clientCancel()
+}
+
+// ProcMonitorMethod returns the monitor method configured.
+// If it's not present in the config file, it'll return an empty string.
+func (c *Client) ProcMonitorMethod() string {
+ config.RLock()
+ defer config.RUnlock()
+ return config.ProcMonitorMethod
+}
+
+// InterceptUnknown returns
+func (c *Client) InterceptUnknown() bool {
+ config.RLock()
+ defer config.RUnlock()
+ return config.InterceptUnknown
+}
+
+// GetStatsConfig returns the stats config from disk
+func (c *Client) GetStatsConfig() statistics.StatsConfig {
+ config.RLock()
+ defer config.RUnlock()
+ return config.Stats
+}
+
+// GetFirewallType returns the firewall to use
+func (c *Client) GetFirewallType() string {
+ config.RLock()
+ defer config.RUnlock()
+ if config.Firewall == "" {
+ return iptables.Name
+ }
+ return config.Firewall
+}
+
+// DefaultAction returns the default configured action for
+func (c *Client) DefaultAction() rule.Action {
+ isConnected := c.Connected()
+
+ c.RLock()
+ defer c.RUnlock()
+
+ if isConnected {
+ return clientConnectedRule.Action
+ }
+
+ return clientDisconnectedRule.Action
+}
+
+// DefaultDuration returns the default duration configured for a rule.
+// For example it can be: once, always, "until restart".
+func (c *Client) DefaultDuration() rule.Duration {
+ c.RLock()
+ defer c.RUnlock()
+ return clientDisconnectedRule.Duration
+}
+
+// Connected checks if the client has established a connection with the server.
+func (c *Client) Connected() bool {
+ c.RLock()
+ defer c.RUnlock()
+ if c.con == nil || c.con.GetState() != connectivity.Ready {
+ return false
+ }
+ return true
+}
+
+//GetIsAsking returns the isAsking flag
+func (c *Client) GetIsAsking() bool {
+ c.RLock()
+ defer c.RUnlock()
+ return c.isAsking
+}
+
+//SetIsAsking sets the isAsking flag
+func (c *Client) SetIsAsking(flag bool) {
+ c.Lock()
+ defer c.Unlock()
+ c.isAsking = flag
+}
+
+func (c *Client) poller() {
+ log.Debug("UI service poller started for socket %s", c.socketPath)
+ wasConnected := false
+ for {
+ select {
+ case <-c.clientCtx.Done():
+ log.Info("Client.poller() exit, Done()")
+ goto Exit
+ default:
+ isConnected := c.Connected()
+ if wasConnected != isConnected {
+ c.onStatusChange(isConnected)
+ wasConnected = isConnected
+ }
+
+ if c.Connected() == false {
+ // connect and create the client if needed
+ if err := c.connect(); err != nil {
+ log.Warning("Error while connecting to UI service: %s", err)
+ }
+ }
+ if c.Connected() == true {
+ // if the client is connected and ready, send a ping
+ if err := c.ping(time.Now()); err != nil {
+ log.Warning("Error while pinging UI service: %s, state: %v", err, c.con.GetState())
+ }
+ }
+
+ time.Sleep(1 * time.Second)
+ }
+ }
+Exit:
+ log.Info("uiClient exit")
+}
+
+func (c *Client) onStatusChange(connected bool) {
+ if connected {
+ log.Info("Connected to the UI service on %s", c.socketPath)
+ go c.Subscribe()
+ } else {
+ log.Error("Connection to the UI service lost.")
+ c.disconnect()
+ }
+}
+
+func (c *Client) connect() (err error) {
+ if c.Connected() {
+ return
+ }
+
+ if c.con != nil {
+ if c.con.GetState() == connectivity.TransientFailure || c.con.GetState() == connectivity.Shutdown {
+ c.disconnect()
+ } else {
+ return
+ }
+ }
+
+ if err := c.openSocket(); err != nil {
+ c.disconnect()
+ return err
+ }
+
+ if c.client == nil {
+ c.client = protocol.NewUIClient(c.con)
+ }
+ return nil
+}
+
+func (c *Client) openSocket() (err error) {
+ c.Lock()
+ defer c.Unlock()
+
+ if c.isUnixSocket {
+ c.con, err = grpc.Dial(c.socketPath, grpc.WithInsecure(),
+ grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
+ return net.DialTimeout("unix", addr, timeout)
+ }))
+ } else {
+ // https://pkg.go.dev/google.golang.org/grpc/keepalive#ClientParameters
+ var kacp = keepalive.ClientParameters{
+ Time: 5 * time.Second,
+ // if there's no activity after ^, wait 20s and close
+ // server timeout is 20s by default.
+ Timeout: 22 * time.Second,
+ // send pings even without active streams
+ PermitWithoutStream: true,
+ }
+
+ c.con, err = grpc.Dial(c.socketPath, grpc.WithInsecure(), grpc.WithKeepaliveParams(kacp))
+ }
+
+ return err
+}
+
+func (c *Client) disconnect() {
+ c.Lock()
+ defer c.Unlock()
+
+ c.client = nil
+ if c.con != nil {
+ c.con.Close()
+ c.con = nil
+ log.Debug("client.disconnect()")
+ }
+}
+
+func (c *Client) ping(ts time.Time) (err error) {
+ if c.Connected() == false {
+ return fmt.Errorf("service is not connected")
+ }
+
+ c.Lock()
+ defer c.Unlock()
+
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+ defer cancel()
+ reqID := uint64(ts.UnixNano())
+
+ pReq := &protocol.PingRequest{
+ Id: reqID,
+ Stats: c.stats.Serialize(),
+ }
+ c.stats.RLock()
+ pong, err := c.client.Ping(ctx, pReq)
+ c.stats.RUnlock()
+ if err != nil {
+ return err
+ }
+
+ if pong.Id != reqID {
+ return fmt.Errorf("Expected pong with id 0x%x, got 0x%x", reqID, pong.Id)
+ }
+
+ return nil
+}
+
+// Ask sends a request to the server, with the values of a connection to be
+// allowed or denied.
+func (c *Client) Ask(con *conman.Connection) *rule.Rule {
+ if c.client == nil {
+ return nil
+ }
+
+ // FIXME: if timeout is fired, the rule is not added to the list in the GUI
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*120)
+ defer cancel()
+ reply, err := c.client.AskRule(ctx, con.Serialize())
+ if err != nil {
+ log.Warning("Error while asking for rule: %s - %v", err, con)
+ return nil
+ }
+
+ r, err := rule.Deserialize(reply)
+ if err != nil {
+ return nil
+ }
+ return r
+}
+
+func (c *Client) monitorConfigWorker() {
+ for {
+ select {
+ case event := <-c.configWatcher.Events:
+ if (event.Op&fsnotify.Write == fsnotify.Write) || (event.Op&fsnotify.Remove == fsnotify.Remove) {
+ c.loadDiskConfiguration(true)
+ }
+ }
+ }
+}
--- /dev/null
+package ui
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "strings"
+
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/evilsocket/opensnitch/daemon/procmon/monitor"
+ "github.com/evilsocket/opensnitch/daemon/rule"
+)
+
+func (c *Client) getSocketPath(socketPath string) string {
+ c.Lock()
+ defer c.Unlock()
+
+ if strings.HasPrefix(socketPath, "unix://") == true {
+ c.isUnixSocket = true
+ return socketPath[7:]
+ }
+
+ c.isUnixSocket = false
+ return socketPath
+}
+
+func (c *Client) setSocketPath(socketPath string) {
+ c.Lock()
+ defer c.Unlock()
+
+ c.socketPath = socketPath
+}
+
+func (c *Client) isProcMonitorEqual(newMonitorMethod string) bool {
+ config.RLock()
+ defer config.RUnlock()
+
+ return newMonitorMethod == config.ProcMonitorMethod
+}
+
+func (c *Client) parseConf(rawConfig string) (conf Config, err error) {
+ err = json.Unmarshal([]byte(rawConfig), &conf)
+ return conf, err
+}
+
+func (c *Client) loadDiskConfiguration(reload bool) {
+ raw, err := ioutil.ReadFile(configFile)
+ if err != nil {
+ fmt.Errorf("Error loading disk configuration %s: %s", configFile, err)
+ }
+
+ if ok := c.loadConfiguration(raw); ok {
+ if err := c.configWatcher.Add(configFile); err != nil {
+ log.Error("Could not watch path: %s", err)
+ return
+ }
+ }
+
+ if reload {
+ return
+ }
+
+ go c.monitorConfigWorker()
+}
+
+func (c *Client) loadConfiguration(rawConfig []byte) bool {
+ config.Lock()
+ defer config.Unlock()
+
+ if err := json.Unmarshal(rawConfig, &config); err != nil {
+ log.Error("Error parsing configuration %s: %s", configFile, err)
+ return false
+ }
+ // firstly load config level, to detect further errors if any
+ if config.LogLevel != nil {
+ log.SetLogLevel(int(*config.LogLevel))
+ }
+ if config.Server.LogFile != "" {
+ log.Close()
+ log.OpenFile(config.Server.LogFile)
+ }
+
+ if config.Server.Address != "" {
+ tempSocketPath := c.getSocketPath(config.Server.Address)
+ if tempSocketPath != c.socketPath {
+ // disconnect, and let the connection poller reconnect to the new address
+ c.disconnect()
+ }
+ c.setSocketPath(tempSocketPath)
+ }
+ if config.DefaultAction != "" {
+ clientDisconnectedRule.Action = rule.Action(config.DefaultAction)
+ clientErrorRule.Action = rule.Action(config.DefaultAction)
+ }
+ if config.DefaultDuration != "" {
+ clientDisconnectedRule.Duration = rule.Duration(config.DefaultDuration)
+ clientErrorRule.Duration = rule.Duration(config.DefaultDuration)
+ }
+ if config.ProcMonitorMethod != "" {
+ if err := monitor.ReconfigureMonitorMethod(config.ProcMonitorMethod); err != nil {
+ log.Warning("Unable to set new process monitor method from disk: %v", err)
+ }
+ }
+
+ return true
+}
+
+func (c *Client) saveConfiguration(rawConfig string) (err error) {
+ if c.loadConfiguration([]byte(rawConfig)) != true {
+ return fmt.Errorf("Error parsing configuration %s: %s", rawConfig, err)
+ }
+
+ if err = ioutil.WriteFile(configFile, []byte(rawConfig), 0644); err != nil {
+ log.Error("writing configuration to disk: %s", err)
+ return err
+ }
+ return nil
+}
--- /dev/null
+package ui
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/evilsocket/opensnitch/daemon/core"
+ "github.com/evilsocket/opensnitch/daemon/firewall"
+ "github.com/evilsocket/opensnitch/daemon/log"
+ "github.com/evilsocket/opensnitch/daemon/procmon"
+ "github.com/evilsocket/opensnitch/daemon/procmon/monitor"
+ "github.com/evilsocket/opensnitch/daemon/rule"
+ "github.com/evilsocket/opensnitch/daemon/ui/protocol"
+ "golang.org/x/net/context"
+)
+
+var stopMonitoringProcess = make(chan int)
+
+// NewReply constructs a new protocol notification reply
+func NewReply(rID uint64, replyCode protocol.NotificationReplyCode, data string) *protocol.NotificationReply {
+ return &protocol.NotificationReply{
+ Id: rID,
+ Code: replyCode,
+ Data: data,
+ }
+}
+
+func (c *Client) getClientConfig() *protocol.ClientConfig {
+ raw, _ := ioutil.ReadFile(configFile)
+ nodeName := core.GetHostname()
+ nodeVersion := core.GetKernelVersion()
+ var ts time.Time
+ rulesTotal := len(c.rules.GetAll())
+ ruleList := make([]*protocol.Rule, rulesTotal)
+ idx := 0
+ for _, r := range c.rules.GetAll() {
+ ruleList[idx] = r.Serialize()
+ idx++
+ }
+ return &protocol.ClientConfig{
+ Id: uint64(ts.UnixNano()),
+ Name: nodeName,
+ Version: nodeVersion,
+ IsFirewallRunning: firewall.IsRunning(),
+ Config: strings.Replace(string(raw), "\n", "", -1),
+ LogLevel: uint32(log.MinLevel),
+ Rules: ruleList,
+ }
+}
+
+func (c *Client) monitorProcessDetails(pid int, stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ p := procmon.NewProcess(pid, "")
+ ticker := time.NewTicker(2 * time.Second)
+
+ for {
+ select {
+ case _pid := <-stopMonitoringProcess:
+ if _pid != pid {
+ continue
+ }
+ goto Exit
+ case <-ticker.C:
+ if err := p.GetInfo(); err != nil {
+ c.sendNotificationReply(stream, notification.Id, notification.Data, err)
+ goto Exit
+ }
+
+ pJSON, err := json.Marshal(p)
+ notification.Data = string(pJSON)
+ if errs := c.sendNotificationReply(stream, notification.Id, notification.Data, err); errs != nil {
+ goto Exit
+ }
+ }
+ }
+
+Exit:
+ ticker.Stop()
+}
+
+func (c *Client) handleActionChangeConfig(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ log.Info("[notification] Reloading configuration")
+ // Parse received configuration first, to get the new proc monitor method.
+ newConf, err := c.parseConf(notification.Data)
+ if err != nil {
+ log.Warning("[notification] error parsing received config: %v", notification.Data)
+ c.sendNotificationReply(stream, notification.Id, "", err)
+ return
+ }
+
+ if err := monitor.ReconfigureMonitorMethod(newConf.ProcMonitorMethod); err != nil {
+ c.sendNotificationReply(stream, notification.Id, "", err)
+ return
+ }
+
+ // this save operation triggers a re-loadConfiguration()
+ err = c.saveConfiguration(notification.Data)
+ if err != nil {
+ log.Warning("[notification] CHANGE_CONFIG not applied %s", err)
+ }
+
+ c.sendNotificationReply(stream, notification.Id, "", err)
+}
+
+func (c *Client) handleActionEnableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ var err error
+ for _, rul := range notification.Rules {
+ log.Info("[notification] enable rule: %s", rul.Name)
+ // protocol.Rule(protobuf) != rule.Rule(json)
+ r, _ := rule.Deserialize(rul)
+ r.Enabled = true
+ // save to disk only if the duration is rule.Always
+ err = c.rules.Replace(r, r.Duration == rule.Always)
+ }
+ c.sendNotificationReply(stream, notification.Id, "", err)
+}
+
+func (c *Client) handleActionDisableRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ var err error
+ for _, rul := range notification.Rules {
+ log.Info("[notification] disable rule: %s", rul)
+ r, _ := rule.Deserialize(rul)
+ r.Enabled = false
+ err = c.rules.Replace(r, r.Duration == rule.Always)
+ }
+ c.sendNotificationReply(stream, notification.Id, "", err)
+}
+
+func (c *Client) handleActionChangeRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ var rErr error
+ for _, rul := range notification.Rules {
+ r, err := rule.Deserialize(rul)
+ if r == nil {
+ rErr = fmt.Errorf("Invalid rule, %s", err)
+ continue
+ }
+ log.Info("[notification] change rule: %s %d", r, notification.Id)
+ if err := c.rules.Replace(r, r.Duration == rule.Always); err != nil {
+ log.Warning("[notification] Error changing rule: %s %s", err, r)
+ rErr = err
+ }
+ }
+ c.sendNotificationReply(stream, notification.Id, "", rErr)
+}
+
+func (c *Client) handleActionDeleteRule(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ var err error
+ for _, rul := range notification.Rules {
+ log.Info("[notification] delete rule: %s %d", rul.Name, notification.Id)
+ err = c.rules.Delete(rul.Name)
+ if err != nil {
+ log.Error("[notification] Error deleting rule: %s %s", err, rul)
+ }
+ }
+ c.sendNotificationReply(stream, notification.Id, "", err)
+}
+
+func (c *Client) handleActionMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ pid, err := strconv.Atoi(notification.Data)
+ if err != nil {
+ log.Error("parsing PID to monitor: %d, err: %s", pid, err)
+ return
+ }
+ if !core.Exists(fmt.Sprint("/proc/", pid)) {
+ c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("The process is no longer running"))
+ return
+ }
+ go c.monitorProcessDetails(pid, stream, notification)
+}
+
+func (c *Client) handleActionStopMonitorProcess(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ pid, err := strconv.Atoi(notification.Data)
+ if err != nil {
+ log.Error("parsing PID to stop monitor: %d, err: %s", pid, err)
+ c.sendNotificationReply(stream, notification.Id, "", fmt.Errorf("Error stopping monitor: %s", notification.Data))
+ return
+ }
+ stopMonitoringProcess <- pid
+ c.sendNotificationReply(stream, notification.Id, "", nil)
+}
+
+func (c *Client) handleNotification(stream protocol.UI_NotificationsClient, notification *protocol.Notification) {
+ switch {
+ case notification.Type == protocol.Action_MONITOR_PROCESS:
+ c.handleActionMonitorProcess(stream, notification)
+
+ case notification.Type == protocol.Action_STOP_MONITOR_PROCESS:
+ c.handleActionStopMonitorProcess(stream, notification)
+
+ case notification.Type == protocol.Action_CHANGE_CONFIG:
+ c.handleActionChangeConfig(stream, notification)
+
+ case notification.Type == protocol.Action_LOAD_FIREWALL:
+ log.Info("[notification] starting firewall")
+ firewall.Init(c.GetFirewallType(), nil)
+ c.sendNotificationReply(stream, notification.Id, "", nil)
+
+ case notification.Type == protocol.Action_UNLOAD_FIREWALL:
+ log.Info("[notification] stopping firewall")
+ firewall.Stop()
+ c.sendNotificationReply(stream, notification.Id, "", nil)
+
+ // ENABLE_RULE just replaces the rule on disk
+ case notification.Type == protocol.Action_ENABLE_RULE:
+ c.handleActionEnableRule(stream, notification)
+
+ case notification.Type == protocol.Action_DISABLE_RULE:
+ c.handleActionDisableRule(stream, notification)
+
+ case notification.Type == protocol.Action_DELETE_RULE:
+ c.handleActionDeleteRule(stream, notification)
+
+ // CHANGE_RULE can add() or replace) an existing rule.
+ case notification.Type == protocol.Action_CHANGE_RULE:
+ c.handleActionChangeRule(stream, notification)
+ }
+}
+
+func (c *Client) sendNotificationReply(stream protocol.UI_NotificationsClient, nID uint64, data string, err error) error {
+ reply := NewReply(nID, protocol.NotificationReplyCode_OK, data)
+ if err != nil {
+ reply.Code = protocol.NotificationReplyCode_ERROR
+ reply.Data = fmt.Sprint(err)
+ }
+ if err := stream.Send(reply); err != nil {
+ log.Error("Error replying to notification: %s %d", err, reply.Id)
+ return err
+ }
+
+ return nil
+}
+
+// Subscribe opens a connection with the server (UI), to start
+// receiving notifications.
+// It firstly sends the daemon status and configuration.
+func (c *Client) Subscribe() {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
+ defer cancel()
+
+ clientCfg, err := c.client.Subscribe(ctx, c.getClientConfig())
+ if err != nil {
+ log.Error("Subscribing to GUI %s", err)
+ // When connecting to the GUI via TCP, sometimes the notifications channel is
+ // not established, and the main channel is never closed.
+ // We need to disconnect everything after a timeout and try it again.
+ c.disconnect()
+ return
+ }
+
+ if tempConf, err := c.parseConf(clientCfg.Config); err == nil {
+ c.Lock()
+ clientConnectedRule.Action = rule.Action(tempConf.DefaultAction)
+ c.Unlock()
+ }
+ c.listenForNotifications()
+}
+
+// Notifications is the channel where the daemon receives messages from the server.
+// It consists of 2 grpc streams (send/receive) that are never closed,
+// this way we can share messages in realtime.
+// If the GUI is closed, we'll receive an error reading from the channel.
+func (c *Client) listenForNotifications() {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ // open the stream channel
+ streamReply := &protocol.NotificationReply{Id: 0, Code: protocol.NotificationReplyCode_OK}
+ notisStream, err := c.client.Notifications(ctx)
+ if err != nil {
+ log.Error("establishing notifications channel %s", err)
+ return
+ }
+ // send the first notification
+ if err := notisStream.Send(streamReply); err != nil {
+ log.Error("sending notification HELLO %s", err)
+ return
+ }
+ log.Info("Start receiving notifications")
+ for {
+ select {
+ case <-c.clientCtx.Done():
+ goto Exit
+ default:
+ noti, err := notisStream.Recv()
+ if err == io.EOF {
+ log.Warning("notification channel closed by the server")
+ goto Exit
+ }
+ if err != nil {
+ log.Error("getting notifications: %s %s", err, noti)
+ goto Exit
+ }
+ c.handleNotification(notisStream, noti)
+ }
+ }
+Exit:
+ notisStream.CloseSend()
+ log.Info("Stop receiving notifications")
+ c.disconnect()
+}
--- /dev/null
+opensnitch (1.5.5-1) unstable; urgency=medium
+
+ * New upstream release.
+ * Bump Standards-Version to 4.6.2.
+ * Upload sponsored by Petter Reinholdtsen.
+
+ -- Gustavo Iñiguez Goya <gustavo.iniguez.goya@gmail.com> Wed, 01 Feb 2023 22:37:12 +0100
+
+opensnitch (1.5.4-1) unstable; urgency=high
+
+ * New upstream release. (Closes: #1030115)
+ * debian/control:
+ - Updated packages description.
+ - Removed debconf and whiptail|dialog dependencies.
+ - Added xdg-user-dirs, gtk-update-icon-cache dependencies.
+ - Point Vcs-Git field to the 1.5.0 branch.
+ * debian/postinst:
+ - Fixed opensnitch_ui.desktop installation.
+ - Fixed updating icons cache.
+ * debian/postrm:
+ - Fixed removing opensnitch_ui.desktop
+ * debian/tests/:
+ - Added autopkgtests.
+ * Upload sponsored by Petter Reinholdtsen.
+
+ -- Gustavo Iñiguez Goya <gustavo.iniguez.goya@gmail.com> Tue, 31 Jan 2023 23:48:58 +0100
+
+opensnitch (1.5.3-1) unstable; urgency=medium
+
+ * Added debian/upstream/metadata.
+ * Updated Homepage url.
+ * Updated Copyright years.
+
+ -- Gustavo-Iniguez-Goya <gustavo.iniguez.goya@gmail.com> Sun, 22 Jan 2023 21:30:45 +0100
+
+opensnitch (1.5.2.1-1) unstable; urgency=medium
+
+ * Initial release. (Closes: #909567)
+
+ -- Gustavo-Iniguez-Goya <gustavo.iniguez.goya@gmail.com> Fri, 20 Jan 2023 22:26:40 +0000
+
+opensnitch (1.5.2-1) unstable; urgency=medium
+
+ * try to mount debugfs on boot up
+
+ -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Wed, 27 Jul 2022 17:29:33 +0200
+
+opensnitch (1.5.1-1) unstable; urgency=medium
+
+ * Better eBPF cache.
+ * Fixed error resolving domains to localhost.
+ * Fixed error deleting our nftables rules.
+
+ -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Fri, 25 Feb 2022 01:21:38 +0100
+
+opensnitch (1.5.0-1) unstable; urgency=medium
+
+ * New release.
+ * Added Reject option.
+ * New lists types to block ads/malware/...
+ * Better connections interception.
+ * Better VPNs handling.
+ * Bug fixes.
+
+ -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Fri, 28 Jan 2022 23:20:38 +0100
+
+opensnitch (1.5.0~rc2-1) unstable; urgency=medium
+
+ * Better connections interception.
+ * Improvements.
+
+ -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Sun, 16 Jan 2022 23:15:12 +0100
+
+opensnitch (1.5.0~rc1-1) unstable; urgency=medium
+
+ * New features.
+
+ -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Thu, 07 Oct 2021 14:57:35 +0200
+
+opensnitch (1.4.0-1) unstable; urgency=medium
+
+ * final release.
+
+ -- gustavo-iniguez-goya <gustavo.iniguez.goya@gmail.com> Fri, 27 Aug 2021 13:33:07 +0200
+
+opensnitch (1.4.0~rc4-1) unstable; urgency=medium
+
+ * Bug fix release.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 11 Aug 2021 15:17:49 +0200
+
+opensnitch (1.4.0~rc3-1) unstable; urgency=medium
+
+ * Bug fix release.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 16 Jul 2021 23:28:52 +0200
+
+opensnitch (1.4.0~rc2-1) unstable; urgency=medium
+
+ * Added eBPF support.
+ * Fixes and improvements.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 07 May 2021 01:08:02 +0200
+
+opensnitch (1.4.0~rc-1) unstable; urgency=medium
+
+ * Bug fix and improvements release.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Thu, 25 Mar 2021 01:02:31 +0100
+
+opensnitch (1.3.6-1) unstable; urgency=medium
+
+ * Bug fix and improvements release.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 10 Feb 2021 10:17:43 +0100
+
+opensnitch (1.3.5-1) unstable; urgency=medium
+
+ * Bug fix and improvements release.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Mon, 11 Jan 2021 18:01:53 +0100
+
+opensnitch (1.3.0-1) unstable; urgency=medium
+
+ * Fixed how we check rules
+ * Fixed cpu spike after disable interception.
+ * Fixed cleaning up fw rules on exit.
+ * make regexp rules case-insensitive by default
+ * allow to filter by dst network.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 16 Dec 2020 01:15:03 +0100
+
+opensnitch (1.3.0~rc-1) unstable; urgency=medium
+
+ * Non-maintainer upload.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 13 Nov 2020 00:51:34 +0100
+
+opensnitch (1.2.0-1) unstable; urgency=medium
+
+ * Fixed memleaks.
+ * Sort rules by name
+ * Added priority field to rules.
+ * Other fixes
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Mon, 09 Nov 2020 22:55:13 +0100
+
+opensnitch (1.0.1-1) unstable; urgency=medium
+
+ * Fixed app exit when IPv6 is not supported.
+ * Other fixes.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Thu, 30 Jul 2020 21:56:20 +0200
+
+opensnitch (1.0.0-1) unstable; urgency=medium
+
+ * v1.0.0 released.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Thu, 16 Jul 2020 00:19:26 +0200
+
+opensnitch (1.0.0rc11-1) unstable; urgency=medium
+
+ * Fixed multiple race conditions.
+ * Fixed CWD parsing when using audit proc monitor method.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 24 Jun 2020 00:10:38 +0200
+
+opensnitch (1.0.0rc10-1) unstable; urgency=medium
+
+ * Fixed checking UID functions availability.
+ * Improved process path parsing.
+ * Fixed applying config from the UI.
+ * Fixed default log level.
+ * Gather CWD and process environment vars.
+ * Increase default timeout when asking for a rule.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Sat, 13 Jun 2020 18:45:02 +0200
+
+opensnitch (1.0.0rc9-1) unstable; urgency=medium
+
+ * Ignore malformed rules from loading.
+ * Allow to modify and add rules from the UI.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Sun, 17 May 2020 18:18:24 +0200
+
+opensnitch (1.0.0rc8) unstable; urgency=medium
+
+ * Allow to change settings from the UI.
+ * Improved connection handling with the UI.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 29 Apr 2020 21:52:27 +0200
+
+opensnitch (1.0.0rc7-1) unstable; urgency=medium
+
+ * Stability, performance and realiability improvements.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Sun, 12 Apr 2020 23:25:41 +0200
+
+opensnitch (1.0.0rc6-1) unstable; urgency=medium
+
+ * Fixed iptables rules deletion.
+ * Improved PIDs cache.
+ * Added audit process monitoring method.
+ * Added logrotate file.
+ * Added default configuration file.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Sun, 08 Mar 2020 20:47:58 +0100
+
+opensnitch (1.0.0rc-5) unstable; urgency=medium
+
+ * Fixed netlink socket querying.
+ * Added check to reload firewall rules if missing.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Mon, 24 Feb 2020 19:55:06 +0100
+
+opensnitch (1.0.0rc-3) unstable; urgency=medium
+
+ * @see: https://github.com/gustavo-iniguez-goya/opensnitch/releases
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Tue, 18 Feb 2020 10:09:45 +0100
+
+opensnitch (1.0.0rc-2) unstable; urgency=medium
+
+ * UI minor changes
+ * Expand deb package compatibility.
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Wed, 05 Feb 2020 21:50:20 +0100
+
+opensnitch (1.0.0rc-1) unstable; urgency=medium
+
+ * Initial release
+
+ -- gustavo-iniguez-goya <gooffy1@gmail.com> Fri, 22 Nov 2019 01:14:08 +0100
--- /dev/null
+Source: opensnitch
+Maintainer: Gustavo Iñiguez Goya <gustavo.iniguez.goya@gmail.com>
+Section: devel
+Testsuite: autopkgtest-pkg-go
+Priority: optional
+Build-Depends:
+ debhelper-compat (= 11),
+ dh-golang,
+ dh-python,
+ golang-any,
+ golang-github-evilsocket-ftrace-dev,
+ golang-github-fsnotify-fsnotify-dev,
+ golang-github-google-gopacket-dev,
+ golang-github-google-nftables-dev,
+ golang-github-iovisor-gobpf-dev,
+ golang-github-vishvananda-netlink-dev,
+ golang-golang-x-net-dev,
+ golang-google-grpc-dev,
+ golang-goprotobuf-dev,
+ libmnl-dev,
+ libnetfilter-queue-dev,
+ pkg-config,
+ protoc-gen-go-grpc,
+ pyqt5-dev-tools,
+ qttools5-dev-tools,
+ python3-all,
+ python3-grpc-tools,
+ python3-setuptools
+Standards-Version: 4.6.2
+Vcs-Browser: https://github.com/evilsocket/opensnitch
+Vcs-Git: https://github.com/evilsocket/opensnitch.git -b 1.5.0
+Homepage: https://github.com/evilsocket/opensnitch
+Rules-Requires-Root: no
+XS-Go-Import-Path: github.com/evilsocket/opensnitch
+
+Package: opensnitch
+Section: net
+Architecture: any
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+Recommends: python3-opensnitch-ui
+Built-Using: ${misc:Built-Using}
+Description: GNU/Linux interactive application firewall
+ OpenSnitch is a GNU/Linux firewall application.
+ Whenever a program makes a connection, it'll prompt the user to allow or deny
+ it.
+ .
+ The user can decide if block the outgoing connection based on properties of
+ the connection: by port, by uid, by dst ip, by program or a combination
+ of them.
+ .
+ These rules can last forever, until the app restart or just one time.
+ .
+ The GUI allows the user to view live outgoing connections, as well as search
+ by process, user, host or port.
+ .
+ OpenSnitch can also work as a system-wide domains blocker, by using lists
+ of domains, list of IPs or list of regular expressions.
+
+
+Package: python3-opensnitch-ui
+Architecture: all
+Section: net
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+ libqt5sql5-sqlite,
+ python3-grpcio,
+ python3-notify2,
+ python3-pyinotify,
+ python3-pyqt5,
+ python3-pyqt5.qtsql,
+ python3-setuptools,
+ python3-six,
+ python3-slugify,
+ python3:any,
+ xdg-user-dirs,
+ gtk-update-icon-cache
+Recommends:
+ python3-pyasn
+Suggests: opensnitch
+Description: GNU/Linux interactive application firewall GUI
+ opensnitch-ui is a GUI for opensnitch written in Python.
+ It allows the user to view live outgoing connections, as well as search
+ for details of the intercepted connections.
+ .
+ The user can decide if block outgoing connections based on properties of
+ the connection: by port, by uid, by dst ip, by program or a combination
+ of them.
+ .
+ These rules can last forever, until restart the daemon or just one time.
+ .
+ OpenSnitch can also work as a system-wide domains blocker, by using lists
+ of domains, list of IPs or list of regular expressions.
--- /dev/null
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Source: https://github.com/evilsocket/opensnitch
+Upstream-Contact: Gustavo Iñiguez Goia <gooffy1@gmail.com>
+Upstream-Name: opensnitch
+Files-Excluded:
+ Godeps/_workspace
+
+Files: *
+Copyright:
+ 2017-2018 evilsocket
+ 2019-2023 Gustavo Iñiguez Goia
+Comment: Debian packaging is licensed under the same terms as upstream
+License: GPL-3.0+
+ This program is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later
+ version.
+ .
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the GNU General Public License for more
+ details.
+ .
+ You should have received a copy of the GNU General Public
+ License along with this program. If not, If not, see
+ http://www.gnu.org/licenses/.
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 3 can be found in the file
+ '/usr/share/common-licenses/GPL-3'.
--- /dev/null
+[DEFAULT]
+pristine-tar = True
--- /dev/null
+# auto-generated, DO NOT MODIFY.
+# The authoritative copy of this file lives at:
+# https://salsa.debian.org/go-team/ci/blob/master/config/gitlabciyml.go
+
+# TODO: publish under debian-go-team/ci
+image: stapelberg/ci2
+
+test_the_archive:
+ artifacts:
+ paths:
+ - before-applying-commit.json
+ - after-applying-commit.json
+ script:
+ # Create an overlay to discard writes to /srv/gopath/src after the build:
+ - "rm -rf /cache/overlay/{upper,work}"
+ - "mkdir -p /cache/overlay/{upper,work}"
+ - "mount -t overlay overlay -o lowerdir=/srv/gopath/src,upperdir=/cache/overlay/upper,workdir=/cache/overlay/work /srv/gopath/src"
+ - "export GOPATH=/srv/gopath"
+ - "export GOCACHE=/cache/go"
+ # Build the world as-is:
+ - "ci-build -exemptions=/var/lib/ci-build/exemptions.json > before-applying-commit.json"
+ # Copy this package into the overlay:
+ - "GBP_CONF_FILES=:debian/gbp.conf gbp buildpackage --git-no-pristine-tar --git-ignore-branch --git-ignore-new --git-export-dir=/tmp/export --git-no-overlay --git-tarball-dir=/nonexistant --git-cleaner=/bin/true --git-builder='dpkg-buildpackage -S -d --no-sign'"
+ - "pgt-gopath -dsc /tmp/export/*.dsc"
+ # Rebuild the world:
+ - "ci-build -exemptions=/var/lib/ci-build/exemptions.json > after-applying-commit.json"
+ - "ci-diff before-applying-commit.json after-applying-commit.json"
--- /dev/null
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides: opensnitchd
+# Required-Start: $network $local_fs
+# Required-Stop: $network $local_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: opensnitchd daemon
+# Description: opensnitch application firewall
+### END INIT INFO
+
+NAME=opensnitchd
+PIDDIR=/var/run/$NAME
+OPENSNITCHDPID=$PIDDIR/$NAME.pid
+
+# clear conflicting settings from the environment
+unset TMPDIR
+
+test -x /usr/bin/$NAME || exit 0
+
+. /lib/lsb/init-functions
+
+case $1 in
+ start)
+ log_daemon_msg "Starting opensnitch daemon" $NAME
+ if [ ! -d /etc/$NAME/rules ]; then
+ mkdir -p /etc/$NAME/rules &>/dev/null
+ fi
+
+ # Make sure we have our PIDDIR, even if it's on a tmpfs
+ install -o root -g root -m 755 -d $PIDDIR
+
+ if ! start-stop-daemon --start --quiet --oknodo --pidfile $OPENSNITCHDPID --background --exec /usr/bin/$NAME -- -rules-path /etc/$NAME/rules; then
+ log_end_msg 1
+ exit 1
+ fi
+
+ log_end_msg 0
+ ;;
+ stop)
+
+ log_daemon_msg "Stopping $NAME daemon" $NAME
+
+ start-stop-daemon --stop --quiet --signal QUIT --name $NAME
+ # Wait a little and remove stale PID file
+ sleep 1
+ if [ -f $OPENSNITCHDPID ] && ! ps h `cat $OPENSNITCHDPID` > /dev/null
+ then
+ rm -f $OPENSNITCHDPID
+ fi
+
+ log_end_msg 0
+
+ ;;
+ reload)
+ log_daemon_msg "Reloading $NAME" $NAME
+
+ start-stop-daemon --stop --quiet --signal HUP --pidfile $OPENSNITCHDPID
+
+ log_end_msg 0
+ ;;
+ restart|force-reload)
+ $0 stop
+ sleep 1
+ $0 start
+ ;;
+ status)
+ status_of_proc /usr/bin/$NAME $NAME
+ exit $?
+ ;;
+ *)
+ echo "Usage: /etc/init.d/opensnitchd {start|stop|reload|restart|force-reload|status}"
+ exit 1
+ ;;
+esac
+
+exit 0
--- /dev/null
+daemon/default-config.json etc/opensnitchd/
+daemon/system-fw.json etc/opensnitchd/
+#ebpf_prog/opensnitch.o etc/opensnitchd/
--- /dev/null
+/var/log/opensnitchd.log {
+ rotate 7
+# order of the fields is important
+ maxsize 50M
+# we need this option in order to keep logging
+ copytruncate
+ missingok
+ notifempty
+ delaycompress
+ compress
+ create 640 root root
+ weekly
+}
--- /dev/null
+[Unit]
+Description=OpenSnitch is a GNU/Linux application firewall.
+Documentation=https://github.com/gustavo-iniguez-goya/opensnitch/wiki
+Wants=network.target
+After=network.target
+
+[Service]
+Type=simple
+PermissionsStartOnly=true
+ExecStartPre=/bin/mkdir -p /etc/opensnitchd/rules
+ExecStart=/usr/bin/opensnitchd -rules-path /etc/opensnitchd/rules
+Restart=always
+RestartSec=30
+
+[Install]
+WantedBy=multi-user.target
--- /dev/null
+#!/bin/sh
+
+set -e
+
+autostart_by_default()
+{
+ if [ -f /etc/xdg/autostart -a ! -f /etc/xdg/autostart/opensnitch_ui.desktop ]; then
+ ln -s /usr/share/applications/opensnitch_ui.desktop /etc/xdg/autostart/
+ fi
+}
+
+autostart_by_default
+
+if command -v gtk-update-icon-cache >/dev/null && test -f /usr/share/icons/hicolor/index.theme ; then
+ gtk-update-icon-cache --quiet /usr/share/icons/hicolor/
+fi
+
+#DEBHELPER#
--- /dev/null
+#!/bin/sh
+set -e
+
+case "$1" in
+ purge)
+ if [ -f /etc/xdg/autostart/opensnitch_ui.desktop ];then
+ rm -f /etc/xdg/autostart/opensnitch_ui.desktop
+ fi
+ ;;
+ remove)
+ pkill -15 opensnitch-ui || true
+ ;;
+esac
+
+#DEBHELPER#
--- /dev/null
+#!/usr/bin/make -f
+export DH_VERBOSE = 1
+export DESTDIR := $(shell pwd)/debian/opensnitch
+export UIDESTDIR := $(shell pwd)/debian/python3-opensnitch-ui
+
+override_dh_installsystemd:
+ dh_installsystemd --restart-after-upgrade
+
+override_dh_auto_build:
+ $(MAKE) protocol
+# Workaround for Go build problem when building in _build
+ mkdir -p _build/src/github.com/evilsocket/opensnitch/daemon/ui/protocol/
+ cp daemon/ui/protocol/* _build/src/github.com/evilsocket/opensnitch/daemon/ui/protocol/
+ dh_auto_build
+ cd ui && python3 setup.py build --force
+
+override_dh_auto_install:
+# daemon
+ mkdir -p $(DESTDIR)/usr/bin
+ cp _build/bin/daemon $(DESTDIR)/usr/bin/opensnitchd
+# GUI
+ make -C ui/i18n
+ cp -r ui/i18n/locales/ ui/opensnitch/i18n/
+ pyrcc5 -o ui/opensnitch/resources_rc.py ui/opensnitch/res/resources.qrc
+ sed -i 's/^import ui_pb2/from . import ui_pb2/' ui/opensnitch/ui_pb2*
+ cd ui && python3 setup.py install --force --root=$(UIDESTDIR) --no-compile -O0 --install-layout=deb
+
+# daemon
+ dh_auto_install
+
+%:
+ dh $@ --builddirectory=_build --buildsystem=golang --with=golang,python3
+
+override_dh_auto_clean:
+ dh_auto_clean
+ $(MAKE) clean
+ $(RM) ui/opensnitch/resources_rc.py
+ $(RM) -r ui/opensnitch/i18n/
+ $(RM) ui/i18n/locales/*/*.qm
+ cd ui && python3 setup.py clean -a
+ $(RM) -r ui/opensnitch_ui.egg-info/
+ find ui -name \*.pyc -exec rm {} \;
--- /dev/null
+3.0 (quilt)
--- /dev/null
+extend-diff-ignore="\.egg-info$"
\ No newline at end of file
--- /dev/null
+Tests: test-resources.sh
+Depends: opensnitch
--- /dev/null
+#!/bin/sh
+set -e
+
+ophome="/etc/opensnitchd"
+
+ls -dl $ophome 1>/dev/null
+echo "installed OK: $ophome"
+ls -l $ophome/system-fw.json 1>/dev/null
+echo "installed OK: $ophome/system-fw.json"
+ls -l $ophome/default-config.json 1>/dev/null
+echo "installed OK: $ophome/default-config.json"
+ls -dl $ophome/rules 1>/dev/null
+echo "installed OK: $ophome/rules/"
--- /dev/null
+---
+Name: opensnitch
+Bug-Database: https://github.com/evilsocket/opensnitch/issues
+Bug-Submit: https://github.com/evilsocket/opensnitch/issues/new
+Contact: Gustavo Iñiguez Goia <gooffy1@gmail.com>
+Documentation: https://github.com/evilsocket/opensnitch/wiki
+CPE: cpe:/a:evilsocket:opensnitch
+Repository: https://github.com/evilsocket/opensnitch.git
+Repository-Browse: https://github.com/evilsocket/opensnitch
--- /dev/null
+version=4
+opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/opensnitch-\$1\.tar\.gz/,\
+uversionmangle=s/(\d)[_\.\-\+]?(RC|rc|pre|dev|beta|alpha)[.]?(\d*)$/\$1~\$2\$3/ \
+ https://github.com/evilsocket/opensnitch/tags .*/v?(\d\S*)\.tar\.gz
--- /dev/null
+#taken from /samples/bpf/Makefile and removed all targets
+
+# SPDX-License-Identifier: GPL-2.0
+
+BPF_SAMPLES_PATH ?= $(abspath $(srctree)/$(src))
+TOOLS_PATH := $(BPF_SAMPLES_PATH)/../../tools
+
+# Libbpf dependencies
+LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a
+
+CGROUP_HELPERS := ../../tools/testing/selftests/bpf/cgroup_helpers.o
+TRACE_HELPERS := ../../tools/testing/selftests/bpf/trace_helpers.o
+
+always-y += opensnitch.o
+
+ifeq ($(ARCH), arm)
+# Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux
+# headers when arm instruction set identification is requested.
+ARM_ARCH_SELECTOR := $(filter -D__LINUX_ARM_ARCH__%, $(KBUILD_CFLAGS))
+BPF_EXTRA_CFLAGS := $(ARM_ARCH_SELECTOR)
+TPROGS_CFLAGS += $(ARM_ARCH_SELECTOR)
+endif
+
+TPROGS_CFLAGS += -Wall -O2
+TPROGS_CFLAGS += -Wmissing-prototypes
+TPROGS_CFLAGS += -Wstrict-prototypes
+
+TPROGS_CFLAGS += -I$(objtree)/usr/include
+TPROGS_CFLAGS += -I$(srctree)/tools/testing/selftests/bpf/
+TPROGS_CFLAGS += -I$(srctree)/tools/lib/
+TPROGS_CFLAGS += -I$(srctree)/tools/include
+TPROGS_CFLAGS += -I$(srctree)/tools/perf
+TPROGS_CFLAGS += -DHAVE_ATTR_TEST=0
+
+ifdef SYSROOT
+TPROGS_CFLAGS += --sysroot=$(SYSROOT)
+TPROGS_LDFLAGS := -L$(SYSROOT)/usr/lib
+endif
+
+TPROGCFLAGS_bpf_load.o += -Wno-unused-variable
+
+TPROGS_LDLIBS += $(LIBBPF) -lelf -lz
+TPROGLDLIBS_tracex4 += -lrt
+TPROGLDLIBS_trace_output += -lrt
+TPROGLDLIBS_map_perf_test += -lrt
+TPROGLDLIBS_test_overhead += -lrt
+TPROGLDLIBS_xdpsock += -pthread
+
+# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
+# make M=samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
+LLC ?= llc
+CLANG ?= clang
+LLVM_OBJCOPY ?= llvm-objcopy
+BTF_PAHOLE ?= pahole
+
+# Detect that we're cross compiling and use the cross compiler
+ifdef CROSS_COMPILE
+CLANG_ARCH_ARGS = --target=$(notdir $(CROSS_COMPILE:%-=%))
+endif
+
+# Don't evaluate probes and warnings if we need to run make recursively
+ifneq ($(src),)
+HDR_PROBE := $(shell printf "\#include <linux/types.h>\n struct list_head { int a; }; int main() { return 0; }" | \
+ $(CC) $(TPROGS_CFLAGS) $(TPROGS_LDFLAGS) -x c - \
+ -o /dev/null 2>/dev/null && echo okay)
+
+ifeq ($(HDR_PROBE),)
+$(warning WARNING: Detected possible issues with include path.)
+$(warning WARNING: Please install kernel headers locally (make headers_install).)
+endif
+
+BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help 2>&1 | grep dwarfris)
+BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help 2>&1 | grep BTF)
+BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --help 2>&1 | grep -i 'usage.*llvm')
+BTF_LLVM_PROBE := $(shell echo "int main() { return 0; }" | \
+ $(CLANG) -target bpf -O2 -g -c -x c - -o ./llvm_btf_verify.o; \
+ readelf -S ./llvm_btf_verify.o | grep BTF; \
+ /bin/rm -f ./llvm_btf_verify.o)
+
+BPF_EXTRA_CFLAGS += -fno-stack-protector
+ifneq ($(BTF_LLVM_PROBE),)
+ BPF_EXTRA_CFLAGS += -g
+else
+ifneq ($(and $(BTF_LLC_PROBE),$(BTF_PAHOLE_PROBE),$(BTF_OBJCOPY_PROBE)),)
+ BPF_EXTRA_CFLAGS += -g
+ LLC_FLAGS += -mattr=dwarfris
+ DWARF2BTF = y
+endif
+endif
+endif
+
+# Trick to allow make to be run from this directory
+all:
+ $(MAKE) -C ../../ M=$(CURDIR) BPF_SAMPLES_PATH=$(CURDIR)
+
+clean:
+ $(MAKE) -C ../../ M=$(CURDIR) clean
+ @find $(CURDIR) -type f -name '*~' -delete
+
+$(LIBBPF): FORCE
+# Fix up variables inherited from Kbuild that tools/ build system won't like
+ $(MAKE) -C $(dir $@) RM='rm -rf' EXTRA_CFLAGS="$(TPROGS_CFLAGS)" \
+ LDFLAGS=$(TPROGS_LDFLAGS) srctree=$(BPF_SAMPLES_PATH)/../../ O=
+
+$(obj)/syscall_nrs.h: $(obj)/syscall_nrs.s FORCE
+ $(call filechk,offsets,__SYSCALL_NRS_H__)
+
+targets += syscall_nrs.s
+clean-files += syscall_nrs.h
+
+FORCE:
+
+
+# Verify LLVM compiler tools are available and bpf target is supported by llc
+.PHONY: verify_cmds verify_target_bpf $(CLANG) $(LLC)
+
+verify_cmds: $(CLANG) $(LLC)
+ @for TOOL in $^ ; do \
+ if ! (which -- "$${TOOL}" > /dev/null 2>&1); then \
+ echo "*** ERROR: Cannot find LLVM tool $${TOOL}" ;\
+ exit 1; \
+ else true; fi; \
+ done
+
+verify_target_bpf: verify_cmds
+ @if ! (${LLC} -march=bpf -mattr=help > /dev/null 2>&1); then \
+ echo "*** ERROR: LLVM (${LLC}) does not support 'bpf' target" ;\
+ echo " NOTICE: LLVM version >= 3.7.1 required" ;\
+ exit 2; \
+ else true; fi
+
+$(BPF_SAMPLES_PATH)/*.c: verify_target_bpf $(LIBBPF)
+$(src)/*.c: verify_target_bpf $(LIBBPF)
+
+$(obj)/tracex5_kern.o: $(obj)/syscall_nrs.h
+$(obj)/hbm_out_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
+$(obj)/hbm.o: $(src)/hbm.h
+$(obj)/hbm_edt_kern.o: $(src)/hbm.h $(src)/hbm_kern.h
+
+-include $(BPF_SAMPLES_PATH)/Makefile.target
+
+# asm/sysreg.h - inline assembly used by it is incompatible with llvm.
+# But, there is no easy way to fix it, so just exclude it since it is
+# useless for BPF samples.
+$(obj)/%.o: $(src)/%.c
+ @echo " CLANG-bpf " $@
+ $(Q)$(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(BPF_EXTRA_CFLAGS) \
+ -I$(obj) -I$(srctree)/tools/testing/selftests/bpf/ \
+ -I$(srctree)/tools/lib/ \
+ -D__KERNEL__ -D__BPF_TRACING__ -Wno-unused-value -Wno-pointer-sign \
+ -D__TARGET_ARCH_$(SRCARCH) -Wno-compare-distinct-pointer-types \
+ -Wno-gnu-variable-sized-type-not-at-end \
+ -Wno-address-of-packed-member -Wno-tautological-compare \
+ -Wno-unknown-warning-option $(CLANG_ARCH_ARGS) \
+ -I$(srctree)/samples/bpf/ -include asm_goto_workaround.h \
+ -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf $(LLC_FLAGS) -filetype=obj -o $@
+ifeq ($(DWARF2BTF),y)
+ $(BTF_PAHOLE) -J $@
+endif
--- /dev/null
+opensnitch.c is an eBPF program. Compilation requires getting kernel source.
+
+sudo apt install clang llvm libelf-dev libzip-dev flex bison libssl-dev bc rsync python3
+cd opensnitch
+wget https://github.com/torvalds/linux/archive/v5.8.tar.gz
+tar -xf v5.8.tar.gz
+patch linux-5.8/tools/lib/bpf/bpf_helpers.h < ebpf_prog/file.patch
+cp ebpf_prog/opensnitch.c ebpf_prog/Makefile linux-5.8/samples/bpf
+cd linux-5.8 && yes "" | make oldconfig && make prepare && make headers_install # (1 min)
+cd samples/bpf && make
+objdump -h opensnitch.o #you should see many section, number 1 should be called kprobe/tcp_v4_connect
+llvm-strip -g opensnitch.o #remove debug info
+sudo cp opensnitch.o /etc/opensnitchd/
+cd ../../../daemon
+
+--opensnitchd expects to find opensnitch.o in /etc/opensnitchd/
+--start opensnitchd with:
+
+opensnitchd -rules-path /etc/opensnitchd/rules -process-monitor-method ebpf
+
+The kernel where you intend to run it must have some options activated:
+
+$ grep BPF /boot/config-$(uname -r)
+CONFIG_CGROUP_BPF=y
+CONFIG_BPF=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_EVENTS=y
+CONFIG_KPROBES=y
+CONFIG_KPROBE_EVENTS=y
--- /dev/null
+--- ../../arch/arm/include/asm/unified.h 2021-04-20 10:47:54.075834124 +0000
++++ ../../arch/arm/include/asm/unified-clang-fix.h 2021-04-20 10:47:38.943811970 +0000
+@@ -11,7 +11,10 @@
+ #if defined(__ASSEMBLY__)
+ .syntax unified
+ #else
+-__asm__(".syntax unified");
++//__asm__(".syntax unified");
++#ifndef __clang__
++ __asm__(".syntax unified");
++#endif
+ #endif
+
+ #ifdef CONFIG_CPU_V7M
--- /dev/null
+--- linux-5.8/tools/lib/bpf/bpf_helpers.h 2020-08-03 00:21:45.000000000 +0300
++++ linux-5.8/tools/lib/bpf/bpf_helpersnew.h 2021-02-23 18:45:21.789624834 +0300
+@@ -54,7 +54,7 @@
+ * Helper structure used by eBPF C program
+ * to describe BPF map attributes to libbpf loader
+ */
+-struct bpf_map_def {
++struct bpf_map_defold {
+ unsigned int type;
+ unsigned int key_size;
+ unsigned int value_size;
--- /dev/null
+#define KBUILD_MODNAME "dummy"
+
+//uncomment if building on x86_32
+//#define OPENSNITCH_x86_32
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/version.h>
+#include <uapi/linux/bpf.h>
+#include <uapi/linux/tcp.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <net/sock.h>
+#include <net/udp_tunnel.h>
+#include <net/inet_sock.h>
+
+#define MAPSIZE 12000
+
+//-------------------------------map definitions
+// which github.com/iovisor/gobpf/elf expects
+#define BUF_SIZE_MAP_NS 256
+
+typedef struct bpf_map_def {
+ unsigned int type;
+ unsigned int key_size;
+ unsigned int value_size;
+ unsigned int max_entries;
+ unsigned int map_flags;
+ unsigned int pinning;
+ char namespace[BUF_SIZE_MAP_NS];
+} bpf_map_def;
+
+enum bpf_pin_type {
+ PIN_NONE = 0,
+ PIN_OBJECT_NS,
+ PIN_GLOBAL_NS,
+ PIN_CUSTOM_NS,
+};
+//-----------------------------------
+
+// even though we only need 32 bits of pid, on x86_32 ebpf verifier complained when pid type was set to u32
+typedef u64 pid_size_t;
+typedef u64 uid_size_t;
+
+struct tcp_key_t {
+ u16 sport;
+ u32 daddr;
+ u16 dport;
+ u32 saddr;
+}__attribute__((packed));
+
+struct tcp_value_t{
+ pid_size_t pid;
+ uid_size_t uid;
+ u64 counter;
+}__attribute__((packed));
+
+// not using unsigned __int128 because it is not supported on x86_32
+struct ipV6 {
+ u64 part1;
+ u64 part2;
+}__attribute__((packed));
+
+struct tcpv6_key_t {
+ u16 sport;
+ struct ipV6 daddr;
+ u16 dport;
+ struct ipV6 saddr;
+}__attribute__((packed));
+
+struct tcpv6_value_t{
+ pid_size_t pid;
+ uid_size_t uid;
+ u64 counter;
+}__attribute__((packed));;
+
+struct udp_key_t {
+ u16 sport;
+ u32 daddr;
+ u16 dport;
+ u32 saddr;
+} __attribute__((packed));
+
+struct udp_value_t{
+ pid_size_t pid;
+ uid_size_t uid;
+ u64 counter;
+}__attribute__((packed));
+
+struct udpv6_key_t {
+ u16 sport;
+ struct ipV6 daddr;
+ u16 dport;
+ struct ipV6 saddr;
+}__attribute__((packed));
+
+struct udpv6_value_t{
+ pid_size_t pid;
+ uid_size_t uid;
+ u64 counter;
+}__attribute__((packed));
+
+
+// on x86_32 "struct sock" is arranged differently from x86_64 (at least on Debian kernels).
+// We hardcode offsets of IP addresses.
+struct sock_on_x86_32_t {
+ u8 data_we_dont_care_about[40];
+ struct ipV6 daddr;
+ struct ipV6 saddr;
+};
+
+
+// Add +1,+2,+3 etc. to map size helps to easier distinguish maps in bpftool's output
+struct bpf_map_def SEC("maps/tcpMap") tcpMap = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(struct tcp_key_t),
+ .value_size = sizeof(struct tcp_value_t),
+ .max_entries = MAPSIZE+1,
+};
+struct bpf_map_def SEC("maps/tcpv6Map") tcpv6Map = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(struct tcpv6_key_t),
+ .value_size = sizeof(struct tcpv6_value_t),
+ .max_entries = MAPSIZE+2,
+};
+struct bpf_map_def SEC("maps/udpMap") udpMap = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(struct udp_key_t),
+ .value_size = sizeof(struct udp_value_t),
+ .max_entries = MAPSIZE+3,
+};
+struct bpf_map_def SEC("maps/udpv6Map") udpv6Map = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(struct udpv6_key_t),
+ .value_size = sizeof(struct udpv6_value_t),
+ .max_entries = MAPSIZE+4,
+};
+
+// for TCP the IP-tuple can be copied from "struct sock" only upon return from tcp_connect().
+// We stash the socket here to look it up upon return.
+struct bpf_map_def SEC("maps/tcpsock") tcpsock = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(u64),
+ .value_size = sizeof(u64),// using u64 instead of sizeof(struct sock *)
+ // to avoid pointer size related quirks on x86_32
+ .max_entries = 100,
+};
+struct bpf_map_def SEC("maps/tcpv6sock") tcpv6sock = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(u64),
+ .value_size = sizeof(u64),
+ .max_entries = 100,
+};
+
+// //counts how many connections we've processed. Starts at 0.
+struct bpf_map_def SEC("maps/tcpcounter") tcpcounter = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(u32),
+ .value_size = sizeof(u64),
+ .max_entries = 1,
+};
+struct bpf_map_def SEC("maps/tcpv6counter") tcpv6counter = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(u32),
+ .value_size = sizeof(u64),
+ .max_entries = 1,
+};
+struct bpf_map_def SEC("maps/udpcounter") udpcounter = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(u32),
+ .value_size = sizeof(u64),
+ .max_entries = 1,
+};
+struct bpf_map_def SEC("maps/udpv6counter") udpv6counter = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(u32),
+ .value_size = sizeof(u64),
+ .max_entries = 1,
+};
+struct bpf_map_def SEC("maps/debugcounter") debugcounter = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(u32),
+ .value_size = sizeof(u64),
+ .max_entries = 1,
+};
+
+// size 150 gave ebpf verifier errors for kernel 4.14, 100 is ok
+// we can cast any struct into rawBytes_t to be able to access arbitrary bytes of the struct
+struct rawBytes_t {
+ u8 bytes[100];
+};
+
+
+//used for debug purposes only
+struct bpf_map_def SEC("maps/bytes") bytes = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(u32),
+ .value_size = sizeof(u32),
+ .max_entries = 222,
+};
+
+//used for debug purposes only
+struct bpf_map_def SEC("maps/debug") debug = {
+ .type = BPF_MAP_TYPE_HASH,
+ .key_size = sizeof(struct tcpv6_key_t),
+ .value_size = sizeof(struct rawBytes_t),
+ .max_entries = 555,
+};
+
+
+// initializing variables with __builtin_memset() is required
+// for compatibility with bpf on kernel 4.4
+
+SEC("kprobe/tcp_v4_connect")
+int kprobe__tcp_v4_connect(struct pt_regs *ctx)
+{
+ #ifdef OPENSNITCH_x86_32
+ // On x86_32 platforms I couldn't get function arguments using PT_REGS_PARM1
+ // that's why we are accessing registers directly
+ struct sock *sk = (struct sock *)((ctx)->ax);
+ #else
+ struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);
+ #endif
+
+ u64 skp = (u64)sk;
+ u64 pid_tgid = bpf_get_current_pid_tgid();
+ bpf_map_update_elem(&tcpsock, &pid_tgid, &skp, BPF_ANY);
+ return 0;
+};
+
+SEC("kretprobe/tcp_v4_connect")
+int kretprobe__tcp_v4_connect(struct pt_regs *ctx)
+{
+ u64 pid_tgid = bpf_get_current_pid_tgid();
+ u64 *skp = bpf_map_lookup_elem(&tcpsock, &pid_tgid);
+ if (skp == NULL) {return 0;}
+
+ struct sock *sk;
+ __builtin_memset(&sk, 0, sizeof(sk));
+ sk = (struct sock *)*skp;
+
+ struct tcp_key_t tcp_key;
+ __builtin_memset(&tcp_key, 0, sizeof(tcp_key));
+ bpf_probe_read(&tcp_key.dport, sizeof(tcp_key.dport), &sk->__sk_common.skc_dport);
+ bpf_probe_read(&tcp_key.sport, sizeof(tcp_key.sport), &sk->__sk_common.skc_num);
+ bpf_probe_read(&tcp_key.daddr, sizeof(tcp_key.daddr), &sk->__sk_common.skc_daddr);
+ bpf_probe_read(&tcp_key.saddr, sizeof(tcp_key.saddr), &sk->__sk_common.skc_rcv_saddr);
+
+ u32 zero_key = 0;
+ u64 *val = bpf_map_lookup_elem(&tcpcounter, &zero_key);
+ if (val == NULL){return 0;}
+
+ struct tcp_value_t tcp_value;
+ __builtin_memset(&tcp_value, 0, sizeof(tcp_value));
+ tcp_value.pid = pid_tgid >> 32;
+ tcp_value.uid = bpf_get_current_uid_gid() & 0xffffffff;
+ tcp_value.counter = *val;
+ bpf_map_update_elem(&tcpMap, &tcp_key, &tcp_value, BPF_ANY);
+
+ u64 newval = *val + 1;
+ bpf_map_update_elem(&tcpcounter, &zero_key, &newval, BPF_ANY);
+ bpf_map_delete_elem(&tcpsock, &pid_tgid);
+ return 0;
+};
+
+
+SEC("kprobe/tcp_v6_connect")
+int kprobe__tcp_v6_connect(struct pt_regs *ctx)
+{
+ #ifdef OPENSNITCH_x86_32
+ struct sock *sk = (struct sock *)((ctx)->ax);
+ #else
+ struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);
+ #endif
+
+ u64 skp = (u64)sk;
+ u64 pid_tgid = bpf_get_current_pid_tgid();
+ bpf_map_update_elem(&tcpv6sock, &pid_tgid, &skp, BPF_ANY);
+ return 0;
+};
+
+SEC("kretprobe/tcp_v6_connect")
+int kretprobe__tcp_v6_connect(struct pt_regs *ctx)
+{
+ u64 pid_tgid = bpf_get_current_pid_tgid();
+ u64 *skp = bpf_map_lookup_elem(&tcpv6sock, &pid_tgid);
+ if (skp == NULL) {return 0;}
+ struct sock *sk;
+ __builtin_memset(&sk, 0, sizeof(sk));
+ sk = (struct sock *)*skp;
+
+ struct tcpv6_key_t tcpv6_key;
+ __builtin_memset(&tcpv6_key, 0, sizeof(tcpv6_key));
+ bpf_probe_read(&tcpv6_key.dport, sizeof(tcpv6_key.dport), &sk->__sk_common.skc_dport);
+ bpf_probe_read(&tcpv6_key.sport, sizeof(tcpv6_key.sport), &sk->__sk_common.skc_num);
+ #ifdef OPENSNITCH_x86_32
+ struct sock_on_x86_32_t sock;
+ __builtin_memset(&sock, 0, sizeof(sock));
+ bpf_probe_read(&sock, sizeof(sock), *(&sk));
+ tcpv6_key.daddr = sock.daddr;
+ tcpv6_key.saddr = sock.saddr;
+ #else
+ bpf_probe_read(&tcpv6_key.daddr, sizeof(tcpv6_key.daddr), &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
+ bpf_probe_read(&tcpv6_key.saddr, sizeof(tcpv6_key.saddr), &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
+ #endif
+
+ u32 zero_key = 0;
+ u64 *val = bpf_map_lookup_elem(&tcpv6counter, &zero_key);
+ if (val == NULL){return 0;}
+
+ struct tcpv6_value_t tcpv6_value;
+ __builtin_memset(&tcpv6_value, 0, sizeof(tcpv6_value));
+ tcpv6_value.pid = pid_tgid >> 32;
+ tcpv6_value.uid = bpf_get_current_uid_gid() & 0xffffffff;
+ tcpv6_value.counter = *val;
+ bpf_map_update_elem(&tcpv6Map, &tcpv6_key, &tcpv6_value, BPF_ANY);
+
+ u64 newval = *val + 1;
+ bpf_map_update_elem(&tcpv6counter, &zero_key, &newval, BPF_ANY);
+ bpf_map_delete_elem(&tcpv6sock, &pid_tgid);
+ return 0;
+};
+
+
+SEC("kprobe/udp_sendmsg")
+int kprobe__udp_sendmsg(struct pt_regs *ctx)
+{
+ #ifdef OPENSNITCH_x86_32
+ struct sock *sk = (struct sock *)((ctx)->ax);
+ struct msghdr *msg = (struct msghdr *)((ctx)->dx);
+ #else
+ struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);
+ struct msghdr *msg = (struct msghdr *)PT_REGS_PARM2(ctx);
+ #endif
+
+ u64 msg_name; //pointer
+ __builtin_memset(&msg_name, 0, sizeof(msg_name));
+ bpf_probe_read(&msg_name, sizeof(msg_name), &msg->msg_name);
+ struct sockaddr_in * usin = (struct sockaddr_in *)msg_name;
+
+ struct udp_key_t udp_key;
+ __builtin_memset(&udp_key, 0, sizeof(udp_key));
+ bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &usin->sin_port);
+ if (udp_key.dport != 0){ //likely
+ bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &usin->sin_addr.s_addr);
+ }
+ else {
+ //very rarely dport can be found in skc_dport
+ bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &sk->__sk_common.skc_dport);
+ bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &sk->__sk_common.skc_daddr);
+ }
+ bpf_probe_read(&udp_key.sport, sizeof(udp_key.sport), &sk->__sk_common.skc_num);
+ bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &sk->__sk_common.skc_rcv_saddr);
+
+ u32 zero_key = 0;
+ __builtin_memset(&zero_key, 0, sizeof(zero_key));
+ u64 *counterVal = bpf_map_lookup_elem(&udpcounter, &zero_key);
+ if (counterVal == NULL){return 0;}
+ struct udp_value_t *lookedupValue = bpf_map_lookup_elem(&udpMap, &udp_key);
+ u64 pid = bpf_get_current_pid_tgid() >> 32;
+ if ( lookedupValue == NULL || lookedupValue->pid != pid) {
+ struct udp_value_t udp_value;
+ __builtin_memset(&udp_value, 0, sizeof(udp_value));
+ udp_value.pid = pid;
+ udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff;
+ udp_value.counter = *counterVal;
+ bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY);
+
+ u64 newval = *counterVal + 1;
+ bpf_map_update_elem(&udpcounter, &zero_key, &newval, BPF_ANY);
+ }
+ //else nothing to do
+ return 0;
+
+};
+
+
+SEC("kprobe/udpv6_sendmsg")
+int kprobe__udpv6_sendmsg(struct pt_regs *ctx)
+{
+ #ifdef OPENSNITCH_x86_32
+ struct sock *sk = (struct sock *)((ctx)->ax);
+ struct msghdr *msg = (struct msghdr *)((ctx)->dx);
+ #else
+ struct sock *sk = (struct sock *)PT_REGS_PARM1(ctx);
+ struct msghdr *msg = (struct msghdr *)PT_REGS_PARM2(ctx);
+ #endif
+
+ u64 msg_name; //a pointer
+ __builtin_memset(&msg_name, 0, sizeof(msg_name));
+ bpf_probe_read(&msg_name, sizeof(msg_name), &msg->msg_name);
+
+ struct udpv6_key_t udpv6_key;
+ __builtin_memset(&udpv6_key, 0, sizeof(udpv6_key));
+ bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &sk->__sk_common.skc_dport);
+ if (udpv6_key.dport != 0){ //likely
+ bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32);
+ }
+ else {
+ struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *)msg_name;
+ bpf_probe_read(&udpv6_key.dport, sizeof(udpv6_key.dport), &sin6->sin6_port);
+ bpf_probe_read(&udpv6_key.daddr, sizeof(udpv6_key.daddr), &sin6->sin6_addr.in6_u.u6_addr32);
+ }
+
+ bpf_probe_read(&udpv6_key.sport, sizeof(udpv6_key.sport), &sk->__sk_common.skc_num);
+ bpf_probe_read(&udpv6_key.saddr, sizeof(udpv6_key.saddr), &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
+
+
+ #ifdef OPENSNITCH_x86_32
+ struct sock_on_x86_32_t sock;
+ __builtin_memset(&sock, 0, sizeof(sock));
+ bpf_probe_read(&sock, sizeof(sock), *(&sk));
+ udpv6_key.daddr = sock.daddr;
+ udpv6_key.saddr = sock.saddr;
+ #endif
+
+ u32 zero_key = 0;
+ u64 *counterVal = bpf_map_lookup_elem(&udpv6counter, &zero_key);
+ if (counterVal == NULL){return 0;}
+ struct udpv6_value_t *lookedupValue = bpf_map_lookup_elem(&udpv6Map, &udpv6_key);
+ u64 pid = bpf_get_current_pid_tgid() >> 32;
+ if ( lookedupValue == NULL || lookedupValue->pid != pid) {
+ struct udpv6_value_t udpv6_value;
+ __builtin_memset(&udpv6_value, 0, sizeof(udpv6_value));
+ udpv6_value.pid = pid;
+ udpv6_value.uid = bpf_get_current_uid_gid() & 0xffffffff;
+ udpv6_value.counter = *counterVal;
+ bpf_map_update_elem(&udpv6Map, &udpv6_key, &udpv6_value, BPF_ANY);
+ u64 newval = *counterVal + 1;
+ bpf_map_update_elem(&udpv6counter, &zero_key, &newval, BPF_ANY);
+ }
+ //else nothing to do
+ return 0;
+
+};
+
+SEC("kprobe/iptunnel_xmit")
+int kprobe__iptunnel_xmit(struct pt_regs *ctx)
+{
+ #ifdef OPENSNITCH_x86_32
+ // TODO
+ return 0;
+ #else
+ struct sk_buff *skb = (struct sk_buff *)PT_REGS_PARM3(ctx);
+ u32 src = (u32)PT_REGS_PARM4(ctx);
+ u32 dst = (u32)PT_REGS_PARM5(ctx);
+ #endif
+
+ u16 sport = 0;
+ unsigned char *head;
+ u16 pkt_hdr;
+ __builtin_memset(&head, 0, sizeof(head));
+ __builtin_memset(&pkt_hdr, 0, sizeof(pkt_hdr));
+ bpf_probe_read(&head, sizeof(head), &skb->head);
+ bpf_probe_read(&pkt_hdr, sizeof(pkt_hdr), &skb->transport_header);
+ struct udphdr *udph;
+ __builtin_memset(&udph, 0, sizeof(udph));
+
+ udph = (struct udphdr *)(head + pkt_hdr);
+ bpf_probe_read(&sport, sizeof(sport), &udph->source);
+ sport = (sport >> 8) | ((sport << 8) & 0xff00);
+
+ struct udp_key_t udp_key;
+ struct udp_value_t udp_value;
+ u32 zero_key = 0;
+ __builtin_memset(&udp_key, 0, sizeof(udp_key));
+ __builtin_memset(&udp_value, 0, sizeof(udp_value));
+
+ bpf_probe_read(&udp_key.sport, sizeof(udp_key.sport), &sport);
+ bpf_probe_read(&udp_key.dport, sizeof(udp_key.dport), &udph->dest);
+ bpf_probe_read(&udp_key.saddr, sizeof(udp_key.saddr), &src);
+ bpf_probe_read(&udp_key.daddr, sizeof(udp_key.daddr), &dst);
+
+ u64 *counterVal = bpf_map_lookup_elem(&udpcounter, &zero_key);
+ if (counterVal == NULL){return 0;}
+
+ struct udp_value_t *lookedupValue = bpf_map_lookup_elem(&udpMap, &udp_key);
+ u64 pid = bpf_get_current_pid_tgid() >> 32;
+ if ( lookedupValue == NULL || lookedupValue->pid != pid) {
+ udp_value.pid = pid;
+ udp_value.uid = bpf_get_current_uid_gid() & 0xffffffff;
+ udp_value.counter = *counterVal;
+ bpf_map_update_elem(&udpMap, &udp_key, &udp_value, BPF_ANY);
+ u64 newval = *counterVal + 1;
+ bpf_map_update_elem(&udpcounter, &zero_key, &newval, BPF_ANY);
+ }
+
+ return 0;
+
+};
+
+// debug only: increment key's value by 1 in map "bytes"
+void increment(u32 key){
+ u32 *lookedupValue = bpf_map_lookup_elem(&bytes, &key);
+ if (lookedupValue == NULL){
+ u32 zero = 0;
+ bpf_map_update_elem(&bytes, &key, &zero, BPF_ANY);
+ }
+ else {
+ u32 newval = *lookedupValue + 1;
+ bpf_map_update_elem(&bytes, &key, &newval, BPF_ANY);
+ }
+}
+
+char _license[] SEC("license") = "GPL";
+// this number will be interpreted by the elf loader
+// to set the current running kernel version
+u32 _version SEC("version") = 0xFFFFFFFE;
--- /dev/null
+all: ../daemon/ui/protocol/ui.pb.go ../ui/opensnitch/ui_pb2.py
+
+../daemon/ui/protocol/ui.pb.go: ui.proto
+ protoc -I. ui.proto --go_out=../daemon/ui/protocol/ --go-grpc_out=../daemon/ui/protocol/ --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative
+
+../ui/opensnitch/ui_pb2.py: ui.proto
+ python3 -m grpc_tools.protoc -I. --python_out=../ui/opensnitch/ --grpc_python_out=../ui/opensnitch/ ui.proto
+
+clean:
+ @rm -rf ../daemon/ui/protocol/ui.pb.go
+ @rm -rf ../daemon/ui/protocol/ui_grpc.pb.go
+ @rm -rf ../ui/opensnitch/ui_pb2.py
+ @rm -rf ../ui/opensnitch/ui_pb2_grpc.py
+
--- /dev/null
+syntax = "proto3";
+
+package protocol;
+
+option go_package = "github.com/evilsocket/opensnitch/daemon/ui/protocol";
+
+service UI {
+ rpc Ping(PingRequest) returns (PingReply) {}
+ rpc AskRule (Connection) returns (Rule) {}
+ rpc Subscribe (ClientConfig) returns (ClientConfig) {}
+ rpc Notifications (stream NotificationReply) returns (stream Notification) {}
+}
+
+message Event {
+ string time = 1;
+ Connection connection = 2;
+ Rule rule = 3;
+ int64 unixnano = 4;
+}
+
+message Statistics {
+ string daemon_version = 1;
+ uint64 rules = 2;
+ uint64 uptime = 3;
+ uint64 dns_responses = 4;
+ uint64 connections = 5;
+ uint64 ignored = 6;
+ uint64 accepted = 7;
+ uint64 dropped = 8;
+ uint64 rule_hits = 9;
+ uint64 rule_misses = 10;
+ map<string, uint64> by_proto = 11;
+ map<string, uint64> by_address = 12;
+ map<string, uint64> by_host = 13;
+ map<string, uint64> by_port = 14;
+ map<string, uint64> by_uid = 15;
+ map<string, uint64> by_executable = 16;
+ repeated Event events = 17;
+}
+
+message PingRequest {
+ uint64 id = 1;
+ Statistics stats = 2;
+}
+
+message PingReply {
+ uint64 id = 1;
+}
+
+message Connection {
+ string protocol = 1;
+ string src_ip = 2;
+ uint32 src_port = 3;
+ string dst_ip = 4;
+ string dst_host = 5;
+ uint32 dst_port = 6;
+ uint32 user_id = 7;
+ uint32 process_id = 8;
+ string process_path = 9;
+ string process_cwd = 10;
+ repeated string process_args = 11;
+ map<string, string> process_env = 12;
+}
+
+message Operator {
+ string type = 1;
+ string operand = 2;
+ string data = 3;
+ bool sensitive = 4;
+}
+
+message Rule {
+ string name = 1;
+ bool enabled = 2;
+ bool precedence = 3;
+ string action = 4;
+ string duration = 5;
+ Operator operator = 6;
+}
+
+enum Action {
+ NONE = 0;
+ LOAD_FIREWALL = 1;
+ UNLOAD_FIREWALL = 2;
+ CHANGE_CONFIG = 3;
+ ENABLE_RULE = 4;
+ DISABLE_RULE = 5;
+ DELETE_RULE = 6;
+ CHANGE_RULE = 7;
+ LOG_LEVEL = 8;
+ STOP = 9;
+ MONITOR_PROCESS = 10;
+ STOP_MONITOR_PROCESS = 11;
+}
+
+// client configuration sent on Subscribe()
+message ClientConfig {
+ uint64 id = 1;
+ string name = 2;
+ string version = 3;
+ bool isFirewallRunning = 4;
+ // daemon configuration as json string
+ string config = 5;
+ uint32 logLevel = 6;
+ repeated Rule rules = 7;
+}
+
+// notification sent to the clients (daemons)
+message Notification {
+ uint64 id = 1;
+ string clientName = 2;
+ string serverName = 3;
+ // CHANGE_CONFIG: 2, data: {"default_timeout": 1, ...}
+ Action type = 4;
+ string data = 5;
+ repeated Rule rules = 6;
+}
+
+// notification reply sent to the server (GUI)
+message NotificationReply {
+ uint64 id = 1;
+ NotificationReplyCode code = 2;
+ string data = 3;
+}
+
+enum NotificationReplyCode {
+ OK = 0;
+ ERROR = 1;
+}
--- /dev/null
+#!/bin/bash
+# nothing to see here, just a utility i use to create new releases ^_^
+
+CURRENT_VERSION=$(cat daemon/core/version.go | grep Version | cut -d '"' -f 2)
+TO_UPDATE=(
+ daemon/core/version.go
+ ui/version.py
+)
+
+echo -n "Current version is $CURRENT_VERSION, select new version: "
+read NEW_VERSION
+echo "Creating version $NEW_VERSION ...\n"
+
+for file in "${TO_UPDATE[@]}"
+do
+ echo "Patching $file ..."
+ sed -i "s/$CURRENT_VERSION/$NEW_VERSION/g" $file
+ git add $file
+done
+
+git commit -m "Releasing v$NEW_VERSION"
+git push
+
+git tag -a v$NEW_VERSION -m "Release v$NEW_VERSION"
+git push origin v$NEW_VERSION
+
+echo
+echo "All done, v$NEW_VERSION released ^_^"
--- /dev/null
+*.pyc
+build
+dist
+*.egg-info
+__pycache__
--- /dev/null
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Source: https://github.com/gustavo-iniguez-goya/opensnitch
+Upstream-Name: python3-opensnitch-ui
+Files: *
+Copyright:
+ 2017-2018 evilsocket
+ 2019-2020 Gustavo Iñiguez Goia
+Comment: Debian packaging is licensed under the same terms as upstream
+License: GPL-3.0
+ This program is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later
+ version.
+ .
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the GNU General Public License for more
+ details.
+ .
+ You should have received a copy of the GNU General Public
+ License along with this program. If not, If not, see
+ http://www.gnu.org/licenses/.
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 3 can be found in the file
+ '/usr/share/common-licenses/GPL-3'.
--- /dev/null
+recursive-include opensnitch/res *
+recursive-include opensnitch/i18n *.qm
+include LICENSE
--- /dev/null
+all: opensnitch/resources_rc.py
+
+install:
+
+opensnitch/resources_rc.py: translations # deps
+ @pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc
+ sed -i 's/^import ui_pb2/from . import ui_pb2/' opensnitch/ui_pb2*
+
+translations:
+ @$(MAKE) -C i18n
+
+deps:
+ @pip3 install -r requirements.txt
+
+clean:
+ @rm -rf *.pyc
+ @rm -rf opensnitch/resources_rc.py
--- /dev/null
+#!/usr/bin/env python3
+
+from PyQt5 import QtWidgets, QtGui, QtCore
+
+import sys
+import os
+import time
+import signal
+import argparse
+import logging
+from threading import Timer
+
+logging.getLogger().disabled = True
+
+from concurrent import futures
+
+import grpc
+
+dist_path = '/usr/lib/python3/dist-packages/'
+if dist_path not in sys.path:
+ sys.path.append(dist_path)
+
+from opensnitch.service import UIService
+from opensnitch.config import Config
+from opensnitch.utils import Themes
+import opensnitch.version
+import opensnitch.ui_pb2
+from opensnitch.ui_pb2_grpc import add_UIServicer_to_server
+
+def on_exit():
+ server.stop(0)
+ app.quit()
+ sys.exit(0)
+
+def supported_qt_version(major, medium, minor):
+ q = QtCore.QT_VERSION_STR.split(".")
+ return int(q[0]) >= major and int(q[1]) >= medium and int(q[2]) >= minor
+
+def load_translations():
+ locale = QtCore.QLocale.system()
+ i18n_path = os.path.dirname(os.path.realpath(opensnitch.__file__)) + "/i18n"
+ print("Loading translations:", i18n_path, "locale:", locale.name())
+ translator = QtCore.QTranslator()
+ translator.load(i18n_path + "/" + locale.name() + "/opensnitch-" + locale.name() + ".qm")
+
+ return translator
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='OpenSnitch UI service.')
+ parser.add_argument("--socket", dest="socket", default="unix:///tmp/osui.sock", help="Path of the unix socket for the gRPC service (https://github.com/grpc/grpc/blob/master/doc/naming.md).", metavar="FILE")
+ parser.add_argument("--max-clients", dest="serverWorkers", default=10, help="Max number of allowed clients (incoming connections).")
+
+ args = parser.parse_args()
+
+ os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
+ if supported_qt_version(5,6,0):
+ try:
+ # NOTE: maybe we also need Qt::AA_UseHighDpiPixmaps
+ QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True)
+ except Exception:
+ pass
+
+ translator = load_translations()
+ app = QtWidgets.QApplication(sys.argv)
+ app.installTranslator(translator)
+ thm = Themes.instance()
+ thm.load_theme(app)
+
+ service = UIService(app, on_exit)
+ # @doc: https://grpc.github.io/grpc/python/grpc.html#server-object
+ server = grpc.server(futures.ThreadPoolExecutor(),
+ options=(
+ # https://github.com/grpc/grpc/blob/master/doc/keepalive.md
+ # https://grpc.github.io/grpc/core/group__grpc__arg__keys.html
+ # send keepalive ping every 5 second, default is 2 hours)
+ ('grpc.keepalive_time_ms', 5000),
+ # after 5s of inactivity, wait 20s and close the connection if
+ # there's no response.
+ ('grpc.keepalive_timeout_ms', 20000),
+ ('grpc.keepalive_permit_without_calls', True),
+ ))
+
+ add_UIServicer_to_server(service, server)
+
+ if args.socket.startswith("unix://"):
+ socket = args.socket[7:]
+ socket = os.path.abspath(socket)
+ server.add_insecure_port("unix:%s" % socket)
+ else:
+ server.add_insecure_port(args.socket)
+
+ # https://stackoverflow.com/questions/5160577/ctrl-c-doesnt-work-with-pyqt
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
+
+ try:
+ # print "OpenSnitch UI service running on %s ..." % socket
+ server.start()
+ app.exec_()
+ except KeyboardInterrupt:
+ on_exit()
+
--- /dev/null
+SOURCES += ../opensnitch/service.py \
+ ../opensnitch/dialogs/prompt.py \
+ ../opensnitch/dialogs/preferences.py \
+ ../opensnitch/dialogs/ruleseditor.py \
+ ../opensnitch/dialogs/processdetails.py \
+ ../opensnitch/dialogs/stats.py
+
+FORMS += ../opensnitch/res/prompt.ui \
+ ../opensnitch/res/ruleseditor.ui \
+ ../opensnitch/res/preferences.ui \
+ ../opensnitch/res/process_details.ui \
+ ../opensnitch/res/stats.ui
+
+#TSFILES contains all *.ts files in locales/ and its subfolders
+TSFILES := $(shell find locales/ -type f -name '*.ts')
+#QMFILES contains all *.qm files in locales/ and its subfolders
+QMFILES := $(shell find locales/ -type f -name '*.qm')
+#if QMFILES is empty, we set it to phony target to run unconditionally
+ifeq ($(QMFILES),)
+QMFILES := "qmfiles"
+endif
+
+all: $(TSFILES) $(QMFILES)
+
+#if any file from SOURCES or FORMS is older than any file from $(TSFILES)
+#or if opensnitch_i18n.pro was manually modified
+$(TSFILES): $(SOURCES) $(FORMS) opensnitch_i18n.pro
+ @pylupdate5 opensnitch_i18n.pro
+
+#if any of the *.ts files are older that any of the *.qm files
+#QMFILES may also be a phony target (when no *.qm exist yet) which will always run
+$(QMFILES):$(TSFILES)
+ @./generate_i18n.sh
+ for lang in $$(ls locales/); do \
+ if [ ! -d ../opensnitch/i18n/$$lang ]; then mkdir -p ../opensnitch/i18n/$$lang ; fi ; \
+ cp locales/$$lang/opensnitch-$$lang.qm ../opensnitch/i18n/$$lang/ ; \
+ done
--- /dev/null
+
+### Adding a new translation:
+0. Install needed packages: `apt install qtchooser pyqt5-dev-tools`
+1. mkdir `locales/<YOUR LOCALE>/`
+ (echo $LANG)
+2. add the path to opensnitch_i18n.pro:
+```
+ TRANSLATIONS += locales/es_ES/opensnitch-es_ES.ts \
+ locales/<YOUR LOCALE>/opensnitch-<YOUR LOCALE>.ts
+```
+3. make
+
+### Updating translations:
+
+1. update translations definitions:
+ - pylupdate5 opensnitch_i18n.pro
+
+2. translate a language:
+ - linguist locales/es_ES/opensnitch-es_ES.ts
+
+3. create .qm file:
+ - lrelease locales/es_ES/opensnitch-es_ES.ts -qm locales/es_ES/opensnitch-es_ES.qm
+
+or:
+
+1. make
+2. linguist locales/es_ES/opensnitch-es_ES.ts
+3. make
+
+### Installing translations (manually)
+
+In order to test a new translation:
+
+`mkdir -p /usr/lib/python3/dist-packages/opensnitch/i18n/<YOUR LOCALE>/`
+`cp locales/<YOUR LOCALE>/opensnitch-<YOUR LOCALE>.qm /usr/lib/python3/dist-packages/opensnitch/i18n/<YOUR LOCALE>/`
+
+Note: the destination path may vary depending on your system.
--- /dev/null
+#!/bin/sh
+
+app_name="opensnitch"
+langs_dir="./locales"
+lrelease="lrelease"
+if ! command -v $lrelease; then
+ # fedora
+ lrelease="lrelease-qt5"
+fi
+
+#pylupdate5 opensnitch_i18n.pro
+
+for lang in $(ls $langs_dir)
+do
+ lang_path="$langs_dir/$lang/$app_name-$lang"
+ $lrelease $lang_path.ts -qm $lang_path.qm
+done
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="de_DE" sourcelanguage="">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>OpenSnitch Firewall</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>User ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-weight:600;">Ausgeführt von</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>Quell-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>Prozess ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>Ziel-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>Zielport</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="150"/>
+ <source>Chromium Web Browser</source>
+ <translation type="obsolete">Chromium-Webbrowser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="226"/>
+ <source>(/path/to/bin/chromium)</source>
+ <translation type="obsolete">(Pfad/zur/bin/chromium)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="271"/>
+ <source>Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</source>
+ <translation type="obsolete">Der Chromium-Webbrowser möchte eine Verbindung zu www.evilsocket.net über TCP-Port 443 herstellen. Und möglicherweise zu www.goodsocket.net über Port 344</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>von dieser ausführbaren Datei</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>von dieser Kommandozeile</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>dieser Zielport</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>dieser Benutzer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>diese Ziel-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation>einmal</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="706"/>
+ <source>for this session</source>
+ <translation type="obsolete">für diese Sitzung</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation>für immer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation>Verweigern</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation>Erlauben</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation>bis zum Neustart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>New node connected</name>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation>Popup-Fenster</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="54"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Dieses Zeitlimit ist der Countdown, der angezeigt wird, wenn ein Popup-Fenster angezeigt wird.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation>Standardzeitlimit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation>Popup-Standarddauer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="754"/>
+ <source>Default duration</source>
+ <translation>Standarddauer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">Popup-Standardaktion</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">Standardaktion</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation>Standardfilterung</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation>mittig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation>oben rechts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation>unten rechts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation>oben links</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation>unten links</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="167"/>
+ <source>Prompt dialog default position on screen</source>
+ <translation type="obsolete">Grundposition</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation>nach ausführbarer Datei</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation>nach Befehl</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation>nach Zielport</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation>nach Ziel-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation>nach UID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="863"/>
+ <source>once</source>
+ <translation>einmal</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>for this session</source>
+ <translation type="obsolete">für diese Sitzung</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation>für immer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="905"/>
+ <source>deny</source>
+ <translation>verweigern</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="914"/>
+ <source>allow</source>
+ <translation>erlauben</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">Popups deaktivieren, nur eine Warnung anzeigen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="711"/>
+ <source>Nodes</source>
+ <translation>Knoten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="717"/>
+ <source>Process monitor method</source>
+ <translation>Prozessüberwachungsmethode</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="751"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Die Standarddauer gilt, wenn keine Benutzeroberfläche verbunden ist.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>Knotenadresse.</p><p>Standardmäßig: unix: ///tmp/osui.sock (unix: // ist erforderlich, wenn ein Unix-Socket vorhanden ist)</p><p>Es kann sich auch um eine IP mit diesem Format handeln: 127.0.0.1:50051, 192.168.1.122:12345 usw.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="884"/>
+ <source>Address</source>
+ <translation>Adresse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1024"/>
+ <source>Default log level</source>
+ <translation>Standardprotokollstufe</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>Version</source>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Die Standardaktion wird angewendet, wenn keine Benutzeroberfläche verbunden ist.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>Protokolldatei, in welche die Protokolle geschrieben werden sollen.<br/></p><p>/dev/stdout schreibt die Protokolle in die Standardausgabe des Dienstes..</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="737"/>
+ <source>Log file</source>
+ <translation>Logdatei</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete">Wenn Sie diese Option aktivieren, werden Sie von OpenSnitch aufgefordert, Verbindungen zu akzeptieren oder zu verweigern, denen aus verschiedenen Gründen keine PID zugeordnet ist.
+
+Das Popup-Fenster enthält nur Informationen zur Verbindung.
+
+Hinweis: Diese Verbindungen müssen nicht darauf hinweisen, dass etwas Verdächtiges passiert. Einfach
+ist, dass wir die PID nicht entdeckt haben (zum Beispiel Verbindungen, die nicht vom Computer stammen, oder fehlerhafte Pakete).</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">Unbekannte Verbindungen abfangen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="809"/>
+ <source>HostName</source>
+ <translation>HostName</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="983"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="868"/>
+ <source>until restart</source>
+ <translation>bis zum Neustart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="873"/>
+ <source>always</source>
+ <translation>immer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="995"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1000"/>
+ <source>/dev/stdout</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="767"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>Konfiguration auf alle Knoten anwenden</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1039"/>
+ <source>Database</source>
+ <translation>Datenbank</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="630"/>
+ <source>Database name</source>
+ <translation type="obsolete">Datenbankname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1074"/>
+ <source>In memory</source>
+ <translation>Im Speicher</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1079"/>
+ <source>File</source>
+ <translation>Datei</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="669"/>
+ <source>/path/to/the/file.db</source>
+ <translation type="obsolete">/Pfad/zu/der/Datei.db</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1345"/>
+ <source>Close</source>
+ <translation>Schließen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1356"/>
+ <source>Apply</source>
+ <translation>Anwenden</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1367"/>
+ <source>Save</source>
+ <translation>Speichern</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation>Bis zum Neustart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation>In der erweiterten Ansicht können Sie ganz einfach mehrere Felder auswählen, um Verbindungen zu filtern</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation>Standardmäßig erweiterte Ansicht anzeigen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="665"/>
+ <source>Action</source>
+ <translation>Aktion</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation><html><head/><body><p>Wenn diese Option aktiviert ist, werden die Pop-ups mit aktiver erweiterter Ansicht angezeigt.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation>Dauer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation><html><head/><body><p>Wenn ein neues Popup-Fenster in seiner einfachsten Form angezeigt wird, können Sie standardmäßig Verbindungen oder Anwendungen nach einer Eigenschaft der Verbindung (ausführbare Datei, Port, IP usw.) filtern.</p><p>Mit diesen Optionen können Sie mehrere Felder auswählen, nach denen Verbindungen gefiltert werden sollen.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation>Verbindungen auch filtern nach:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation>Wenn aktiviert, wird dieses Feld ausgewählt, wenn ein Popup angezeigt wird</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation>User ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation>Ziel Port</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation>Ziel-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation><html><head/><body><p>Dieses Timeout ist der Countdown, den Sie sehen, wenn ein Popup-Dialogfeld angezeigt wird.</p><p>Wenn das Popup nicht beantwortet wird, werden die Standardoptionen angewendet.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1093"/>
+ <source>Database type</source>
+ <translation>Datenbanktyp</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1100"/>
+ <source>Select</source>
+ <translation>Auswählen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation><html><head/><body><p>Popup-Standardaktion.</p><p>Wenn eine neue ausgehende Verbindung hergestellt werden soll, wird diese Aktion standardmäßig ausgewählt. Wenn das Timeout auftritt, wird diese Option angewendet.</p><p><br/></p><p>Während ein Pop-up den Benutzer auffordert, eine Verbindung zuzulassen oder abzulehnen:</p><p>1. neue ausgehende Verbindungen werden verweigert.</p><p>2. bekannte Verbindungen werden nach den vom Benutzer definierten Regeln zugelassen oder verweigert.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>Standardaktion, wenn die GUI getrennt ist</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="894"/>
+ <source>Debug invalid connections</source>
+ <translation>Debugge ungültige Verbindungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation>Pop-ups</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation>Standardoptionen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation>Standardposition auf dem Bildschirm</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="479"/>
+ <source>any temporary rules</source>
+ <translation>jede temporäre Regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="492"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation><html><head/><body><p>Wenn diese Option ausgewählt ist, werden die Regeln der ausgewählten Dauer nicht zur Liste der temporären Regeln in der GUI hinzugefügt.</p><p><br/></p><p>Temporäre Regeln sind weiterhin gültig und Sie können sie verwenden, wenn Sie dazu aufgefordert werden, eine neue Verbindung zuzulassen/zu verweigern.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="495"/>
+ <source>Don't save rules of duration</source>
+ <translation>Speichern Sie keine Regeln der Dauer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="601"/>
+ <source>Time</source>
+ <translation>Zeit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>Destination</source>
+ <translation>Ziel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="649"/>
+ <source>Protocol</source>
+ <translation>Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="697"/>
+ <source>Process</source>
+ <translation>Prozess</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="617"/>
+ <source>Rule</source>
+ <translation>Regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="633"/>
+ <source>Node</source>
+ <translation>Knoten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="723"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Wenn diese Option aktiviert ist, fordert Opensnitch Sie aus verschiedenen Gründen auf, Verbindungen zuzulassen oder zu verweigern, die keine zugeordnete PID haben, hauptsächlich aufgrund von Verbindungen mit schlechtem Status.</p><p>Der Popup-Dialog enthält nur Informationen über die Netzwerkverbindung.</p><p>Es gibt jedoch einige Szenarien, in denen dies gültige Verbindungen sind, z. B. beim Einrichten eines VPN mit Wireguard.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="589"/>
+ <source>Events tab columns</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="508"/>
+ <source>Desktop notifications</source>
+ <translation>Desktop-Benachrichtigungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="526"/>
+ <source>Use system notifications</source>
+ <translation>Systembenachrichtigungen verwenden</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="542"/>
+ <source>Use Qt notifications</source>
+ <translation>Qt-Benachrichtigungen verwenden</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="571"/>
+ <source>Test</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1178"/>
+ <source>minutes</source>
+ <translation>Minuten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1204"/>
+ <source>Minutes between events purges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1227"/>
+ <source>days</source>
+ <translation>Tage</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1237"/>
+ <source>Maximum days of events to keep</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation>Prozessdetails</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation>wird geladen...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation>CWD: Laden...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation>Speicherstatistik: Laden ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation>Status</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation>Dateien öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation>I/O Statistiken</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation>Dateien in den Speicher geladen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation>Stapel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation>Umgebungsvariablen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation>Anwendungs-PIDs</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation>Starten oder beenden Sie die Überwachung dieses Prozesses</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation>Schließen</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="14"/>
+ <source>Rule</source>
+ <translation>Regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/>
+ <source>Node</source>
+ <translation>Knoten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/>
+ <source>Apply rule to all nodes</source>
+ <translation>Regel auf alle Knoten anwenden</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/>
+ <source>From this command line</source>
+ <translation>Von dieser Kommandozeile</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/>
+ <source>From this executable</source>
+ <translation>Von dieser ausführbaren Datei</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/>
+ <source>Action</source>
+ <translation>Aktion</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/>
+ <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source>
+ <translation type="obsolete">/Pfad/zur/ausführbaren/Datei, .*/bin/executable[0-9\.]+$, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/>
+ <source>To this IP / Network</source>
+ <translation>Zu dieser IP / Netzwerk</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/>
+ <source>once</source>
+ <translation>einmal</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source>until restart</source>
+ <translation type="obsolete">bis zum Neustart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/>
+ <source>always</source>
+ <translation>immer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/>
+ <source>To this port</source>
+ <translation>Zu diesem Port</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/>
+ <source>From this user ID</source>
+ <translation>Von dieser Benutzer-ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation>Kommas oder Leerzeichen dürfen nicht mehrere Domänen angeben.
+
+Verwenden Sie stattdessen reguläre Ausdrücke:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+oder eine einzelne Domain:
+www.gnu.org - es wird nur mit www.gnu.org, noch ftp.gnu.org oder www2.gnu.org übereinstimmen, ...
+gnu.org - es wird nur mit gnu.org, www.gnu.org oder ftp.gnu.org übereinstimmen, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="430"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation>www.domain.org, .*\.domain.org</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation><html><head/><body><p>Es sind nur TCP-, UDP- oder UDPLITE-Optionen zulässig.</p><p>Sie können reguläre Ausdrücke verwenden zu diesen Optionen, zum Beispiel TCP oder UDP: ^ (TCP|UDP)$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/>
+ <source>TCP</source>
+ <translation>TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/>
+ <source>UDP</source>
+ <translation>UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/>
+ <source>UDPLITE</source>
+ <translation>UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>TCP6</source>
+ <translation>TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/>
+ <source>UDP6</source>
+ <translation>UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/>
+ <source>UDPLITE6</source>
+ <translation>UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation>Sie können eine IP angeben:
+- 192.168.1.1
+
+oder ein regulärer Ausdruck:
+- 192\.168\.1\.[0-9]+
+
+mehrere IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+Sie können auch ein Subnetz angeben:
+- 192.168.1.0/24
+
+Hinweis: Kommas und Leerzeichen dürfen keine IPs oder Netzwerke angeben.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/>
+ <source>LAN</source>
+ <translation>LAN</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/>
+ <source>127.0.0.0/8</source>
+ <translation>127.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/>
+ <source>192.168.0.0/24</source>
+ <translation>192.168.0.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/>
+ <source>192.168.1.0/24</source>
+ <translation>192.168.1.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/>
+ <source>192.168.2.0/24</source>
+ <translation>192.168.2.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/>
+ <source>192.168.0.0/16</source>
+ <translation>192.168.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/>
+ <source>169.254.0.0/16</source>
+ <translation>169.254.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/>
+ <source>172.16.0.0/12</source>
+ <translation>172.16.0.0/12</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/>
+ <source>10.0.0.0/8</source>
+ <translation>10.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/>
+ <source>::1/128</source>
+ <translation>::1/128</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/>
+ <source>fc00::/7</source>
+ <translation>fc00::/7</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/>
+ <source>ff00::/8</source>
+ <translation>ff00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/>
+ <source>fe80::/10</source>
+ <translation>fe80::/10</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/>
+ <source>fd00::/8</source>
+ <translation>fd00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/>
+ <source>Duration</source>
+ <translation>Dauer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/>
+ <source>Protocol</source>
+ <translation>Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/>
+ <source>To this host</source>
+ <translation>Zu diesem Host</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/>
+ <source>Deny</source>
+ <translation>Verweigern</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/>
+ <source>Allow</source>
+ <translation>Erlauben</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Name</source>
+ <translation>Name</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/>
+ <source>Enable</source>
+ <translation>Aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation>Regeln werden in alphabetischer Reihenfolge überprüft, daher können Sie diese so benennen, um sie zu priorisieren.
+
+000-allow-localhost
+0001-Deny-Broadcast
+...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="713"/>
+ <source>leave blank to autocreate</source>
+ <translation>Lassen Sie das Feld leer, um den Namen automatisch zuzuweisen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation>Wenn Sie diese Option aktivieren, hat diese Regel bei der Bewertung Vorrang vor den übrigen Regeln. Danach werden keine Regeln mehr überprüft.
+
+Sie müssen die Regel so benennen, dass sie zuerst überprüft wird, da sie in alphabetischer Reihenfolge überprüft wird. Zum Beispiel:
+
+[x] Priorität - 000-Prioritätsregel
+[] Priorität - 001-Regel mit weniger Priorität</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="54"/>
+ <source>Priority rule</source>
+ <translation>Prioritätsregel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation><html><head/><body><p>Standardmäßig wird bei den Feldern einer Regel NICHT zwischen Groß- und Kleinschreibung unterschieden, d. H.; Wenn ein Prozess versucht, auf gOOgle.CoM zuzugreifen, und Sie eine Regel zum Verweigern haben. * Google.com, wird die Verbindung blockiert.<br/></p><p>Wenn Sie diese Option aktivieren und gOOgle.CoM GENAU blockieren möchten, müssen Sie dies im Regelfeld angeben, also die genaue Domain, die Sie filtern möchten (in diesem Fall: gOOgle.CoM).</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="77"/>
+ <source>Case-sensitive</source>
+ <translation>Groß- und Kleinschreibung beachten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation><html><head/><body><p>Sie können mehrere Ports mit regulären Ausdrücken angeben:</p><p><br/></p><p>- 53, 80 oder 443:
+</p><p>^ (53|80|443)$</p><p><br/></p><p>- 53, 443 oder 5551, 5552, 5553 usw.:</p><p>^ (53|443|555[0-9])$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/>
+ <source>until reboot</source>
+ <translation>Bis zum Neustart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/>
+ <source>To this list of domains</source>
+ <translation>Zu dieser Domainliste</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Wählen Sie ein Verzeichnis mit Domänenlisten aus, die blockiert oder zugelassen werden sollen.</p><p>Legen Sie in diesem Verzeichnis Dateien mit einer beliebigen Erweiterung ab, die Listen von Domänen enthalten.</p><p><br/>Das Format jedes Eintrags einer Liste ist wie folgt (Hosts-Format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/>
+ <source>Applications</source>
+ <translation>Anwendungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/>
+ <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="246"/>
+ <source>From this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/>
+ <source>Network</source>
+ <translation>Netzwerk</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/>
+ <source>List of domains/IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>To this list of network ranges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/>
+ <source>To this list of IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="608"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="636"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="651"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>OpenSnitch-Netzwerkstatistik</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="290"/>
+ <source>Save to CSV.</source>
+ <translation>Als CSV exportieren.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="300"/>
+ <source>Ctrl+S</source>
+ <translation>Strg+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="351"/>
+ <source>Create a new rule</source>
+ <translation>Erstellen Sie eine neue Regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="381"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="420"/>
+ <source>Status</source>
+ <translation>Status</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1665"/>
+ <source>-</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="464"/>
+ <source>Start or Stop interception</source>
+ <translation>Abfangen starten oder stoppen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="509"/>
+ <source>Events</source>
+ <translation>Ereignisse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation>Filter</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation>Erlauben</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation>Verweigern</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation>Beispiel: firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="748"/>
+ <source>Nodes</source>
+ <translation>Knoten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(Doppelklicken Sie auf die Addressenspalte, um Details eines Knotens anzuzeigen)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1569"/>
+ <source>Rules</source>
+ <translation>Regeln</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="857"/>
+ <source>enable</source>
+ <translation>aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="671"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete">(Doppelklicken Sie auf die Namenspalte, um Details einer Regel anzuzeigen.)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation type="obsolete">Suchregelname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="704"/>
+ <source>Application rules</source>
+ <translation>Anwendungsregeln</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="806"/>
+ <source>Permanent</source>
+ <translation>Dauerhaft</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="815"/>
+ <source>Temporary</source>
+ <translation>Temporär</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="933"/>
+ <source>Hosts</source>
+ <translation>Hosts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(Doppelklicken Sie auf eine Element, um Details anzuzeigen.)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1020"/>
+ <source>Applications</source>
+ <translation>Anwendungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1127"/>
+ <source>Addresses</source>
+ <translation>Adressen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1214"/>
+ <source>Ports</source>
+ <translation>Ports</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1298"/>
+ <source>Users</source>
+ <translation>Benutzer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1404"/>
+ <source>Connections</source>
+ <translation>Verbindungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1459"/>
+ <source>Dropped</source>
+ <translation>Abgelehnt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1514"/>
+ <source>Uptime</source>
+ <translation>Betriebszeit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1639"/>
+ <source>Version</source>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation>Löschen Sie alle abgefangenen Ereignisse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="864"/>
+ <source>Edit rule</source>
+ <translation>Regel bearbeiten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="878"/>
+ <source>Delete rule</source>
+ <translation>Regel löschen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation type="obsolete">Löschen Sie alle abgefangenen Hosts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation type="obsolete">Löschen Sie alle abgefangenen Anwendungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation type="obsolete">Löschen Sie alle abgefangenen Adressen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation type="obsolete">Löschen Sie alle abgefangenen Ports</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation type="obsolete">Löschen Sie alle abgefangenen Benutzer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(Doppelklicken Sie auf eine Zeile, um Details zu einer Regel anzuzeigen)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="912"/>
+ <source>Delete connections that matched this rule</source>
+ <translation>Verbindungen löschen, die dieser Regel entsprechen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="797"/>
+ <source>All applications</source>
+ <translation>Alle Anwendungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation>Statistiken</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation>Hilfe</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation>Schließen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation>Aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation>Deaktivieren</translation>
+ </message>
+</context>
+<context>
+ <name>menu_close</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="131"/>
+ <source>Close</source>
+ <translation type="obsolete">Cerrar</translation>
+ </message>
+</context>
+<context>
+ <name>menu_help</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="126"/>
+ <source>Help</source>
+ <translation type="obsolete">Ayuda</translation>
+ </message>
+</context>
+<context>
+ <name>menu_statistics</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="120"/>
+ <source>Statistics</source>
+ <translation type="obsolete">Eventos</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation>Erlauben</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation>Verweigern</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation>für immer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation>Ausgehende Verbindung</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation>Prozess ausgeführt von:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation>von dieser Kommandozeile</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation>von dieser ausführbaren Datei</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/>
+ <source>Unknown process</source>
+ <translation type="obsolete">Unbekannter Prozess</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation>Bis zum Neustart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation>zum Port {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/>
+ <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete"><b>%s</b> stellt eine Verbindung zu <b>%s</b> an Port%s %d her</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete"><b>Remote-Prozess <b>%s</b>, der auf <b>%s</b> ausgeführt wird, stellt eine Verbindung zu <b>%s</b> auf %s Port %d her</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation>zu {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation>UID {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation>zu {0}.*</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation>zu *.{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation type="obsolete">zu *{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation><b>Remote-Prozess </b> %s wird ausgeführt auf <b>%s</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation>stellt eine Verbindung zu <b>%s</b> auf %s Port %d her</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation>versucht <b>%s</b> über%s,%s Port%d aufzulösen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="105"/>
+ <source>New outgoing connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/>
+ <source>Exception saving config: %s</source>
+ <translation type="obsolete">Fehler beim Speichern der Konfiguration: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/>
+ <source>Applying configuration on %s ...</source>
+ <translation type="obsolete">Konfiguration in %s anwenden ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/>
+ <source>Server address can not be empty</source>
+ <translation>Die Serveradresse darf nicht leer sein</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/>
+ <source>Error loading %s configuration</source>
+ <translation type="obsolete">Fehler beim Laden der Konfiguration %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/>
+ <source>Configuration applied.</source>
+ <translation>Konfiguration angewendet.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/>
+ <source>Error applying configuration: %s</source>
+ <translation type="obsolete">Fehler beim Anwenden der Konfiguration: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/>
+ <source>Exception saving config: {0}</source>
+ <translation>Fehler beim Speichern der Konfiguration: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation>Konfiguration in {0} anwenden ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/>
+ <source>Error loading {0} configuration</source>
+ <translation>Fehler beim Laden der Konfiguration {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/>
+ <source>Error applying configuration: {0}</source>
+ <translation>Fehler beim Anwenden der Konfiguration: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>Warning</source>
+ <translation>Warnung</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation>Sie müssen eine Datei für die Datenbank auswählen<br>oder wählen Sie den Typ "Im Speicher".</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>DB type changed</source>
+ <translation>DB-Typ geändert</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation>Starten Sie die GUI neu, damit die Effekte wirksam werden</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation>Fahren Sie mit der Maus über die Texte, um die Hilfe anzuzeigen<br><br>Vergessen Sie nicht, das Wiki zu besuchen: <a href="{0}">{0}</a></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation><b>Fehler beim Laden der Prozessinformationen:</b> <br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation><b>Fehler beim Beenden des Überwachungsprozesses:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation>Wird geladen...</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/>
+ <source>There're no nodes connected.</source>
+ <translation>Es sind keine Knoten verbunden.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/>
+ <source>Rule applied.</source>
+ <translation>Regel angewendet.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/>
+ <source>Error applying rule: %s</source>
+ <translation type="obsolete">Fehler beim Anwenden der Regel:%s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation>Das Protokoll darf nicht leer sein oder die Option deaktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/>
+ <source>Protocol regexp error</source>
+ <translation>Protokoll-Regexp-Fehler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/>
+ <source>process path can not be empty</source>
+ <translation>Prozesspfad darf nicht leer sein</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/>
+ <source>Process path regexp error</source>
+ <translation>Prozesspfad-Regexp-Fehler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/>
+ <source>command line can not be empty</source>
+ <translation>Befehlszeile darf nicht leer sein oder die Option deaktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/>
+ <source>Command line regexp error</source>
+ <translation>Befehlszeilen-Regexp-Fehler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/>
+ <source>Dest port can not be empty</source>
+ <translation>Der Zielport darf nicht leer sein oder die Option deaktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/>
+ <source>Dst port regexp error</source>
+ <translation>Fehler im regulären Ausdruck des Zielports</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/>
+ <source>Dest host can not be empty</source>
+ <translation>Der Zielhost kann nicht leer sein oder die Option deaktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/>
+ <source>Dst host regexp error</source>
+ <translation>Fehler beim regulären Ausdruck des Zielhosts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>Ziel-IP / Netzwerk darf nicht leer sein</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/>
+ <source>Dst IP regexp error</source>
+ <translation>Fehler beim regulären Ausdruck der Ziel-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/>
+ <source>User ID can not be empty</source>
+ <translation>Die Benutzer-ID darf nicht leer sein oder die Option deaktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/>
+ <source>User ID regexp error</source>
+ <translation>Regexp-Fehler der Benutzer-ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Fehler beim Anwenden der Regel: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Fehler beim Laden der Regel</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/>
+ <source>Lists field cannot be empty</source>
+ <translation>Listenfeld darf nicht leer sein</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/>
+ <source>Lists field must be a directory</source>
+ <translation>Listenfeld muss ein Verzeichnis sein</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>Regel nicht unterstützt</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/>
+ <source>There's already a rule with this name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/>
+ <source>PID field can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/>
+ <source>PID field regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/>
+ <source>Select at least one field.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation>Gestoppt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation>Deaktiviert</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation>Eingeschaltet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="412"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="414"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="761"/>
+ <source> Your are about to delete this rule. </source>
+ <translation> Sie sind im Begriff, diese Regel zu löschen. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> Are you sure?</source>
+ <translation> Bist du sicher?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="568"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation>OpenSnitch-Netzwerkstatistiken {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="570"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation>OpenSnitch-Netzwerkstatistiken für {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">Name</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">Adresse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="176"/>
+ <source>Status</source>
+ <translation type="obsolete">Status</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="177"/>
+ <source>Hostname</source>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="183"/>
+ <source>Version</source>
+ <translation type="obsolete">Version</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="180"/>
+ <source>Rules</source>
+ <translation type="obsolete">Regeln</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">Zeit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">Aktion</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">Dauer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <translation type="obsolete">Knoten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="253"/>
+ <source>Hits</source>
+ <translation type="unfinished">Treffer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/>
+ <source>Save as CSV</source>
+ <translation>Als CSV speichern</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">Aktiviert</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="738"/>
+ <source>Delete</source>
+ <translation>Löschen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source>always</source>
+ <translation type="obsolete">siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="575"/>
+ <source><b>Error:</b><br><br>{0}</source>
+ <translation type="obsolete"><b>Error:</b><br><br>{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="921"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation><b>Fehler:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="928"/>
+ <source>Warning:</source>
+ <translation>Warnung:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="717"/>
+ <source>Allow</source>
+ <translation>Erlauben</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="718"/>
+ <source>Deny</source>
+ <translation>Verweigern</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="722"/>
+ <source>Always</source>
+ <translation>Immer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="723"/>
+ <source>Until reboot</source>
+ <translation>Bis zum Neustart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="731"/>
+ <source>Disable</source>
+ <translation>Deaktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="733"/>
+ <source>Enable</source>
+ <translation>Aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Duplicate</source>
+ <translation>Duplizieren</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="737"/>
+ <source>Edit</source>
+ <translation>Bearbeiten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="891"/>
+ <source>Rule not found by that name and node</source>
+ <translation>Regel von diesem Namen und Knoten nicht gefunden</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> You are about to delete this rule. </source>
+ <translation> Sie sind dabei, diese Regel zu löschen. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <translation type="obsolete">Regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">Name</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Name</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Adresse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Status</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Version</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regeln</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Zeit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Aktion</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Dauer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Knoten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Aktiviert</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Treffer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="392"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Name</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Adresse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="379"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Status</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="380"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Regeln</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="390"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Zeit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="395"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Aktion</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="396"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Dauer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="391"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Knoten</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="393"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Aktiviert</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Treffer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Prozess</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Ziel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>BenutzerID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="377"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>LetzteVerbindung</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>ZielIP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>ZielHost</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>ZielPort</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="174"/>
+ <source>LastConnection</source>
+ <translation type="obsolete">LetzteVerbindung</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="179"/>
+ <source>Uptime</source>
+ <translation type="obsolete">Betriebszeit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="181"/>
+ <source>Connections</source>
+ <translation type="obsolete">Verbindungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="182"/>
+ <source>Dropped</source>
+ <translation type="obsolete">Abgelehnt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="252"/>
+ <source>What</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="709"/>
+ <source>Apply to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="719"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="378"/>
+ <source>Addr</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="382"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">Betriebszeit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">Verbindungen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">Abgelehnt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="394"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="644"/>
+ <source>New node connected</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="774"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="obsolete"> Estás a punto de borrar esta regla. </translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="776"/>
+ <source> Are you sure?</source>
+ <translation type="obsolete"> ¿Estás seguro?</translation>
+ </message>
+</context>
+<context>
+ <name>stats_disabled</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="74"/>
+ <source>Disabled</source>
+ <translation type="obsolete">Deshabilitado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_notrunning</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="73"/>
+ <source>Not running</source>
+ <translation type="obsolete">Parado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_running</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="75"/>
+ <source>Running</source>
+ <translation type="obsolete">Interceptando</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="409"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de red OpenSnitch</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="411"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="es_ES">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>opensnitch-qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>UID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation>Ejecutado desde</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation>Etiqueta de texto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>IP origen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>IP destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>Puerto destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>de este ejecutable</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>de este comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>este puerto destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>este usuario</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>esta IP destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation>una vez</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation>30 segundos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation>5 minutos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation>15 minutos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation>30 minutos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation>1 hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="706"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation>para siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation>Denegar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation>Hasta reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation>a partir de este PID</translation>
+ </message>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Preferencias</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation>UI</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="54"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source>
+ <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation>Timeout por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation>Duración por defecto (de la acción/regla)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="754"/>
+ <source>Default duration</source>
+ <translation>Duración por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">Acción por defecto de la ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">Acción por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation>Filtrado por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation>centro</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation>Arriba a la derecha</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation>Abajo a la derecha</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation>Arriba a la izquierda</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation>Abajo a la izquierda</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="167"/>
+ <source>Prompt dialog default position on screen</source>
+ <translation type="obsolete">Posición por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation>por ejecutable</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation>por comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation>por puerto destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation>por IP destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation>por UID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="863"/>
+ <source>once</source>
+ <translation>una vez</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation>30 segundos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation>5 minutos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation>15 minutos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation>30 minutos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation>1 hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation>para siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="905"/>
+ <source>deny</source>
+ <translation>Denegar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="914"/>
+ <source>allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">Deshabilitar ventanas emergentes,
+sólo mostrar alerta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="711"/>
+ <source>Nodes</source>
+ <translation>Nodos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="717"/>
+ <source>Process monitor method</source>
+ <translation>Método parar monitorizar procesos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="751"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation>La Duración por defecto se aplicará cuando no haya ninguna UI conectada</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>Dirección del nodo.</p><p>Por defecto: unix:///tmp/osui.sock (unix:// es obligatorio si es un socket Unix)</p><p>También puede ser una IP con este formato: 127.0.0.1:50051, 192.168.1.122:12345, etc..</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="884"/>
+ <source>Address</source>
+ <translation>Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1024"/>
+ <source>Default log level</source>
+ <translation>Nivel de log por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>Version</source>
+ <translation>Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation>La Acción por defecto se aplicará cuando no haya ninguna UI conectada</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>Fichero en el que escribir los logs.<br/></p><p>/dev/stdout escribirá los logs por la salida estándar del servicio.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="737"/>
+ <source>Log file</source>
+ <translation>Fichero de log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones.
+
+La ventana emergente sólo contendrá información relativa a la conexión.
+
+Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente
+es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">Interceptar conexiones desconocidas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="809"/>
+ <source>HostName</source>
+ <translation>Nombre del host</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="983"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation>unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="868"/>
+ <source>until restart</source>
+ <translation>hasta reiniciar (el servicio)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="873"/>
+ <source>always</source>
+ <translation>siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="995"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation>/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1000"/>
+ <source>/dev/stdout</source>
+ <translation>/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="767"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>Aplicar configuración a todos
+los nodos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1039"/>
+ <source>Database</source>
+ <translation>Datos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1074"/>
+ <source>In memory</source>
+ <translation>En memoria</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1079"/>
+ <source>File</source>
+ <translation>Fichero</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1345"/>
+ <source>Close</source>
+ <translation>Cerrar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1356"/>
+ <source>Apply</source>
+ <translation>Aplicar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1367"/>
+ <source>Save</source>
+ <translation>Guardar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation>Hasta reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1093"/>
+ <source>Database type</source>
+ <translation>Tipo de base de datos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1100"/>
+ <source>Select</source>
+ <translation>Seleccionar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Pop-ups default options</source>
+ <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="367"/>
+ <source>Pop-ups default position on screen</source>
+ <translation type="obsolete">Posición en pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="102"/>
+ <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source>
+ <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation>Mostrar vista avanzada por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="665"/>
+ <source>Action</source>
+ <translation>Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation>Si se selecciona, las ventanas emergentes se mostrarán con la vista avanzada activada</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation>Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation>Por defecto cuando una ventana emergente aparece, en su forma más simple, puedes filtrar conexiones por un
+parámetro de la conexión (ejecutable, puerto, IP, etc).
+
+Con estas opciones, puedes seleccionar varios campos por los que filtrar por defecto conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation>Filtrar conexiones también por:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="362"/>
+ <source>If checked, this field will be checked when a pop-up is displayed</source>
+ <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation>UID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation>Puerto destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation>IP destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation>Este timeout es la cuenta atrás que ves cuando se muestra una ventana emergente
+
+Si no respondes a la ventana emergente, se aplicarán las opciones por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation>La vista avanzada te permite seleccionar fácilmente múltiples campos para filtrar conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation>Si se selecciona, este campo estará marcado cuando una ventana emergente aparezca</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation><html><head/><body><p>Acción por defecto de la ventana emergente.</p><p>Cuando una nueva conexión saliente está a punto de establecerse, esta acción será la predeterminada, por lo que si llega el timeout, está será la que se aplique.</p><p><br/></p><p>Mientras una ventana emergente está activa esperando ser aprobada o denegada:</p><p>1. Las nuevas conexiones salientes son denegadas (según la configuración del demonio)</p><p>2. Las conexiones ya conocidas se permitirán o denegarán en base a las reglas ya creadas por el usuario.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>Opción por defecto cuando la GUI no está conectada</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="894"/>
+ <source>Debug invalid connections</source>
+ <translation>Depurar conexiones inválidas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation>Ventanas emergentes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation>Opciones por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation>Posición por defecto en la pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="479"/>
+ <source>any temporary rules</source>
+ <translation>cualquier regla temporal</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="492"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation><html><head/><body><p>Cuando esta opción está seleccionada, las reglas de la duración elegida no se añadirán a la lista de reglas temporales en la GUI.</p><p><br/></p><p>Las reglas temporales seguirán siendo válidas, y puedes usarlas cuando se pregunte para permitir o denegar una nueva conexión.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="495"/>
+ <source>Don't save rules of duration</source>
+ <translation>No guardar reglas de duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="463"/>
+ <source>Show events columns</source>
+ <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="601"/>
+ <source>Time</source>
+ <translation>Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>Destination</source>
+ <translation>Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="649"/>
+ <source>Protocol</source>
+ <translation>Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="697"/>
+ <source>Process</source>
+ <translation>Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="617"/>
+ <source>Rule</source>
+ <translation>Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="633"/>
+ <source>Node</source>
+ <translation>Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="723"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Si se selecciona opensnitch te preguntará para permitir o denegar conexiones que no tienen un PID asociado. Esto puede pasar por diferentes motivos, principalmente debido a conexiones inválidas.</p><p>La ventana emergente sólo contendrá información de la conexión.</p><p>Hay algunas situaciones en las que estas conexiones son válidas, por ejemplo al establecer un túnel VPN con wireguard.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="589"/>
+ <source>Events tab columns</source>
+ <translation>Columnas de la pestaña Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation>por PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation>Deshabilitar ventanas emergentes, sólo mostrar notificaciones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="508"/>
+ <source>Desktop notifications</source>
+ <translation>Notificaciones de escritorio</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="526"/>
+ <source>Use system notifications</source>
+ <translation>Usar notificaciones del sistema</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="542"/>
+ <source>Use Qt notifications</source>
+ <translation>Usar notificaciones de Qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="571"/>
+ <source>Test</source>
+ <translation>Probar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation><html><head/><body><p>Si lo marcas, OpenSnitch sólo te preguntará para denegar o permitir conexiones que por diversas razones no tengan un PID/aplicación asociado. Generalmente son conexiones en estado erróneo.</p><p>La ventana emergente sólo contendrá información sobre la conexión de red.</p><p>Hay algunos casos en los que estas conexiones pueden ser válidas, como cuando se establecen conexiones VPN.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1178"/>
+ <source>minutes</source>
+ <translation>minutos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1204"/>
+ <source>Minutes between events purges</source>
+ <translation>Minutos entre borrado de eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1227"/>
+ <source>days</source>
+ <translation>días</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1237"/>
+ <source>Maximum days of events to keep</source>
+ <translation>Máximo de días a guardar</translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation>Detalles del proceso</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation>cargando...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation>CWD: cargando...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation>estadísticas de memoria: cargando...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation>Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation>Ficheros abiertos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation>Estadísticas Entrada/Salida</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation>Ficheros cargados en memoria</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation>Pila</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation>Variables de entorno</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation>PIDs de la aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation>Iniciar o Parar el monitorizado de este proceso</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation>Cerrar</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="14"/>
+ <source>Rule</source>
+ <translation>Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/>
+ <source>Node</source>
+ <translation>Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/>
+ <source>Apply rule to all nodes</source>
+ <translation>Aplicar regla a todos los nodos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/>
+ <source>From this command line</source>
+ <translation>De este comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/>
+ <source>From this executable</source>
+ <translation>De este ejecutable</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/>
+ <source>Action</source>
+ <translation>Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/>
+ <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source>
+ <translation type="obsolete">/ruta/al/ejecutable, .*/bin/executable[0-9\.]+$, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/>
+ <source>To this IP / Network</source>
+ <translation>A esta IP/Red</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/>
+ <source>once</source>
+ <translation>una vez</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source>until restart</source>
+ <translation type="obsolete">hasta reiniciar (el servicio)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/>
+ <source>always</source>
+ <translation>siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/>
+ <source>To this port</source>
+ <translation>A este puerto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/>
+ <source>From this user ID</source>
+ <translation>De este UID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation>No se permiten ni comas ni espacios para especificar múltiples dominios.
+
+Puedes usar expresiones regulares en su lugar:
+
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+o un único dominio:
+www.gnu.org - sólo filtrará www.gnu.org, NO filtrará ftp.gnu.org ni www2.gnu.org
+gnu.org - sólo filtrará gnu.org, ni www.gnu.org, ni ftp. gnu.org, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="430"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation>www.dominio.org, .*\.dominio.org</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation>Sólo se permiten las opciones TCP, UDP o UDPLITE. Puedes usar expresiones regulares
+sobre estas opciones, por ejemplo TCP o UDP: ^(TCP|UDP)$</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/>
+ <source>TCP</source>
+ <translation>TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/>
+ <source>UDP</source>
+ <translation>UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/>
+ <source>UDPLITE</source>
+ <translation>UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>TCP6</source>
+ <translation>TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/>
+ <source>UDP6</source>
+ <translation>UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/>
+ <source>UDPLITE6</source>
+ <translation>UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation>Puedes especificar una IP:
+- 192.168.1.1
+
+o una expresión regular:
+- 192\.168\.1\.[0-9]+
+
+múltiples IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+También puedes especificar una subnet:
+- 192.168.1.0/24
+
+Nota: No se permiten ni comas ni espacios para especificar IPs o redes.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/>
+ <source>LAN</source>
+ <translation>LAN</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/>
+ <source>127.0.0.0/8</source>
+ <translation>127.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/>
+ <source>192.168.0.0/24</source>
+ <translation>192.168.0.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/>
+ <source>192.168.1.0/24</source>
+ <translation>192.168.1.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/>
+ <source>192.168.2.0/24</source>
+ <translation>192.168.2.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/>
+ <source>192.168.0.0/16</source>
+ <translation>192.168.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/>
+ <source>169.254.0.0/16</source>
+ <translation>169.254.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/>
+ <source>172.16.0.0/12</source>
+ <translation>172.16.0.0/12</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/>
+ <source>10.0.0.0/8</source>
+ <translation>10.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/>
+ <source>::1/128</source>
+ <translation>::1/128</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/>
+ <source>fc00::/7</source>
+ <translation>fc00::/7</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/>
+ <source>ff00::/8</source>
+ <translation>ff00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/>
+ <source>fe80::/10</source>
+ <translation>fe80::/10</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/>
+ <source>fd00::/8</source>
+ <translation>fd00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/>
+ <source>Duration</source>
+ <translation>Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/>
+ <source>Protocol</source>
+ <translation>Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/>
+ <source>To this host</source>
+ <translation>A este host</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/>
+ <source>Deny</source>
+ <translation>Denegar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Name</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/>
+ <source>Enable</source>
+ <translation>Habilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation>Las reglas se comprueban en orden alfabético, por lo que debes nombrarlas así para priorizarlas.
+
+000-allow-localhost
+0001-deny-broadcast
+...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="713"/>
+ <source>leave blank to autocreate</source>
+ <translation>dejar en blanco para autoasignar nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation>Si marcas esta opción, esta regla tendrá prioridad sobre el resto de reglas cuando le toque evaluarla. No se comprobará ninguna regla más después de esta si coincide.
+
+Debes nombrar la regla de tal manera que se compruebe de las primeras, ya que se nombran alfabéticamente. Por ejemplo:
+
+[x] Prioritaria-000-regla-prioritaria
+[ ] Prioritaria-001-regla-menos-prioritaria</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="54"/>
+ <source>Priority rule</source>
+ <translation>Prioritaria</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation><html><head/><body><p>Por defecto los campos de una regla NO son sensibles a mayúsculas/minúsculas, es decir, si un proceso trata de acceder a gOOgle.CoM y tienes una regla para Denegar .*google.com, la conexión será bloqueada. <br/></p><p>Si marcas esta opción y quieres bloquear EXACTAMENTE gOOgle.CoM, tendrás que especificar en el campo de la regla el dominio exacto que quieres filtrar (en este caso: gOOgle.CoM).</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="77"/>
+ <source>Case-sensitive</source>
+ <translation>Distinguir mayúsculas/minúsculas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation>Puedes especificar múltiples puertos usando expresiones regulares:
+
+- 53, 80 o 443:
+^(53|80|443)$
+
+- 53, 443 o 5551, 5552, 5553, etc:
+^(53|443|555[0-9])$</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/>
+ <source>until reboot</source>
+ <translation>Hasta reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/>
+ <source>To this list of domains</source>
+ <translation>A esta lista de dominios</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Selecciona un directorio con listas de dominios para permitir o denegar.</p><p>Mete dentro de este directorio ficheros con cualquier extensión que contengan listas de dominios.</p><p><br/>El formato de cada dominio de la lista tiene que estar en formato hosts, así:</p><p>127.0.0.1 www.domain.com</p><p>o </p><p>0.0.0.0 www.domain.com</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/>
+ <source>Applications</source>
+ <translation>Aplicaciones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/>
+ <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source>
+ <translation><html><head/><body><p>Este campo sólo comprueba la ruta del ejecutable (la cual no es modificable por el usuario).<br/></p><p>Puedes usar expresiones regulares para denegar cualquier ejecución desde /tmp, por ejemplo; ^/tmp/.*$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation><html><head/><body><p>Este campo contendrá y comprobará solamente la linea de comandos tecleada por el usuario.<br/></p><p>Si el usuario sólo escribió el comando (sin la ruta absoluta), sólo aparecerá el comando:</p><p>telnet 1.2.3.4<br/></p><p>Si el usuario escribió la ruta absoluta o relativa al comando, eso es lo que aparecerá:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="246"/>
+ <source>From this PID</source>
+ <translation>De este PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/>
+ <source>Network</source>
+ <translation>Red</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/>
+ <source>List of domains/IPs</source>
+ <translation>A esta lista de dominios/IPs</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>To this list of network ranges</source>
+ <translation>A esta lista de rangos de red</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/>
+ <source>To this list of IPs</source>
+ <translation>A esta lista de IPs</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan listas de IPs a bloquear o permitir:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>Una IP por linea. Las lineas en blanco o que comiencen con # serán ignoradas.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="608"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan listas de rangos de red a bloquear o permitir:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>Un rango de red por linea. Las lineas en blanco o que comiencen con # serán ignoradas.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="636"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan listas de dominios a bloquear o permitir.</p><p>Los ficheros de ese directorio pueden tener cualquier extensión.</p><p><br/>El formato de cada linea es como sigue (formato hosts):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Las lineas en blanco o que comiencen con # serán ignoradas.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="651"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation>A esta lista de dominios
+(expresiones regulares)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Selecciona un directorio con ficheros que contengan expresiones regulares de dominios para bloquear o permitir:</p><p>.*\.example\.com</p><p>También puedes usar un dominio literal (sin expresión regular): &quot;example.com&quot; ,comprobará whatever.example.com, whatever.example.com.localdomain, etc.</p><p>Un dominio por linea. Las lineas en blanco o que comiencen con # serán ignoradas</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/>
+ <source>Reject</source>
+ <translation>Rechazar</translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>Eventos de red - OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="290"/>
+ <source>Save to CSV.</source>
+ <translation>Exportar a CSV.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="300"/>
+ <source>Ctrl+S</source>
+ <translation>Ctrl+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="351"/>
+ <source>Create a new rule</source>
+ <translation>Crear una nueva regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="381"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">Nombre del host - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="420"/>
+ <source>Status</source>
+ <translation>Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1665"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="464"/>
+ <source>Start or Stop interception</source>
+ <translation>Parar o iniciar la interceptación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="509"/>
+ <source>Events</source>
+ <translation>Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation>Filtrar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation>Denegar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation>Ejemplo: firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="748"/>
+ <source>Nodes</source>
+ <translation>Nodos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en la columna Dirección para ver los detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1569"/>
+ <source>Rules</source>
+ <translation>Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="857"/>
+ <source>enable</source>
+ <translation>habilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="684"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation type="obsolete">buscar regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="704"/>
+ <source>Application rules</source>
+ <translation>Reglas de aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="806"/>
+ <source>Permanent</source>
+ <translation>Permanentes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="815"/>
+ <source>Temporary</source>
+ <translation>Temporales</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="933"/>
+ <source>Hosts</source>
+ <translation>Dominios</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en un dominio para ver detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1020"/>
+ <source>Applications</source>
+ <translation>Aplicaciones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1127"/>
+ <source>Addresses</source>
+ <translation>Direcciones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1214"/>
+ <source>Ports</source>
+ <translation>Puertos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1298"/>
+ <source>Users</source>
+ <translation>Usuarios</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1404"/>
+ <source>Connections</source>
+ <translation>Conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1459"/>
+ <source>Dropped</source>
+ <translation>Rechazadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1514"/>
+ <source>Uptime</source>
+ <translation>Tiempo de ejecución</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1639"/>
+ <source>Version</source>
+ <translation>Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation>Borrar todos los eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="864"/>
+ <source>Edit rule</source>
+ <translation>Editar regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="878"/>
+ <source>Delete rule</source>
+ <translation>Borrar regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation type="obsolete">Borrar todos los hosts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation type="obsolete">Borrar todos las aplicaciones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation type="obsolete">Borrar todas las direcciones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation type="obsolete">Borrar todos los puertos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation type="obsolete">Borrar todos los usuarios</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete">(Doble click en una fila para editar una regla)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="912"/>
+ <source>Delete connections that matched this rule</source>
+ <translation>Borrar conexiones que coinciden con esta regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="797"/>
+ <source>All applications</source>
+ <translation>Todas las reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation>Rechazar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation>0</translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation>Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation>Ayuda</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation>Cerrar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation>Habilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation>Deshabilitar</translation>
+ </message>
+</context>
+<context>
+ <name>menu_close</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="131"/>
+ <source>Close</source>
+ <translation type="obsolete">Cerrar</translation>
+ </message>
+</context>
+<context>
+ <name>menu_help</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="126"/>
+ <source>Help</source>
+ <translation type="obsolete">Ayuda</translation>
+ </message>
+</context>
+<context>
+ <name>menu_statistics</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="120"/>
+ <source>Statistics</source>
+ <translation type="obsolete">Eventos</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation>Las notificaciones de sistema no están disponibles, tienes que instalar python3-notify2.</translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation>Denegar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation>para siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation>Conexión saliente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation>Proceso ejecutado desde:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation>este comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation>este ejecutable</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/>
+ <source>Unknown process</source>
+ <translation type="obsolete">Proceso no encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation>Hasta reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation>puerto {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/>
+ <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation>a {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation>UID {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation>a {0}.*</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation>a *.{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation type="obsolete">a *{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation>El proceso <b>Remoto</b> %s ejecutado en <b>%s</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation>está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation>está tratando de resolver <b>%s</b> via %s, %s puerto %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation>de este PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="105"/>
+ <source>New outgoing connection</source>
+ <translation>Nueva conexión saliente</translation>
+ </message>
+</context>
+<context>
+ <name>popups2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/>
+ <source>Exception saving config: %s</source>
+ <translation type="obsolete">Error al guarda la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/>
+ <source>Applying configuration on %s ...</source>
+ <translation type="obsolete">Aplicando configuración en %s ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/>
+ <source>Server address can not be empty</source>
+ <translation>La dirección del servidor no puede estar vacía</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/>
+ <source>Error loading %s configuration</source>
+ <translation type="obsolete">Error al cargar la configuración %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/>
+ <source>Configuration applied.</source>
+ <translation>Configuración aplicada.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/>
+ <source>Error applying configuration: %s</source>
+ <translation type="obsolete">Error al aplicar la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/>
+ <source>Exception saving config: {0}</source>
+ <translation>Error al guardar la configuración: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation>Aplicando configuración en {0} ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/>
+ <source>Error loading {0} configuration</source>
+ <translation>Error al cargar la configuración {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/>
+ <source>Error applying configuration: {0}</source>
+ <translation>Error al aplicar la configuración: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>Warning</source>
+ <translation>Aviso</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation>Debes seleccionar un fichero para la base de datos<br>o elegir el tipo En memoria.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>DB type changed</source>
+ <translation>El tipo de BBDD ha cambiado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation>Reinicia la GUI para que los cambios surtan efecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation>Pasa el ratón sobre los textos para mostrar la ayuda<br><br>Y no olvides visitar el wiki: <a href="{0}">{0}</a></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation><b>Error al carga la información del proceso:</b> <br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation><b>Error al parar de monitorizar el proceso:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation>cargando...</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/>
+ <source>There're no nodes connected.</source>
+ <translation>No hay nodos conectados.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/>
+ <source>Rule applied.</source>
+ <translation>Regla aplicada.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/>
+ <source>Error applying rule: %s</source>
+ <translation type="obsolete">Error al aplicar la regla: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation>el protocolo no puede estar vacío, o desmarca la opción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/>
+ <source>Protocol regexp error</source>
+ <translation>Error en la expresión regular del Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/>
+ <source>process path can not be empty</source>
+ <translation>La ruta del ejecutable no puede estar vacía</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/>
+ <source>Process path regexp error</source>
+ <translation>Error en la expresión regular del ejecutable</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/>
+ <source>command line can not be empty</source>
+ <translation>El comando no puede estar vacío, o desmarca la opción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/>
+ <source>Command line regexp error</source>
+ <translation>Error en la expresión regular del Comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/>
+ <source>Dest port can not be empty</source>
+ <translation>El puerto destino no puede estar vacío, o desmarca la opción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/>
+ <source>Dst port regexp error</source>
+ <translation>Error en la expresión regular del Puerto Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/>
+ <source>Dest host can not be empty</source>
+ <translation>El Host destino no puede estar vacío, o desmarca la opción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/>
+ <source>Dst host regexp error</source>
+ <translation>Error en la expresión regular del Host de destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>La IP/Red de destino no puede estar vacía</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/>
+ <source>Dst IP regexp error</source>
+ <translation>Error en la expresión regular de IP/Red de destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/>
+ <source>User ID can not be empty</source>
+ <translation>El ID de Usuario no puede estar vacío, o desmarca la opción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/>
+ <source>User ID regexp error</source>
+ <translation>Error en la expresión regular del ID de Usuario</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Error al aplicar la regla: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/>
+ <source>Lists field cannot be empty</source>
+ <translation>El campo Listas de dominios no puede estar vacío</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/>
+ <source>Lists field must be a directory</source>
+ <translation>El campo Listas debe ser un directorio</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>Tipo de regla no soportada</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Error cargando la regla</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/>
+ <source>There's already a rule with this name.</source>
+ <translation>Ya hay una regla con este nombre.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/>
+ <source>PID field can not be empty</source>
+ <translation>El campo de PID no puede estar vacío</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/>
+ <source>PID field regexp error</source>
+ <translation>Error en la expresión regular del PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/>
+ <source>Select at least one field.</source>
+ <translation>Selecciona al menos un campo.</translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation>Parado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation>Deshabilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation>Interceptando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="412"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="414"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="761"/>
+ <source> Your are about to delete this rule. </source>
+ <translation> Estás a punto de borrar esta regla. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> Are you sure?</source>
+ <translation> ¿Estás seguro?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="568"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation>Eventos de red OpenSnitch {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="570"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation>Eventos de red OpenSnitch de {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="176"/>
+ <source>Status</source>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="177"/>
+ <source>Hostname</source>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="183"/>
+ <source>Version</source>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="180"/>
+ <source>Rules</source>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="253"/>
+ <source>Hits</source>
+ <translation>Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/>
+ <source>Save as CSV</source>
+ <translation>Guardar como CSV</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="738"/>
+ <source>Delete</source>
+ <translation>Eliminar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source>always</source>
+ <translation type="obsolete">siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="580"/>
+ <source><b>Error:</b><br><br>{0}</source>
+ <translation type="obsolete"><b>Error:</b><br><br>{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="731"/>
+ <source>Disable</source>
+ <translation>Deshabilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="733"/>
+ <source>Enable</source>
+ <translation>Habilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Duplicate</source>
+ <translation>Duplicar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="737"/>
+ <source>Edit</source>
+ <translation>Editar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="891"/>
+ <source>Rule not found by that name and node</source>
+ <translation>Regla no encontrada por ese nombre o nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="921"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation><b>Error:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="928"/>
+ <source>Warning:</source>
+ <translation>Aviso:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="717"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="718"/>
+ <source>Deny</source>
+ <translation>Denegar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="722"/>
+ <source>Always</source>
+ <translation>Siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="723"/>
+ <source>Until reboot</source>
+ <translation>Hasta reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> You are about to delete this rule. </source>
+ <translation> Estás a punto de borrar esta regla. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="174"/>
+ <source>LastConnection</source>
+ <translation type="obsolete">ÚltimaConexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">ÚltimaConexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="392"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="379"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="380"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="390"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="395"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="396"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="391"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="393"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="377"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>ÚltimaConexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Args</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>IPDestino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>HostDestino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>PuertoDestino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="175"/>
+ <source>Addr</source>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="181"/>
+ <source>Connections</source>
+ <translation type="obsolete">Conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="182"/>
+ <source>Dropped</source>
+ <translation type="obsolete">Rechazadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="252"/>
+ <source>What</source>
+ <translation>Qué</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="709"/>
+ <source>Apply to</source>
+ <translation>Aplicar a</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="719"/>
+ <source>Reject</source>
+ <translation>Rechazar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation>Red</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="378"/>
+ <source>Addr</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="382"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Tiempo de ejecución</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Rechazadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Qué</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="394"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Prioritaria</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="644"/>
+ <source>New node connected</source>
+ <translation>Nuevo nodo conectado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="774"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="obsolete"> Estás a punto de borrar esta regla. </translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="776"/>
+ <source> Are you sure?</source>
+ <translation type="obsolete"> ¿Estás seguro?</translation>
+ </message>
+</context>
+<context>
+ <name>stats_disabled</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="74"/>
+ <source>Disabled</source>
+ <translation type="obsolete">Deshabilitado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_notrunning</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="73"/>
+ <source>Not running</source>
+ <translation type="obsolete">Parado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_running</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="75"/>
+ <source>Running</source>
+ <translation type="obsolete">Interceptando</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="409"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de red OpenSnitch</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="411"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="2.0">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>New node connected</name>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="754"/>
+ <source>Default duration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="863"/>
+ <source>once</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="905"/>
+ <source>deny</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="914"/>
+ <source>allow</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="711"/>
+ <source>Nodes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="717"/>
+ <source>Process monitor method</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="751"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="884"/>
+ <source>Address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1024"/>
+ <source>Default log level</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>Version</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="737"/>
+ <source>Log file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="809"/>
+ <source>HostName</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="983"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="868"/>
+ <source>until restart</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="873"/>
+ <source>always</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="995"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1000"/>
+ <source>/dev/stdout</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="767"/>
+ <source>Apply configuration to all nodes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1039"/>
+ <source>Database</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1074"/>
+ <source>In memory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1079"/>
+ <source>File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1345"/>
+ <source>Close</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1356"/>
+ <source>Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1367"/>
+ <source>Save</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="665"/>
+ <source>Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1093"/>
+ <source>Database type</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1100"/>
+ <source>Select</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="894"/>
+ <source>Debug invalid connections</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="479"/>
+ <source>any temporary rules</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="492"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="495"/>
+ <source>Don't save rules of duration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="601"/>
+ <source>Time</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>Destination</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="649"/>
+ <source>Protocol</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="697"/>
+ <source>Process</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="617"/>
+ <source>Rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="633"/>
+ <source>Node</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="589"/>
+ <source>Events tab columns</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="508"/>
+ <source>Desktop notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="526"/>
+ <source>Use system notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="542"/>
+ <source>Use Qt notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="571"/>
+ <source>Test</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1178"/>
+ <source>minutes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1204"/>
+ <source>Minutes between events purges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1227"/>
+ <source>days</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1237"/>
+ <source>Maximum days of events to keep</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="14"/>
+ <source>Rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/>
+ <source>Node</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/>
+ <source>Apply rule to all nodes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/>
+ <source>From this command line</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/>
+ <source>From this executable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/>
+ <source>Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/>
+ <source>To this IP / Network</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/>
+ <source>once</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/>
+ <source>30s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/>
+ <source>5m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/>
+ <source>15m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/>
+ <source>30m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/>
+ <source>1h</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/>
+ <source>always</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/>
+ <source>To this port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/>
+ <source>From this user ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="430"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/>
+ <source>TCP</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/>
+ <source>UDP</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/>
+ <source>UDPLITE</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>TCP6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/>
+ <source>UDP6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/>
+ <source>UDPLITE6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/>
+ <source>LAN</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/>
+ <source>127.0.0.0/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/>
+ <source>192.168.0.0/24</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/>
+ <source>192.168.1.0/24</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/>
+ <source>192.168.2.0/24</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/>
+ <source>192.168.0.0/16</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/>
+ <source>169.254.0.0/16</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/>
+ <source>172.16.0.0/12</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/>
+ <source>10.0.0.0/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/>
+ <source>::1/128</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/>
+ <source>fc00::/7</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/>
+ <source>ff00::/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/>
+ <source>fe80::/10</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/>
+ <source>fd00::/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/>
+ <source>Duration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/>
+ <source>Protocol</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/>
+ <source>To this host</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/>
+ <source>Deny</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/>
+ <source>Allow</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="713"/>
+ <source>leave blank to autocreate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="54"/>
+ <source>Priority rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="77"/>
+ <source>Case-sensitive</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/>
+ <source>until reboot</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/>
+ <source>To this list of domains</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/>
+ <source>Applications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/>
+ <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="246"/>
+ <source>From this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/>
+ <source>Network</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/>
+ <source>List of domains/IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>To this list of network ranges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/>
+ <source>To this list of IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="608"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="636"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="651"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="290"/>
+ <source>Save to CSV.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="300"/>
+ <source>Ctrl+S</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="351"/>
+ <source>Create a new rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="381"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="420"/>
+ <source>Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1665"/>
+ <source>-</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="464"/>
+ <source>Start or Stop interception</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="509"/>
+ <source>Events</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="748"/>
+ <source>Nodes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1569"/>
+ <source>Rules</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="857"/>
+ <source>enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="704"/>
+ <source>Application rules</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="806"/>
+ <source>Permanent</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="815"/>
+ <source>Temporary</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="933"/>
+ <source>Hosts</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1020"/>
+ <source>Applications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1127"/>
+ <source>Addresses</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1214"/>
+ <source>Ports</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1298"/>
+ <source>Users</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1404"/>
+ <source>Connections</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1459"/>
+ <source>Dropped</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1514"/>
+ <source>Uptime</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1639"/>
+ <source>Version</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="864"/>
+ <source>Edit rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="878"/>
+ <source>Delete rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="912"/>
+ <source>Delete connections that matched this rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="797"/>
+ <source>All applications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="105"/>
+ <source>New outgoing connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/>
+ <source>Server address can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/>
+ <source>Configuration applied.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/>
+ <source>Exception saving config: {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/>
+ <source>Error loading {0} configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/>
+ <source>Error applying configuration: {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>DB type changed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/>
+ <source>There're no nodes connected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/>
+ <source>Rule applied.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/>
+ <source>Protocol regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/>
+ <source>process path can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/>
+ <source>Process path regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/>
+ <source>command line can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/>
+ <source>Command line regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/>
+ <source>Dest port can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/>
+ <source>Dst port regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/>
+ <source>Dest host can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/>
+ <source>Dst host regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/>
+ <source>Dst IP regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/>
+ <source>User ID can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/>
+ <source>User ID regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Error applying rule: {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/>
+ <source><b>Error loading rule</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/>
+ <source>Lists field cannot be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/>
+ <source>Lists field must be a directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/>
+ <source><b>Rule not supported</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/>
+ <source>There's already a rule with this name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/>
+ <source>PID field can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/>
+ <source>PID field regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/>
+ <source>Select at least one field.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="761"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> Are you sure?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="568"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="570"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/>
+ <source>Save as CSV</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="738"/>
+ <source>Delete</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="921"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="928"/>
+ <source>Warning:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="717"/>
+ <source>Allow</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="718"/>
+ <source>Deny</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="722"/>
+ <source>Always</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="723"/>
+ <source>Until reboot</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="731"/>
+ <source>Disable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="733"/>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Duplicate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="737"/>
+ <source>Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="891"/>
+ <source>Rule not found by that name and node</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> You are about to delete this rule. </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="392"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="379"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="380"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="390"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="395"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="396"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="391"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="393"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="377"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="252"/>
+ <source>What</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="253"/>
+ <source>Hits</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="709"/>
+ <source>Apply to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="719"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="378"/>
+ <source>Addr</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="382"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="394"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="644"/>
+ <source>New node connected</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="2.0" language="fr" sourcelanguage="">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>opensnitch-qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>ID utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-weight:600;">Exécuté depuis</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation>TextLabel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>IP source</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>ID processus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>IP destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>interface destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>depuis cet exécutable</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>depuis cette ligne de commande</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>vers cette interface</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>cet utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>vers cette IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation>une seule fois</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation>5mn</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation>15mn</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation>30mn</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="706"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation>définitivement</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation>Refuser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation>Permettre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation>jusqu'au redémarrage</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>New node connected</name>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Préférences</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation>IU</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="54"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source>
+ <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation>attente par défaut</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation>attente dialogue par défaut</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="754"/>
+ <source>Default duration</source>
+ <translation>attente par défaut</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">Acción por defecto de la ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">Acción por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation>cible par défaut</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation>centré</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation>en haut à droite</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation>en bas à gauche</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation>en haut à gauche</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation>en bas à gauche</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="167"/>
+ <source>Prompt dialog default position on screen</source>
+ <translation type="obsolete">Posición por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation>par l'exécutable</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation>par la ligne de commande</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation>par l'interface de destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation>par l'IP de destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation>par cet utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="863"/>
+ <source>once</source>
+ <translation>une seule fois</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation>définitivement</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="905"/>
+ <source>deny</source>
+ <translation>Refuser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="914"/>
+ <source>allow</source>
+ <translation>Autoriser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">Pas de dialogue, montrer juste une alerte</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="711"/>
+ <source>Nodes</source>
+ <translation>noeuds</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="717"/>
+ <source>Process monitor method</source>
+ <translation>méthode de surveillance des processus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="751"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>La durée par défaut concerne le cas où aucun utilisateur n'est connecté.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>Adresse du noeud.</p><p>Default: unix:///tmp/osui.sock (unix:// requise si c'est un socket Unix)</p><p>Peut aussi être une adresse IP avec son port: 127.0.0.1:50051</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="884"/>
+ <source>Address</source>
+ <translation>Adresse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1024"/>
+ <source>Default log level</source>
+ <translation>détail noté dans le log par défaut</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>Version</source>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>L'action par défaut se déclenche s'il n'y a pas d'utilisateur connecté.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>fichier dans lequel enregistrer le log<br/></p><p>/dev/stdout imprime les logs dans le standard output.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="737"/>
+ <source>Log file</source>
+ <translation>Fichier log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones.
+
+La ventana emergente sólo contendrá información relativa a la conexión.
+
+Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente
+es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">Interceptar conexiones desconocidas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="809"/>
+ <source>HostName</source>
+ <translation>nom d'hôte (host)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="983"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation>unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="868"/>
+ <source>until restart</source>
+ <translation>jusqu'au redémarrage</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="873"/>
+ <source>always</source>
+ <translation>toujours</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="995"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation>/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1000"/>
+ <source>/dev/stdout</source>
+ <translation>/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="767"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>appliquer la configuration à tous les noeuds</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1039"/>
+ <source>Database</source>
+ <translation>Base de données</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1074"/>
+ <source>In memory</source>
+ <translation>En mémoire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1079"/>
+ <source>File</source>
+ <translation>Fichier</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1345"/>
+ <source>Close</source>
+ <translation>Fermer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1356"/>
+ <source>Apply</source>
+ <translation>Appliquer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1367"/>
+ <source>Save</source>
+ <translation>Enregistrer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation>jusqu'au redémarrage</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1093"/>
+ <source>Database type</source>
+ <translation>type de base de données</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1100"/>
+ <source>Select</source>
+ <translation>Choisir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Pop-ups default options</source>
+ <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="367"/>
+ <source>Pop-ups default position on screen</source>
+ <translation type="obsolete">Posición en pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="102"/>
+ <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source>
+ <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation>Montrer par défaut la vue détaillée</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="665"/>
+ <source>Action</source>
+ <translation>Action</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation><html><head/><body><p>Sélectionner pour que les dialogues s'ouvrent avec le détail avancé.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation>Durée</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation><html><head/><body><p>Par défaut un nouveau dialogue en version simple propose de filtrer les connexions ou les applications sur une propriété de la connexion (exécutable, port, IP, etc).</p><p>Avec ces options, vous pouvez choisir plusieurs critères pour filtrer les connexions.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation>Filtrer aussi les connexion par :</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="362"/>
+ <source>If checked, this field will be checked when a pop-up is displayed</source>
+ <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation>ID utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation>interface (port) de destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation>IP de destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation><html><head/><body><p>Ctte durée est l'attente par défaut lorsqu'un dialogue est présenté.</p><p>Sans réponse au dialogue, les options par défaut sont appliquées.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation>La vue avancée permet de choisir facileent plusieurs critères pour filtrer les connexions</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation>cocher pour que ce champ soit préselectionné lorsqu'un dialogue est affiché</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation><html><head/><body><p>Action par défaut du dialogue.</p><p>Lorsqu'un nouveau dialogue est affiché, cette action sera présélectionnée, et donc appliquée s'il n'y a pas de réponse après le délai par défaut.</p><p><br/></p><p>Pendant que le dialogue demande si on autorise ou non la connexion:</p><p>1. toute autre nouvelle demande de connexion est refusée.</p><p>2. les connexions déjà connues sont autorisée ou rejetées selon les règles définies par l'utilisateur.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>Action par défaut lorsque l'interface graphique utilisateur n'est pas connectée</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="894"/>
+ <source>Debug invalid connections</source>
+ <translation>Noter (debug) les connexions invalides</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation>Dialogues</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation>Options par défaut</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation>Position par défaut sur l'écran</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="479"/>
+ <source>any temporary rules</source>
+ <translation>toute règle temporaire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="492"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation><html><head/><body><p>Lorsque cette option est choisie, les règles de la durée sélectionnée ne sont pas ajoutées à la liste des règles temporaires présentées dans l'interface graphique utilisateur.</p><p><br/></p><p>Les règles temporaires sont cependant toujours valides et peuvent être utilisées lors d'une demande d'autorisation /refus de connexion.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="495"/>
+ <source>Don't save rules of duration</source>
+ <translation>Ne pas enregistrer les règles de cette durée :</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="463"/>
+ <source>Show events columns</source>
+ <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="601"/>
+ <source>Time</source>
+ <translation>Temps</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>Destination</source>
+ <translation>Destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="649"/>
+ <source>Protocol</source>
+ <translation>Protocole</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="697"/>
+ <source>Process</source>
+ <translation>Processus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="617"/>
+ <source>Rule</source>
+ <translation>Règle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="633"/>
+ <source>Node</source>
+ <translation>Noeud</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="723"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>cochez ceci pour recevoir une demande d'autorisation/refus sur les connexions qui n'ont pas de processus (PID) associé, généralement parce que la connexion est en mauvais état.</p><p>Le dialogue ne contiendra alors que l'information sur la connexion.</p><p>Il y a cependant des scénarios pour lesquels ces demandes sont valides, comme par exemple l'établissement d'un VPN utilisant wireguard.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="589"/>
+ <source>Events tab columns</source>
+ <translation>colonnes évènements</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="508"/>
+ <source>Desktop notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="526"/>
+ <source>Use system notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="542"/>
+ <source>Use Qt notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="571"/>
+ <source>Test</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1178"/>
+ <source>minutes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1204"/>
+ <source>Minutes between events purges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1227"/>
+ <source>days</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1237"/>
+ <source>Maximum days of events to keep</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation>Détail processus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation>chargement...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation>chargement CWD...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation>chargement statistiques mémoire...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation>Etat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation>Fichiers ouverts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation>Statistiques entrées/sorties</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation>Fichiers mappés en mémoire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation>Pile</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation>Variables d'environnement</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation>PIDs application</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation>Démarrer ou arrêter la surveillance de ce processus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation>Fermer</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="14"/>
+ <source>Rule</source>
+ <translation>Règle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/>
+ <source>Node</source>
+ <translation>Noeud</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/>
+ <source>Apply rule to all nodes</source>
+ <translation>Appliquer la règle à tous les noeuds</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/>
+ <source>From this command line</source>
+ <translation>Depuis cette ligne de commande</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/>
+ <source>From this executable</source>
+ <translation>Depuis cet exécutable</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/>
+ <source>Action</source>
+ <translation>Action</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/>
+ <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source>
+ <translation type="obsolete">/chemin/vers/executable, .*/bin/executable[0-9\.]+$, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/>
+ <source>To this IP / Network</source>
+ <translation>vers cette IP / ce réseau</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/>
+ <source>once</source>
+ <translation>une seule fois</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source>until restart</source>
+ <translation type="obsolete">hasta reiniciar (el servicio)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/>
+ <source>always</source>
+ <translation>toujours</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/>
+ <source>To this port</source>
+ <translation>vers cette interface (port)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/>
+ <source>From this user ID</source>
+ <translation>depuis cet utilisateur (ID)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation>ni virgules ni espaces ne sont autorisés pour spécifier de multiples domaines.
+
+Utiliser à la place des 'expressions régulières' (RegExp):
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+ou bien un seul domaine:
+www.gnu.org - correspond uniquement à www.gnu.org, mais pas ftp.gnu.org, ou www2.gnu.org, ...
+gnu.org - correspond uniquement à gnu.org, mais pas www.gnu.org, ou ftp.gnu.org, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="430"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation>www.domain.org, .*\.domain.org</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation><html><head/><body><p>Seuls TCP, UDP ou UDPLITE sont permis</p><p>On peut employer des expressions régulières (regexp), par ex.: ^(TCP|UDP)$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/>
+ <source>TCP</source>
+ <translation>TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/>
+ <source>UDP</source>
+ <translation>UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/>
+ <source>UDPLITE</source>
+ <translation>UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>TCP6</source>
+ <translation>TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/>
+ <source>UDP6</source>
+ <translation>UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/>
+ <source>UDPLITE6</source>
+ <translation>UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation>On peut spécifier une IP unique:
+- 192.168.1.1
+
+ou une "expression régulière":
+- 192\.168\.1\.[0-9]+
+pluseurs IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+On peut aussi spécifier un sous-réseau:
+- 192.168.1.0/24
+
+Note : on ne peut pas utiliser virgules ou espaces pour séparer plusieurs IP ou réseaux.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/>
+ <source>LAN</source>
+ <translation>LAN</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/>
+ <source>127.0.0.0/8</source>
+ <translation>127.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/>
+ <source>192.168.0.0/24</source>
+ <translation>192.168.0.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/>
+ <source>192.168.1.0/24</source>
+ <translation>192.168.1.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/>
+ <source>192.168.2.0/24</source>
+ <translation>192.168.2.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/>
+ <source>192.168.0.0/16</source>
+ <translation>192.168.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/>
+ <source>169.254.0.0/16</source>
+ <translation>169.254.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/>
+ <source>172.16.0.0/12</source>
+ <translation>172.16.0.0/12</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/>
+ <source>10.0.0.0/8</source>
+ <translation>10.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/>
+ <source>::1/128</source>
+ <translation>::1/128</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/>
+ <source>fc00::/7</source>
+ <translation>fc00::/7</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/>
+ <source>ff00::/8</source>
+ <translation>ff00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/>
+ <source>fe80::/10</source>
+ <translation>fe80::/10</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/>
+ <source>fd00::/8</source>
+ <translation>fd00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/>
+ <source>Duration</source>
+ <translation>Durée</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/>
+ <source>Protocol</source>
+ <translation>Protocole</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/>
+ <source>To this host</source>
+ <translation>Vers cet hôte</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/>
+ <source>Deny</source>
+ <translation>Refuser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/>
+ <source>Allow</source>
+ <translation>Autoriser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Name</source>
+ <translation>Nom</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/>
+ <source>Enable</source>
+ <translation>Activer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation>Les règles étant appliquées par ordre aplhabétique, on peut leur donner priorité grâce à leur nom.
+
+000-allow-localhost
+001-deny-broadcast
+...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="713"/>
+ <source>leave blank to autocreate</source>
+ <translation>ne pas remplir va créer automatiquement</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation>Cocher pour que cette règle ait priorité sur toutes les autres. Aucune autre règle ne sera appliquée après celle-ci.
+
+Vous devez nommer la règle de façon qu'elle soit testée en premier, par ordre alphabétique. Par exemple:
+
+[x] Priority - 000-règle-prioritaire
+[ ] Priority - 001-règle-moins-prioritaire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="54"/>
+ <source>Priority rule</source>
+ <translation>Règle prioritaire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation><html><head/><body><p>Par défaut, le champ des règles n'est pas sensible à la casse. Par exemple si un processus tente d'atteindre gOOgle.CoM alors que vous avez une règle interdisant .*google.com, la connexion gOOgle.CoM sera bloquée.<br/></p><p>Si vous cochez ceci, vous devrez spécifier la chaîne exacte (domaine, exécutable, ligne de commande) que vous voulez filtrer.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="77"/>
+ <source>Case-sensitive</source>
+ <translation>sensible à la casse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation><html><head/><body><p>Vous pouvez spécifier plusieurs ports d'interface avec des "expessions régulières":</p><p><br/></p><p>- 53, 80 ou 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 ou 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/>
+ <source>until reboot</source>
+ <translation>jusqu'au redémarrage</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/>
+ <source>To this list of domains</source>
+ <translation>Vers cette liste de domaines</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Choisir un dossier contenant des listes de domaines à autoriser ou interdire.</p><p>Y mettre des fichiers contenant des listes de domaines, avec n'importe quelle extension.</p><p><br/>Le format de chaque entrée (hist format) est comme suit:</p><p>127.0.0.1 www.domain.com</p><p>ou </p><p>0.0.0.0 www.domain.com</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/>
+ <source>Applications</source>
+ <translation type="unfinished">Applications</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/>
+ <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="246"/>
+ <source>From this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/>
+ <source>Network</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/>
+ <source>List of domains/IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>To this list of network ranges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/>
+ <source>To this list of IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="608"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="636"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="651"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>Statistiques réseau Opensnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="290"/>
+ <source>Save to CSV.</source>
+ <translation>Enregistrer au format CSV.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="300"/>
+ <source>Ctrl+S</source>
+ <translation>contrôle-S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="351"/>
+ <source>Create a new rule</source>
+ <translation>Créer une nouvelle règle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="381"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="420"/>
+ <source>Status</source>
+ <translation>Etat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1665"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="464"/>
+ <source>Start or Stop interception</source>
+ <translation>Démarre ou stoppe l'interception</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="509"/>
+ <source>Events</source>
+ <translation>Evènements</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation>Filtre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation>Autoriser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation>Refuser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation>Ex.: firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="748"/>
+ <source>Nodes</source>
+ <translation>Noeuds</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double cliquer sur la colonne Addr pour voir les détails d'un noeud)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1569"/>
+ <source>Rules</source>
+ <translation>Règles</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="857"/>
+ <source>enable</source>
+ <translation>activer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="684"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation type="obsolete">rechercher par nom de règle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="704"/>
+ <source>Application rules</source>
+ <translation>Règle d'applications</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="806"/>
+ <source>Permanent</source>
+ <translation>Permanent</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="815"/>
+ <source>Temporary</source>
+ <translation>Temporaire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="933"/>
+ <source>Hosts</source>
+ <translation>Hôtes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double clic pour voir les détails d'un élément)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1020"/>
+ <source>Applications</source>
+ <translation>Applications</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1127"/>
+ <source>Addresses</source>
+ <translation>Adresses</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1214"/>
+ <source>Ports</source>
+ <translation>Ports (interfaces)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1298"/>
+ <source>Users</source>
+ <translation>Utilisateurs</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1404"/>
+ <source>Connections</source>
+ <translation>Connexions</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1459"/>
+ <source>Dropped</source>
+ <translation>Abandonnée</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1514"/>
+ <source>Uptime</source>
+ <translation>durée active (uptime)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1639"/>
+ <source>Version</source>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation>Effacer tous les évènments interceptés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="864"/>
+ <source>Edit rule</source>
+ <translation>Editer règle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="878"/>
+ <source>Delete rule</source>
+ <translation>Effacer règle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation type="obsolete">Effacer tous les hôtes interceptés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation type="obsolete">Effacer toutes les applications interceptées</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation type="obsolete">Effacer toutes les adresses interceptées</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation type="obsolete">Effacer tous les ports interceptés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation type="obsolete">Effacer tous les utilisateurs interceptés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(double clic sur une ligne pour voir les détails d'une règle)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="912"/>
+ <source>Delete connections that matched this rule</source>
+ <translation>Effacer les connexions qui déclenchaient cette règle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="797"/>
+ <source>All applications</source>
+ <translation>Toutes les applications</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation>Statistiques</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation>Aide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation>Fermer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation>Activer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation>Désactiver</translation>
+ </message>
+</context>
+<context>
+ <name>menu_close</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="131"/>
+ <source>Close</source>
+ <translation type="obsolete">Cerrar</translation>
+ </message>
+</context>
+<context>
+ <name>menu_help</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="126"/>
+ <source>Help</source>
+ <translation type="obsolete">Ayuda</translation>
+ </message>
+</context>
+<context>
+ <name>menu_statistics</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="120"/>
+ <source>Statistics</source>
+ <translation type="obsolete">Eventos</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation>Autoriser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation>Refuser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation>indéfiniment</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation>Connexion sortante</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation>Processus lancé par :</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation>depuis cette ligne de commande</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation>depuis cet exécutable</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/>
+ <source>Unknown process</source>
+ <translation type="obsolete">Proceso no encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation>jusqu'au redémarrage</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation>vers le port {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/>
+ <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation>vers {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation>de l'utilisateur {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation>vers {0}.*</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation>vers *.{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation type="obsolete">vers *{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation>processus <b>Distant</b> %s tournant sur <b>%s</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation>se connecte à <b>%s</b> sur %s port %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation>tente de résoudre (connecter) <b>%s</b> via %s, %s port %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="105"/>
+ <source>New outgoing connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/>
+ <source>Exception saving config: %s</source>
+ <translation type="obsolete">Error al guarda la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/>
+ <source>Applying configuration on %s ...</source>
+ <translation type="obsolete">Aplicando configuración en %s ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/>
+ <source>Server address can not be empty</source>
+ <translation>L'adresse du serveur ne peut être vide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/>
+ <source>Error loading %s configuration</source>
+ <translation type="obsolete">Error al cargar la configuración %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/>
+ <source>Configuration applied.</source>
+ <translation>Configuration appliquée.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/>
+ <source>Error applying configuration: %s</source>
+ <translation type="obsolete">Error al aplicar la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/>
+ <source>Exception saving config: {0}</source>
+ <translation>Config enregistrant les exceptions : {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation>Applique la configuration à {0} ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/>
+ <source>Error loading {0} configuration</source>
+ <translation>Erreur au chargement configuration {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/>
+ <source>Error applying configuration: {0}</source>
+ <translation>Erreur en tentant d'appliquer la configution : {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>Warning</source>
+ <translation>Alerte</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation>Vous devez sélectionner un fichier pour la base de données<br>ou choisir le type "en mémoire".</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>DB type changed</source>
+ <translation>type de base de données changé</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation>Prendra effet au redémarrage de l'interface graphique</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation>Glisser la souris sur les textes pour afficher l'aide<br><br>N'hésitez pas à visiter le Wiki : <a href="{0}">{0}</a></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation><b>Erreur au chargement d'information sur le processus:</b> <br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation><b>Erreur en stoppant le processus de monitoring:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation>chargement...</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/>
+ <source>There're no nodes connected.</source>
+ <translation>Aucun noeud n'est connecté.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/>
+ <source>Rule applied.</source>
+ <translation>Règle appliquée.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/>
+ <source>Error applying rule: %s</source>
+ <translation type="obsolete">Error al aplicar la regla: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation>le protocole ne peut être vide, ou bien le désélectionner</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/>
+ <source>Protocol regexp error</source>
+ <translation>Erreur à l'interprétation du RegExp protocole</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/>
+ <source>process path can not be empty</source>
+ <translation>le chemin vers le processus ne peut être vide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/>
+ <source>Process path regexp error</source>
+ <translation>erreur RegExp dans le chemin vers le processus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/>
+ <source>command line can not be empty</source>
+ <translation>la ligne de commande ne peut être vide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/>
+ <source>Command line regexp error</source>
+ <translation>Erreur RegExp dans la ligne de commande</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/>
+ <source>Dest port can not be empty</source>
+ <translation>l'interface (port) destination ne peut être vide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/>
+ <source>Dst port regexp error</source>
+ <translation>ereur RegExp sur l'interface (port) de destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/>
+ <source>Dest host can not be empty</source>
+ <translation>l'hôte de destination ne peut être vide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/>
+ <source>Dst host regexp error</source>
+ <translation>erreur RegExp sur l'hôte de destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>l'IP / réseau de destination ne peut être vide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/>
+ <source>Dst IP regexp error</source>
+ <translation>erreur RegExp sur l'IP destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/>
+ <source>User ID can not be empty</source>
+ <translation>l'ID utilisateur ne peut être vide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/>
+ <source>User ID regexp error</source>
+ <translation>erreur RegExp sur l'ID utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Erreur en appliquant la règle : {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/>
+ <source>Lists field cannot be empty</source>
+ <translation>le champ "listes" ne peut être vide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/>
+ <source>Lists field must be a directory</source>
+ <translation>le champ Listes doit être un répertoire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>RRègle non supportée</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Erreur au chargement de la règle</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/>
+ <source>There's already a rule with this name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/>
+ <source>PID field can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/>
+ <source>PID field regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/>
+ <source>Select at least one field.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation>Inactif</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation>désactivé</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation>Actif</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="412"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="414"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="761"/>
+ <source> Your are about to delete this rule. </source>
+ <translation> Vous êtes sur le point d'effacer cette règle. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> Are you sure?</source>
+ <translation> Êtes-vous sûr?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="568"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation>Statistiques réseau OpenSnitch {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="570"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation>Statistique réseau OpenSnitch pour {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="176"/>
+ <source>Status</source>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="177"/>
+ <source>Hostname</source>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="183"/>
+ <source>Version</source>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="180"/>
+ <source>Rules</source>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="253"/>
+ <source>Hits</source>
+ <translation type="unfinished">Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/>
+ <source>Save as CSV</source>
+ <translation>Enregistrer en CSV</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="738"/>
+ <source>Delete</source>
+ <translation>Effacer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source>always</source>
+ <translation type="obsolete">siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="580"/>
+ <source><b>Error:</b><br><br>{0}</source>
+ <translation type="obsolete"><b>Error:</b><br><br>{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="731"/>
+ <source>Disable</source>
+ <translation>Désactiver</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="733"/>
+ <source>Enable</source>
+ <translation>Activer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Duplicate</source>
+ <translation>Dupliquer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="737"/>
+ <source>Edit</source>
+ <translation>Editer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="891"/>
+ <source>Rule not found by that name and node</source>
+ <translation>Pas trouvé de règle pour ces nom et noeud</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="921"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation><b>Erreur:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="928"/>
+ <source>Warning:</source>
+ <translation>Alerte :</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="717"/>
+ <source>Allow</source>
+ <translation>Autoriser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="718"/>
+ <source>Deny</source>
+ <translation>Refuser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="722"/>
+ <source>Always</source>
+ <translation>Toujours</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="723"/>
+ <source>Until reboot</source>
+ <translation>Jusqu'au redémarrage</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> You are about to delete this rule. </source>
+ <translation> Vous êtes sur le point d'effacer cette règle. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="174"/>
+ <source>LastConnection</source>
+ <translation type="obsolete">Última Conexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">ÚltimaConexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="392"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Nom</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Adresse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="379"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Etat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="380"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Nom d'hôte</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Règles</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="390"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Temps</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="395"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Action</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="396"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Durée</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="391"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Noeud</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="393"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Activé</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Connexions</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Protocole</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Processus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Destination</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Règle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>ID utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="377"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DernièreConnexion</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Args</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DstIP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DstHost</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DstPort</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="179"/>
+ <source>Uptime</source>
+ <translation type="obsolete">durée active (uptime)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="181"/>
+ <source>Connections</source>
+ <translation type="obsolete">Connexions</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="182"/>
+ <source>Dropped</source>
+ <translation type="obsolete">Abandonnée</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="252"/>
+ <source>What</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="709"/>
+ <source>Apply to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="719"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="378"/>
+ <source>Addr</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="382"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">durée active (uptime)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">Connexions</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">Abandonnée</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="394"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="644"/>
+ <source>New node connected</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="774"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="obsolete"> Estás a punto de borrar esta regla. </translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="776"/>
+ <source> Are you sure?</source>
+ <translation type="obsolete"> ¿Estás seguro?</translation>
+ </message>
+</context>
+<context>
+ <name>stats_disabled</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="74"/>
+ <source>Disabled</source>
+ <translation type="obsolete">Deshabilitado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_notrunning</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="73"/>
+ <source>Not running</source>
+ <translation type="obsolete">Parado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_running</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="75"/>
+ <source>Running</source>
+ <translation type="obsolete">Interceptando</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="409"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de red OpenSnitch</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="411"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="hu_HU" sourcelanguage="">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>opensnitch-qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="150"/>
+ <source>Chromium Web Browser</source>
+ <translation type="obsolete">Chromium webböngésző</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="179"/>
+ <source><html><head/><body><p>/opt/google/chrome/bin/chrome --something abc --more-long def --for-word-wrapping</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>/opt/google/chrome/bin/chrome --valami ábécé --hosszabb-ideig def --szócsomagoláshoz</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="226"/>
+ <source>(/path/to/bin/chromium)</source>
+ <translation type="obsolete">(/út/a/bináris/Chromiumhoz)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>ettől a futtatható fájltól</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>erről a parancssorról</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>ezt a célkikötőt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>ezt a felhasználót</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>ezt a cél IP címet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation>egyszer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation>30 másodperc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation>5 perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation>15 perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation type="unfinished">30 perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation>1 óra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation>újraindításig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation>örökre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation>Megtagadás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation>Engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>Felhasználói azonosító</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-weight:600;">Futtatható fájl innen:</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation>Szövegcímke</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>Forrás IP-cím</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>Folyamatazonosító</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>Cél IP-címe</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>Célkikötő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="271"/>
+ <source>Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</source>
+ <translation type="obsolete">A Chromium webböngésző csatlakozni akar a www.evilsocket.net webhelyhez a 443-as TCP-kikötőn. És talán a www.goodsocket.net webhelyhez a 344-es TCP-kikötőn</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation>ebből a folyamatazonosítóból</translation>
+ </message>
+</context>
+<context>
+ <name>New node connected</name>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Beállítások</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation>Felhasználói felület</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Pop-ups default options</source>
+ <translation type="obsolete">Az előugró ablakok alapértelmezett beállításai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="367"/>
+ <source>Pop-ups default position on screen</source>
+ <translation type="obsolete">Felugró ablakok alapértelmezett helye a képernyőn</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation>Alapértelmezés szerint a haladó nézet megjelenítése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="863"/>
+ <source>once</source>
+ <translation>egyszer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation>30 másodperc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation>5 perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation>15 perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation>30 perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation>1 óra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation>újraindításig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation>örökre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">Felugró ablak alapértelmezett művelete</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="665"/>
+ <source>Action</source>
+ <translation>Művelet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation>Alapértelmezett cél</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation><html><head/><body><p>Ha be van jelölve, az előugró ablakok aktív haladó nézettel jelennek meg.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="905"/>
+ <source>deny</source>
+ <translation>megtagadás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="914"/>
+ <source>allow</source>
+ <translation>engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation>futtatható fájl szerint</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation>parancssor szerint</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation>rendeltetési kikötő szerint</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation>rendeltetési hely IP címe szerint</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation>felhasználói azonosító szerint</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation>középre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation>jobb felső</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation>jobb alsó</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation>bal felső</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation>bal alsó</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation>Előugró ablak alapértelmezett időtartama</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation>Időtartam</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation><html><head/><body><p>Alapértelmezés szerint, amikor egy új előugró ablak jelenik meg, a legegyszerűbb formájában képes lesz a kapcsolatok vagy alkalmazások szűrésére a kapcsolat egy tulajdonságával (futtatható fájl, kikötő, IP stb.).</p><p>Ezekkel az opciókkal több mezőt is választhat, amelyekhez a kapcsolatokat szűrni kívánja.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation>Szűrje a csatlakozásokat az alábbiak szerint is:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation>Felhasználói azonosító</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation>Célkikötő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation>Cél IP-címe</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">Tiltsa le a előugró elemet, csak riasztást jelenítsen meg</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation><html><head/><body><p>Ez az időkorlát az a visszaszámlálás, amelyet akkor láthat, amikor egy felbukkanó párbeszédpanel jelenik meg.</p><p>Ha nem válaszol a felbukkanó párbeszédpanelre, akkor az alapértelmezett beállítások kerülnek alkalmazásra.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation>Alapértelmezett időtúllépés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="711"/>
+ <source>Nodes</source>
+ <translation>Csomópontok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="717"/>
+ <source>Process monitor method</source>
+ <translation>Folyamatfigyelés módszer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>Naplófájl a naplók írásához.<br/></p><p>A /dev/stdout naplókat nyomtat a normál kimenetre.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="737"/>
+ <source>Log file</source>
+ <translation>Naplófájl</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="751"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Az alapértelmezett időtartam akkor kerül végrehajtásra, ha nincs csatlakoztatva felhasználói felület.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="754"/>
+ <source>Default duration</source>
+ <translation>Alapértelmezett időtartam</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="767"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>Beállítások alkalmazása az összes csomópontra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Az alapértelmezett művelet akkor kerül végrehajtásra, ha nincs csatlakoztatva felhasználói felület.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">Alapértelmezett művelet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="809"/>
+ <source>HostName</source>
+ <translation>Állomásnév</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="671"/>
+ <source>proc</source>
+ <translation type="obsolete">proc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="676"/>
+ <source>ebpf</source>
+ <translation type="obsolete">ebpf</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>audit</source>
+ <translation type="obsolete">audit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="686"/>
+ <source>ftrace</source>
+ <translation type="obsolete">ftrace</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="868"/>
+ <source>until restart</source>
+ <translation>újraindításáig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="873"/>
+ <source>always</source>
+ <translation>mindig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>A csomópont címe.</p><p>Alapértelmezett: unix:///tmp/osui.sock (Az „unix://” kötelező, ha Unix szoftvercsatorna)</p><p>Lehet IP-cím is a kikötővel: 127.0.0.1:50051</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="884"/>
+ <source>Address</source>
+ <translation>Cím</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Ha be van jelölve, az OpenSnitch több okból is meg fogja engedni vagy megtagadja azokat a kapcsolatokat, amelyek nem rendelkeznek társított folyamatazonosítóval.</p><p>A felbukkanó párbeszédpanel csak a hálózati kapcsolatról tartalmaz információkat.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">Ismeretlen kapcsolatok elfogása</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>Version</source>
+ <translation>Változat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="778"/>
+ <source>DEBUG</source>
+ <translation type="obsolete">HIBAKERESÉS</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="783"/>
+ <source>INFO</source>
+ <translation type="obsolete">TÁJÉKOTTATÁS</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="788"/>
+ <source>IMPORTANT</source>
+ <translation type="obsolete">FONTOS</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>WARNING</source>
+ <translation type="obsolete">FIGYELMEZTETÉS</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="798"/>
+ <source>ERROR</source>
+ <translation type="obsolete">HIBA</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="803"/>
+ <source>FATAL</source>
+ <translation type="obsolete">VÉGZETES</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="983"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation>unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="995"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation>/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1000"/>
+ <source>/dev/stdout</source>
+ <translation>/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1024"/>
+ <source>Default log level</source>
+ <translation>Alapértelmezett naplószint</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1039"/>
+ <source>Database</source>
+ <translation>Adatbázis</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1093"/>
+ <source>Database type</source>
+ <translation>Adatbázistípus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1100"/>
+ <source>Select</source>
+ <translation>Kijelölés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1074"/>
+ <source>In memory</source>
+ <translation>Memóriabeli</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1079"/>
+ <source>File</source>
+ <translation>Fájl</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1345"/>
+ <source>Close</source>
+ <translation>Bezárás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1356"/>
+ <source>Apply</source>
+ <translation>Alkalmazás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1367"/>
+ <source>Save</source>
+ <translation>Mentés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation>A haladó nézet lehetővé teszi több mező egyszerű kiválasztását a kapcsolatok szűréséhez</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation>Ha be van jelölve, akkor ez a mező lesz kiválasztva, amikor egy előugró jelenik meg</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation><html><head/><body><p>Előugró ablak alapértelmezett művelete.</p><p>Amikor új kimenő kapcsolat jön létre, ez a művelet alapértelmezés szerint ki lesz választva, tehát ha az időtúllépés aktiválódik, akkor ez az opció lesz alkalmazva.</p><p><br/></p><p>Miközben egy előugró ablak kéri a felhasználót, hogy engedélyezze vagy tagadja meg a kapcsolatot:</p><p>1. megtagadják az új kimenő kapcsolatokat.</p><p>2. az ismert kapcsolatok a felhasználó által meghatározott szabályok alapján engedélyezhetők vagy megtagadhatók.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>Alapértelmezett művelet a grafikus felhasználói felület leválasztása esetén</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="894"/>
+ <source>Debug invalid connections</source>
+ <translation>Érvénytelen kapcsolatok hibakeresése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation>Előugró ablakok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation>Alapértelmezett beállítások</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation>Alapértelmezett hely a képernyőn</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="479"/>
+ <source>any temporary rules</source>
+ <translation>bármilyen ideiglenes szabályt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="492"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation><html><head/><body><p>Ha ezt az opciót választja, a kiválasztott időtartam szabályai nem kerülnek hozzáadásra a grafikus felhasználói felület ideiglenes szabályainak listájához.</p><p><br/></p><p>Az ideiglenes szabályok továbbra is érvényesek, és használhatja őket, amikor a rendszer kéri az új kapcsolat engedélyezését/elutasítását.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="495"/>
+ <source>Don't save rules of duration</source>
+ <translation>Ne mentse az időtartam szabályait</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="601"/>
+ <source>Time</source>
+ <translation>Idő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>Destination</source>
+ <translation>Cél</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="649"/>
+ <source>Protocol</source>
+ <translation>Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="697"/>
+ <source>Process</source>
+ <translation>Folyamat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="617"/>
+ <source>Rule</source>
+ <translation>Szabály</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="633"/>
+ <source>Node</source>
+ <translation>Csomópont</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="723"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Ha be van jelölve, az OpenSnitch felszólítja Önt, hogy engedélyezze vagy utasítsa el azokat a kapcsolatokat, amelyek nem rendelkeznek aszocizált folyamatazonosítóval, több okból, főleg a rossz állapotú kapcsolatok miatt.</p><p>Az előugró párbeszédablak csak a hálózati kapcsolatra vonatkozó adatokat tartalmazza.</p><p>Vannak azonban olyan esetek, amikor ezek érvényes kapcsolatok, például amikor virtuális magánhálózatot hoznak létre a WireGuard használatával.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="589"/>
+ <source>Events tab columns</source>
+ <translation>Események lap oszlopai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation>folyamatazonosító által</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation>Előugró ablakok letiltása, csak értesítések megjelenítése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="508"/>
+ <source>Desktop notifications</source>
+ <translation>Asztali értesítések</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="526"/>
+ <source>Use system notifications</source>
+ <translation>Rendszerértesítések használata</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="542"/>
+ <source>Use Qt notifications</source>
+ <translation>Qt-értesítések használata</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="571"/>
+ <source>Test</source>
+ <translation>Tesztelés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation><html><head/><body><p>Ha be van jelölve, az OpenSnitch felkéri, hogy engedélyezze vagy tiltsa le azokat a kapcsolatokat, amelyekhez nem tartozik folyamatazonosító, több okból is, főleg a rossz állapotú kapcsolatok miatt.</p><p>A felugró párbeszédpanel csak a hálózati kapcsolatra vonatkozó információkat tartalmaz.</p><p>Vannak olyan esetek, amikor ezek érvényes kapcsolatok, például virtuális magánhálózat WireGuard segítségével történő létesítésekor.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1178"/>
+ <source>minutes</source>
+ <translation>perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1204"/>
+ <source>Minutes between events purges</source>
+ <translation>Eseménytisztítások közötti percek száma</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1227"/>
+ <source>days</source>
+ <translation>nap</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1237"/>
+ <source>Maximum days of events to keep</source>
+ <translation>Események megtartására nyitva álló napok száma</translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation>Folyamat részletei</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation>betöltés…</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation>CWD: betöltés…</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation>memória statisztika: betöltés…</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation>Állapot</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation>Fájlok megnyitása</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation>I/O statisztika</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation>Memóriába ágyazott fájlok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation>Verem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation>Környezeti változók</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation>Alkalmazás folyamatazonosítók</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation>Folyamatfigyelés elindítsa vagy leállítása</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation>Bezárás</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="14"/>
+ <source>Rule</source>
+ <translation>Szabály</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/>
+ <source>Node</source>
+ <translation>Csomópont</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/>
+ <source>Apply rule to all nodes</source>
+ <translation>Alkalmazzon szabályt minden csomópontra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/>
+ <source>To this IP / Network</source>
+ <translation>Erre az IP-címre vagy a hálózatra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/>
+ <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source>
+ <translation type="obsolete">/útvonal/a/futtathatóhoz, .*/bináris/végrehajtható[0-9\.]+$, …</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/>
+ <source>Action</source>
+ <translation>Művelet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/>
+ <source>To this port</source>
+ <translation>Erre a kikötőre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/>
+ <source>To this list of domains</source>
+ <translation>Ehhez a tartománylistához</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation>Megadhat egyetlen IP-címet:
+- 192.168.1.1
+
+vagy reguláris kifejezés:
+- 192\.168\.1\.[0-9]+
+
+több IP-cím:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+Megadhat alhálózatot is:
+- 192.168.1.0/24
+
+Megjegyzés: Vesszőkkel vagy szóközökkel nem szabad elválasztani az IP-címeket vagy a hálózatokat.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/>
+ <source>LAN</source>
+ <translation>Helyi hálózat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/>
+ <source>127.0.0.0/8</source>
+ <translation>127.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/>
+ <source>192.168.0.0/24</source>
+ <translation>192.168.0.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/>
+ <source>192.168.1.0/24</source>
+ <translation>192.168.1.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/>
+ <source>192.168.2.0/24</source>
+ <translation>192.168.2.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/>
+ <source>192.168.0.0/16</source>
+ <translation>192.168.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/>
+ <source>169.254.0.0/16</source>
+ <translation>169.254.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/>
+ <source>172.16.0.0/12</source>
+ <translation>172.16.0.0/12</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/>
+ <source>10.0.0.0/8</source>
+ <translation>10.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/>
+ <source>::1/128</source>
+ <translation>::1/128</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/>
+ <source>fc00::/7</source>
+ <translation>fc00::/7</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/>
+ <source>ff00::/8</source>
+ <translation>ff00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/>
+ <source>fe80::/10</source>
+ <translation>fe80::/10</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/>
+ <source>fd00::/8</source>
+ <translation>fd00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation><html><head/><body><p>Több kikötők megadhatók reguláris kifejezések használatával:</p><p><br/></p><p>- 53, 80 vagy 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 vagy 5551, 5552, 5553, stb:</p><p>^(53|443|555[0-9])$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/>
+ <source>once</source>
+ <translation>egyszer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/>
+ <source>30s</source>
+ <translation>30 másodperc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/>
+ <source>5m</source>
+ <translation>5 perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/>
+ <source>15m</source>
+ <translation>15 perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/>
+ <source>30m</source>
+ <translation>30 perc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/>
+ <source>1h</source>
+ <translation>1 óra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/>
+ <source>until reboot</source>
+ <translation>újraindításig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/>
+ <source>always</source>
+ <translation>mindig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation>Vesszők vagy szóközök nem engedélyezhetnek több tartomány megadását.
+
+Használjon helyette reguláris kifejezéseket:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+vagy egyetlen tartomány:
+www.gnu.org - csak a www.gnu.org, nem az ftp.gnu.org, a www2.gnu.org, …
+gnu.org - csak a gnu.org-nak fog megfelelni, nem a www.gnu.org-nak, nem az ftp.gnu.org-nak, …</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="430"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation>www.tartomány.hu, .*\.tartomány.hu</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/>
+ <source>To this host</source>
+ <translation>Erre az állomásra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/>
+ <source>Duration</source>
+ <translation>Időtartam</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation><html><head/><body><p>Csak TCP, UDP vagy UDPLITE engedélyezett</p><p>Használhatja a reguláris kifejezést, azaz: ^(TCP|UDP)$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/>
+ <source>TCP</source>
+ <translation>TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/>
+ <source>UDP</source>
+ <translation>UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/>
+ <source>UDPLITE</source>
+ <translation>UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>TCP6</source>
+ <translation>TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/>
+ <source>UDP6</source>
+ <translation>UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/>
+ <source>UDPLITE6</source>
+ <translation>UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/>
+ <source>Protocol</source>
+ <translation>Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/>
+ <source>From this executable</source>
+ <translation>Ebből a futtatható fájlból</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/>
+ <source>Deny</source>
+ <translation>Megtagadás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/>
+ <source>Allow</source>
+ <translation>Engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/>
+ <source>From this command line</source>
+ <translation>Ebből a parancssorból</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/>
+ <source>From this user ID</source>
+ <translation>Ebből a felhasználói azonosítóból</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Válasszon egy címtárat a letiltáshoz vagy engedélyezéshez szükséges tartománylistákkal.</p><p>Helyezze be a címtár fájlokat bármilyen kiterjesztéssel, amely tartalmazza a tartományok listáit.</p><p><br/>A lista minden bejegyzésének formátuma a következő (állomás formátum): </p><p>127.0.0.1 www.tartomány.hu</p><p>vagy </p><p>0.0.0.0 www.tartomány.hu</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Name</source>
+ <translation>Név</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/>
+ <source>Enable</source>
+ <translation>Engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation>A szabályokat ábécé sorrendben ellenőrzik, így azok prioritása szerint ennek megfelelően nevezheti meg őket.
+
+000-helyi-állomás-engedélyezése
+001-közvetítés-tagadása
+…</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="713"/>
+ <source>leave blank to autocreate</source>
+ <translation>hagyja üresen az önműködő létrehozáshoz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation>Ha be van jelölve, akkor ez a szabály elsőbbséget élvez a többi szabálygal szemben. Ez után más szabályokat nem fogunk ellenőrizni.
+
+A szabályt úgy kell megneveznie, hogy először ellenőrizni fogják, mert betűrendben ellenőrzik. Például:
+
+[x] Elsőbbség - 000-elsőbbségi-szabály
+[ ] Elsőbbség - 001-kevésbé-elsőbbségi-szabály</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="54"/>
+ <source>Priority rule</source>
+ <translation>Elsőbbségi szabály</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation><html><head/><body><p>Alapértelmezés szerint a szabályok mezője nem különbözteti meg a kis- és nagybetűket, azaz ha egy folyamat megpróbálja elérni a gOOgle.CoM tartományt, és van egy szabályod, amelyet meg kell tagadni a .*google.com tartomány, a kapcsolat letiltva lesz.<br/></p><p>Ha bejelöli ezt a jelölőnégyzetet, meg kell adnia azt a pontos karakterláncot (tartomány, futtatható fájl, parancssor), amelyet szűrni szeretne.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="77"/>
+ <source>Case-sensitive</source>
+ <translation>Kis- és nagybetűk felismerése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/>
+ <source>Applications</source>
+ <translation>Alkalmazások</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/>
+ <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source>
+ <translation><html><head/><body><p>Ez a mező csak a végrehajtható elérési úttal fog megegyezni. A felhasználó nem módosíthatja.<br/></p><p>Szabványos kifejezésekkel tilthatja meg a /tmp fájl végrehajtását, például:<br/></p><p>^/tmp/.*$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation><html><head/><body><p>Ez a mező tartalmazza a felhasználó által végrehajtott parancssort, és megegyezik vele.<br/></p><p>Ha a felhasználó beírta a parancsot, csak a parancsot megjelenik:</p><p>telnet 1.2.3.4<br/></p><p>Ha a felhasználó beírta a parancs abszolút vagy relatív elérési útját, akkor ez fog megjelenni:</p><p> >/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="246"/>
+ <source>From this PID</source>
+ <translation>Ebből a folyamatazonosítóból</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/>
+ <source>Network</source>
+ <translation>Hálózat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/>
+ <source>List of domains/IPs</source>
+ <translation>Tartományok/IP-címek listája</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>To this list of network ranges</source>
+ <translation>Ehhez a hálózati tartományok listájához</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/>
+ <source>To this list of IPs</source>
+ <translation>Ehhez az IP-címlistához</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezendő IP-címek listáját tartalmazó fájlokkal:</p><p>1.2.3.4.5</p><p>1.2.3.4. 6</p><p>.</p><p>stb.</p><p>Soronként egy IP-cím. Az üres sorokat vagy a # karakterrel kezdődő sorokat figyelmen kívül hagyja.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="608"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezni kívánt hálózati tartományok listáját tartalmazó fájlokkal:</p><p>1.2.3.0/24</p><p>80.34.56.0 /20</p><p>.</p><p>stb.<br/></p><p>Egy hálózati tartomány soronként. Az üres sorokat vagy a # karakterrel kezdődő sorokat figyelmen kívül hagyja.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="636"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezni kívánt tartományok listájával.</p><p>A könyvtárba helyezzen be olyan fájlokat, amelyek a tartomány listáit tartalmazzák.</p><p><br/>A lista minden egyes bejegyzésének formátuma a következő (gazdaformátum):</p><p>127.0.0.1 www.tartomány.com</p><p>vagy </p><p>0.0.0.0 www.tartomány.com</p><p>Az üres sorokat vagy a # karakterrel kezdődő sorokat figyelmen kívül hagyja.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="651"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation>Ehhez a tartománylistához
+(szabályos kifejezések)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Válasszon ki egy könyvtárat a tiltandó vagy engedélyezendő tartományok szabványos kifejezéseit tartalmazó fájlokkal:</p><p>.*\.example\.com</p><p> Használhat olyan tartományt is, amilyen: &quot;example.com&quot;, és egyezik a bármi.példa.com, bármi.példa.com.helyitartomány stb. oldallal.</p><p>Soronként egy tartomány. Az üres sorokat vagy a # karakterrel kezdődő sorokat figyelmen kívül hagyja.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/>
+ <source>Reject</source>
+ <translation>Elutasítás</translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>OpenSnitch hálózati statisztika</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="290"/>
+ <source>Save to CSV.</source>
+ <translation>Mentés CSV-fájlként…</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="300"/>
+ <source>Ctrl+S</source>
+ <translation>Ctrl+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="351"/>
+ <source>Create a new rule</source>
+ <translation>Új szabály létrehozása</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="381"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">állomásnév - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="420"/>
+ <source>Status</source>
+ <translation>Állapot</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1665"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="464"/>
+ <source>Start or Stop interception</source>
+ <translation>Adatelérés indítása vagy leállítása</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="509"/>
+ <source>Events</source>
+ <translation>Események</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation>Szűrő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation>Engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation>Megtagadás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation>Például: Firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation>Az összes elfogott esemény törlése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="748"/>
+ <source>Nodes</source>
+ <translation>Csomópontok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(kattintson duplán a Cím oszlopra a csomópont részleteinek megtekintéséhez)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1569"/>
+ <source>Rules</source>
+ <translation>Szabályok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="857"/>
+ <source>enable</source>
+ <translation>engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="864"/>
+ <source>Edit rule</source>
+ <translation>Szabály szerkesztése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="878"/>
+ <source>Delete rule</source>
+ <translation>Szabály törlése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(kattintson duplán egy sorra a szabály részleteinek megtekintéséhez)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation type="obsolete">szabálynév keresése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="704"/>
+ <source>Application rules</source>
+ <translation>Alkalmazási szabályok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="806"/>
+ <source>Permanent</source>
+ <translation>Állandó</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="815"/>
+ <source>Temporary</source>
+ <translation>Ideiglenes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="933"/>
+ <source>Hosts</source>
+ <translation>Gazdagépek</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(kattintson duplán az elem részleteinek megtekintéséhez)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation type="obsolete">Az összes elfogott gazdagép törlése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1020"/>
+ <source>Applications</source>
+ <translation>Alkalmazások</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation type="obsolete">Az összes elfogott alkalmazás törlése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1127"/>
+ <source>Addresses</source>
+ <translation>Címek</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation type="obsolete">Az összes elfogott cím törlése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1214"/>
+ <source>Ports</source>
+ <translation>Kikötők</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation type="obsolete">Az összes elfogott kikötő törlése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1298"/>
+ <source>Users</source>
+ <translation>Felhasználók</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation type="obsolete">Az összes elfogott felhasználó törlése</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1404"/>
+ <source>Connections</source>
+ <translation>Kapcsolatok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1459"/>
+ <source>Dropped</source>
+ <translation>Elvetve</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1514"/>
+ <source>Uptime</source>
+ <translation>Hasznos üzemidő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1639"/>
+ <source>Version</source>
+ <translation>Változat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="912"/>
+ <source>Delete connections that matched this rule</source>
+ <translation>Kapcsolat törlése amelyek megfelelnek ennek a szabálynak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="797"/>
+ <source>All applications</source>
+ <translation>Minden alkalmazás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation>Elutasítás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation>0</translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation>Statisztika</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation>Engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation>Letiltás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation>Súgó</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation>Bezárás</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation>A rendszerértesítések nem érhetők el, telepítenie kell a python3-notify2-t.</translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation>újraindításig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation>örökre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation>Engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation>Megtagadás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation>Kimenő kapcsolat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation>Folyamat innen indult:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation>ettől a végrehajtható fájlból</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation>erről a parancssorról</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation>kikötőig: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation>eddig: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation>a(z) {0} felhasználótól</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation>eddig: {0}.*</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation>eddig: *.{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation type="obsolete">eddig: *{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation>A(z) %s <b>távoli</b> folyamat fut a(z) <b>%s</b>-n</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation>csatlakozik <b>%s</b>-hoz a %s-kikötőn %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation>megpróbálja megoldani a(z) <b>%s</b> problémát a(z) %s segítségével, %s-kikötő %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation>ebből a folyamatazonosítóból</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="105"/>
+ <source>New outgoing connection</source>
+ <translation>Új kimenő kapcsolat</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/>
+ <source>Exception saving config: {0}</source>
+ <translation>Beállítás mentése kivétele: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>Warning</source>
+ <translation>Figyelmeztetés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation>Válasszon egy fájlt az adatbázishoz<br>vagy válassza a „Memóriabeli” típust.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>DB type changed</source>
+ <translation>DB típusa megváltozott</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation>Indítsa újra a grafikus felhasználói felületet, hogy a hatások életbe léphessenek</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation>Beállítások alkalmazása: {0}…</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/>
+ <source>Server address can not be empty</source>
+ <translation>Kiszolgáló címe nem lehet üres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/>
+ <source>Error loading {0} configuration</source>
+ <translation>Hiba történt a(z) {0}-beállítás betöltésekor</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/>
+ <source>Configuration applied.</source>
+ <translation>Beállítás alkalmazva.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/>
+ <source>Error applying configuration: {0}</source>
+ <translation>Hiba a beállítás alkalmazásakor: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation>A súgó megjelenítéséhez vigye az egeret a szövegek fölé<br><br>Emlékezzen meglátogatni a wikit: <a href="{0}">{0}</a></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation><b>Hiba a folyamatadatok betöltésekor:</b> <br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation><b>Hiba a megfigyelési folyamat leállításakor:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation>betöltés folyamatban…</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/>
+ <source>There're no nodes connected.</source>
+ <translation>Nincsenek csomópontok csatlakoztatva.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/>
+ <source>Rule applied.</source>
+ <translation>A szabály alkalmazva.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Hiba a szabály alkalmazásakor: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Hiba történt a szabály betöltésekor</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation>a protokoll nem lehet üres, és nem szüntetheti meg a kijelölést</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/>
+ <source>Protocol regexp error</source>
+ <translation>Protokoll reguláris kifejezéshibája</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/>
+ <source>process path can not be empty</source>
+ <translation>folyamat útvonal nem lehet üres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/>
+ <source>Process path regexp error</source>
+ <translation>Folyamat útvonala reguláris kifejezéshibája</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/>
+ <source>command line can not be empty</source>
+ <translation>parancssor nem lehet üres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/>
+ <source>Command line regexp error</source>
+ <translation>Parancssor reguláris kifejezéshibája</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/>
+ <source>Dest port can not be empty</source>
+ <translation>Célkikötő nem lehet üres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/>
+ <source>Dst port regexp error</source>
+ <translation>Célkikötő szabványos kifejezéshibája</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/>
+ <source>Dest host can not be empty</source>
+ <translation>Célkikötő nem lehet üres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/>
+ <source>Dst host regexp error</source>
+ <translation>Célkikötő reguláris kifejezéshibája</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>Cél IP-cím/hálózat nem lehet üres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/>
+ <source>Dst IP regexp error</source>
+ <translation>Cél IP-cím reguláris kifejezéshibája</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/>
+ <source>User ID can not be empty</source>
+ <translation>Felhasználói azonosító nem lehet üres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/>
+ <source>User ID regexp error</source>
+ <translation>Felhasználói azonosító reguláris kifejezéshibája</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/>
+ <source>Lists field cannot be empty</source>
+ <translation>Listamező nem lehet üres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/>
+ <source>Lists field must be a directory</source>
+ <translation>Listmezők könyvtárnak kell lennie</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>A szabály nem támogatott</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/>
+ <source>There's already a rule with this name.</source>
+ <translation>Már van egy szabály ezzel a névvel.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/>
+ <source>PID field can not be empty</source>
+ <translation>Folyamatazonosító mező nem lehet üres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/>
+ <source>PID field regexp error</source>
+ <translation>Folyamatazonosító mező szabványos kifejezési hiba</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/>
+ <source>Select at least one field.</source>
+ <translation>Jelöljön ki legalább egy mezőt.</translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">Név</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">Cím</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="176"/>
+ <source>Status</source>
+ <translation type="obsolete">Állapot</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="177"/>
+ <source>Hostname</source>
+ <translation type="obsolete">Állomásnév</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="183"/>
+ <source>Version</source>
+ <translation type="obsolete">Változat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="180"/>
+ <source>Rules</source>
+ <translation type="obsolete">Szabályok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">Idő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">Művelet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">Időtartam</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <translation type="obsolete">Csomópont</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">Engedélyezve</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="253"/>
+ <source>Hits</source>
+ <translation>Találatok száma</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation>Nem fut</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation>Letiltva</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation>Futtatás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="568"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation>{0} OpenSnitch hálózati statisztika</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="570"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation>OpenSnitch hálózati statisztikák a következőhöz: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="921"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation><b>Hiba:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="928"/>
+ <source>Warning:</source>
+ <translation>Figyelmeztetés:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="717"/>
+ <source>Allow</source>
+ <translation>Engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="718"/>
+ <source>Deny</source>
+ <translation>Megtagadás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="722"/>
+ <source>Always</source>
+ <translation>Mindig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="723"/>
+ <source>Until reboot</source>
+ <translation>Újraindításig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="731"/>
+ <source>Disable</source>
+ <translation>Letiltás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="733"/>
+ <source>Enable</source>
+ <translation>Engedélyezés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Duplicate</source>
+ <translation>Másolás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="737"/>
+ <source>Edit</source>
+ <translation>Szerkesztés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="738"/>
+ <source>Delete</source>
+ <translation>Törlés</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="761"/>
+ <source> Your are about to delete this rule. </source>
+ <translation>Törölni készül ezt a szabályt.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> Are you sure?</source>
+ <translation>Biztos benne?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="891"/>
+ <source>Rule not found by that name and node</source>
+ <translation>A szabály nem található ezen a néven és csomóponton</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> You are about to delete this rule. </source>
+ <translation>Törölni készül ezt a szabályt.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/>
+ <source>Save as CSV</source>
+ <translation>Mentés CSV formátumban…</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <translation type="obsolete">Szabály</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">Név</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Név</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Cím</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Állapot</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Állomásnév</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Változat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Szabályok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Idő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Művelet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Időtartam</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Csomópont</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Engedélyezve</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Találatok száma</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Szabály</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="392"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Név</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Cím</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="379"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Állapot</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="380"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Állomásnév</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Változat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Szabályok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="390"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Idő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="395"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Művelet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="396"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Időtartam</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="391"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Csomópont</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="393"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Engedélyezve</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Találatok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Protokoll</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Folyamat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Cél</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Szabály</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Felhasználóazonosító</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="377"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>LegutóbbiCsatlakozás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Argumentumok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>CélIPcíme</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Célállomásneve</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Célkikötő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="174"/>
+ <source>LastConnection</source>
+ <translation type="obsolete">LegutóbbiCsatlakozás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="179"/>
+ <source>Uptime</source>
+ <translation type="obsolete">Hasznos üzemidő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="181"/>
+ <source>Connections</source>
+ <translation type="obsolete">Kapcsolatok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="182"/>
+ <source>Dropped</source>
+ <translation type="obsolete">Elvetve</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="252"/>
+ <source>What</source>
+ <translation>Mi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="709"/>
+ <source>Apply to</source>
+ <translation>Alkalmazás a következőre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="719"/>
+ <source>Reject</source>
+ <translation>Elutasítás</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation>Hálózatnév</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="378"/>
+ <source>Addr</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Cím</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="382"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Hasznos üzemidő</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Kapcsolatok</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Elvetve</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Mi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="394"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Sorrend</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="644"/>
+ <source>New node connected</source>
+ <translation>Új csomópont csatlakoztatva</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="2.0" language="ja_JP" sourcelanguage="">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation type="unfinished">ユーザーID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p><span style=" font-weight:600;">実行元</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation type="unfinished">送信元のIP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation type="unfinished">プロセスID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation type="unfinished">宛先IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation type="unfinished">宛先ポート</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation type="unfinished">この実行ファイルを</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation type="unfinished">このコマンドラインを</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation type="unfinished">この宛先ポートに対して</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation type="unfinished">このユーザーを</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation type="unfinished">この宛先IPに対して</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation type="unfinished">一度のみ</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation type="unfinished">30秒間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation type="unfinished">5分間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation type="unfinished">15分間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation type="unfinished">30分間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation type="unfinished">1時間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation type="unfinished">再起動するまで</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation type="unfinished">永久に</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation type="unfinished">拒否する</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation type="unfinished">許可する</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>New node connected</name>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation type="unfinished">設定</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation type="unfinished">UI</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation type="unfinished">ダイアログの表示時間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation type="unfinished">ポップアップ時に選択される規定のルールの有効期間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="754"/>
+ <source>Default duration</source>
+ <translation type="unfinished">既定のルール有効期間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">ポップアップ時に選択される規定のアクション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">既定のアクション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation type="unfinished">既定のターゲット</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation type="unfinished">中央</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation type="unfinished">右上部</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation type="unfinished">右下部</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation type="unfinished">左上部</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation type="unfinished">左下部</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="167"/>
+ <source>Prompt dialog default position on screen</source>
+ <translation type="obsolete">既定のダイアログ表示位置</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation type="unfinished">実行ファイル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation type="unfinished">コマンドライン</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation type="unfinished">宛先ポート</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation type="unfinished">宛先IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation type="unfinished">ユーザーID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="863"/>
+ <source>once</source>
+ <translation type="unfinished">一度のみ</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation type="unfinished">30秒間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation type="unfinished">5分間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation type="unfinished">15分間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation type="unfinished">30分間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation type="unfinished">1時間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation type="unfinished">再起動するまで</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation type="unfinished">永久に</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="905"/>
+ <source>deny</source>
+ <translation type="unfinished">拒否</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="914"/>
+ <source>allow</source>
+ <translation type="unfinished">許可</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">ポップアップを無効にして通知のみ表示</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="711"/>
+ <source>Nodes</source>
+ <translation type="unfinished">ノード</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="717"/>
+ <source>Process monitor method</source>
+ <translation type="unfinished">プロセス監視方式</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="751"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p>規定のルール有効期間は、UIが接続されていないときに使用されます。</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p>ノードのアドレス</p><p>標準: unix:///tmp/osui.sock (Unixソケットの場合はunix://が必須)</p><p>このようにIPアドレスとポートを指定することもできます。127.0.0.1:50051</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="884"/>
+ <source>Address</source>
+ <translation type="unfinished">アドレス</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1024"/>
+ <source>Default log level</source>
+ <translation type="unfinished">既定のログレベル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>Version</source>
+ <translation type="unfinished">バージョン</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p>規定のアクションは、UIが接続されていないときに使用されます。</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="671"/>
+ <source>proc</source>
+ <translation type="obsolete">proc</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>audit</source>
+ <translation type="obsolete">audit</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="686"/>
+ <source>ftrace</source>
+ <translation type="obsolete">ftrace</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p>ファイルにログを記録します<br/></p><p>/dev/stdoutにすると標準出力にログを出力します</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="737"/>
+ <source>Log file</source>
+ <translation type="unfinished">ログファイル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="778"/>
+ <source>DEBUG</source>
+ <translation type="obsolete">DEBUG</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="783"/>
+ <source>INFO</source>
+ <translation type="obsolete">INFO</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="788"/>
+ <source>IMPORTANT</source>
+ <translation type="obsolete">IMPORTANT</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>WARNING</source>
+ <translation type="obsolete">WARNING</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="798"/>
+ <source>ERROR</source>
+ <translation type="obsolete">ERROR</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="803"/>
+ <source>FATAL</source>
+ <translation type="obsolete">FATAL</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>有効にした場合、opensnitchは、関連したPIDを持たない接続を許可するか拒否するかを確認します。</p><p>ポップアップダイアログには、ネットワーク接続に関する情報のみが表示されます。</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">不明なプロセスを検証</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="809"/>
+ <source>HostName</source>
+ <translation type="unfinished">ホスト名</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="983"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation type="unfinished">unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="868"/>
+ <source>until restart</source>
+ <translation type="unfinished">再起動するまで</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="873"/>
+ <source>always</source>
+ <translation type="unfinished">常に</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="995"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation type="unfinished">/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1000"/>
+ <source>/dev/stdout</source>
+ <translation type="unfinished">/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="767"/>
+ <source>Apply configuration to all nodes</source>
+ <translation type="unfinished">全てのノードに設定を反映</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1039"/>
+ <source>Database</source>
+ <translation type="unfinished">データベース</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1093"/>
+ <source>Database type</source>
+ <translation type="unfinished">データベース方式</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1100"/>
+ <source>Select</source>
+ <translation type="unfinished">参照</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1074"/>
+ <source>In memory</source>
+ <translation type="unfinished">メモリ内</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1079"/>
+ <source>File</source>
+ <translation type="unfinished">ファイル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1345"/>
+ <source>Close</source>
+ <translation type="unfinished">閉じる</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1356"/>
+ <source>Apply</source>
+ <translation type="unfinished">適用</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1367"/>
+ <source>Save</source>
+ <translation type="unfinished">保存</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Pop-ups default options</source>
+ <translation type="obsolete">ポップアップの規定のアクション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="367"/>
+ <source>Pop-ups default position on screen</source>
+ <translation type="obsolete">規定のポップアップ表示位置</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation type="unfinished">詳細表示では、接続をフィルタリングするために複数のフィールドを簡単に選択できます</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation type="unfinished">標準で詳細表示を有効にする</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="665"/>
+ <source>Action</source>
+ <translation type="unfinished">アクション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p>有効にすると、詳細表示がアクティブな状態でポップアップが表示されます。</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation type="unfinished">ルールの有効期間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p>ポップアップの表示時、標準では接続の1つのプロパティ (実行可能ファイル、ポート、IP など) によって接続またはアプリケーションをフィルタリングできます。</p><p>これらのオプションを使用すると、接続をフィルタリングする際、複数のフィールドを選択できます。</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation>次を使用して通信をフィルタ</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation type="unfinished">有効にすると、ポップアップが表示されたときに、このフィールドが選択されます</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation type="unfinished">ユーザーID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation type="unfinished">宛先ポート</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation type="unfinished">宛先IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p>このタイムアウトは、ポップアップダイアログの表示時間のカウントダウンです。</p><p>ポップアップに回答しない場合、このオプションが適用されます。</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="676"/>
+ <source>ebpf</source>
+ <translation type="obsolete">ebpf</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p>ポップアップの規定のアクション</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation type="unfinished">GUI未接続時の規定のアクション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="894"/>
+ <source>Debug invalid connections</source>
+ <translation type="unfinished">無効な接続をデバッグ</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation type="unfinished">ポップアップ</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation type="unfinished">規定のオプション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation type="unfinished">規定の表示位置</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="479"/>
+ <source>any temporary rules</source>
+ <translation type="unfinished">全ての一時ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="492"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="495"/>
+ <source>Don't save rules of duration</source>
+ <translation type="unfinished">ルールの有効期間を保持しない</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="601"/>
+ <source>Time</source>
+ <translation type="unfinished">時間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>Destination</source>
+ <translation type="unfinished">宛先</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="649"/>
+ <source>Protocol</source>
+ <translation type="unfinished">プロトコル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="697"/>
+ <source>Process</source>
+ <translation type="unfinished">プロセス</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="617"/>
+ <source>Rule</source>
+ <translation type="unfinished">ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="633"/>
+ <source>Node</source>
+ <translation type="unfinished">ノード</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="589"/>
+ <source>Events tab columns</source>
+ <translation type="unfinished">イベントタブの項目</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="508"/>
+ <source>Desktop notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="526"/>
+ <source>Use system notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="542"/>
+ <source>Use Qt notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="571"/>
+ <source>Test</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1178"/>
+ <source>minutes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1204"/>
+ <source>Minutes between events purges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1227"/>
+ <source>days</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1237"/>
+ <source>Maximum days of events to keep</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation type="unfinished">プロセス情報</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation type="unfinished">読み込み中...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation type="unfinished">CWD:-読み込み中...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation type="unfinished">メモリ状態: 読み込み中...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation type="unfinished">状態</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation type="unfinished">ファイルアクセス</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation type="unfinished">入出力の統計</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation type="unfinished">メモリ内データ</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation type="unfinished">スタック</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation type="unfinished">環境変数</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation type="unfinished">プロセスID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation type="unfinished">プロセスの監視を開始/停止</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation type="unfinished">閉じる</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="14"/>
+ <source>Rule</source>
+ <translation type="unfinished">ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/>
+ <source>Node</source>
+ <translation type="unfinished">ノード</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/>
+ <source>Apply rule to all nodes</source>
+ <translation type="unfinished">全てのノードにルールを反映</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/>
+ <source>To this IP / Network</source>
+ <translation type="unfinished">IP/ネットワーク</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/>
+ <source>Action</source>
+ <translation type="unfinished">アクション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/>
+ <source>To this port</source>
+ <translation type="unfinished">ポート</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/>
+ <source>To this list of domains</source>
+ <translation type="unfinished">ドメインリスト</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/>
+ <source>LAN</source>
+ <translation type="unfinished">LAN</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/>
+ <source>127.0.0.0/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/>
+ <source>192.168.0.0/24</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/>
+ <source>192.168.1.0/24</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/>
+ <source>192.168.2.0/24</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/>
+ <source>192.168.0.0/16</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/>
+ <source>169.254.0.0/16</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/>
+ <source>172.16.0.0/12</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/>
+ <source>10.0.0.0/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/>
+ <source>::1/128</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/>
+ <source>fc00::/7</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/>
+ <source>ff00::/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/>
+ <source>fe80::/10</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/>
+ <source>fd00::/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/>
+ <source>once</source>
+ <translation type="unfinished">一度のみ</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/>
+ <source>30s</source>
+ <translation type="unfinished">30秒間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/>
+ <source>5m</source>
+ <translation type="unfinished">5分間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/>
+ <source>15m</source>
+ <translation type="unfinished">15分間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/>
+ <source>30m</source>
+ <translation type="unfinished">30分間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/>
+ <source>1h</source>
+ <translation type="unfinished">1時間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/>
+ <source>until reboot</source>
+ <translation type="unfinished">再起動するまで</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/>
+ <source>always</source>
+ <translation type="unfinished">常に</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="430"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/>
+ <source>To this host</source>
+ <translation type="unfinished">ホスト</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/>
+ <source>Duration</source>
+ <translation type="unfinished">有効期間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/>
+ <source>TCP</source>
+ <translation type="unfinished">TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/>
+ <source>UDP</source>
+ <translation type="unfinished">UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/>
+ <source>UDPLITE</source>
+ <translation type="unfinished">UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>TCP6</source>
+ <translation type="unfinished">TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/>
+ <source>UDP6</source>
+ <translation type="unfinished">UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/>
+ <source>UDPLITE6</source>
+ <translation type="unfinished">UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/>
+ <source>Protocol</source>
+ <translation type="unfinished">プロトコル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/>
+ <source>From this executable</source>
+ <translation type="unfinished">実行ファイル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/>
+ <source>Deny</source>
+ <translation type="unfinished">拒否</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/>
+ <source>Allow</source>
+ <translation type="unfinished">許可</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/>
+ <source>From this command line</source>
+ <translation type="unfinished">コマンドライン</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/>
+ <source>From this user ID</source>
+ <translation type="unfinished">ユーザーID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Name</source>
+ <translation type="unfinished">名前</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/>
+ <source>Enable</source>
+ <translation type="unfinished">有効</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="713"/>
+ <source>leave blank to autocreate</source>
+ <translation type="unfinished">空にすると自動生成されます</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="54"/>
+ <source>Priority rule</source>
+ <translation type="unfinished">優先ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="77"/>
+ <source>Case-sensitive</source>
+ <translation type="unfinished">大文字/小文字を区別</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/>
+ <source>Applications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/>
+ <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="246"/>
+ <source>From this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/>
+ <source>Network</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/>
+ <source>List of domains/IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>To this list of network ranges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/>
+ <source>To this list of IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="608"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="636"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="651"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="unfinished">OpenSnitchネットワークモニター</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="290"/>
+ <source>Save to CSV.</source>
+ <translation>CSVファイルに保存</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="300"/>
+ <source>Ctrl+S</source>
+ <translation type="unfinished">Ctrl+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="351"/>
+ <source>Create a new rule</source>
+ <translation type="unfinished">ルールを新規作成</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="381"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation type="unfinished"><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">ホスト名 - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="420"/>
+ <source>Status</source>
+ <translation type="unfinished">状態</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1665"/>
+ <source>-</source>
+ <translation type="unfinished">-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="464"/>
+ <source>Start or Stop interception</source>
+ <translation type="unfinished">サービスを開始/停止</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="509"/>
+ <source>Events</source>
+ <translation type="unfinished">イベント</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation type="unfinished">絞り込み</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation type="unfinished">許可中</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation type="unfinished">拒否中</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation type="unfinished">例:firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation type="unfinished">50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation type="unfinished">100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation type="unfinished">200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation type="unfinished">300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation type="unfinished">記録した全てのイベント履歴を消去</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="748"/>
+ <source>Nodes</source>
+ <translation type="unfinished">ノード</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックでノードの詳細を確認できます)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1569"/>
+ <source>Rules</source>
+ <translation type="unfinished">ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="857"/>
+ <source>enable</source>
+ <translation type="unfinished">有効</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="864"/>
+ <source>Edit rule</source>
+ <translation type="unfinished">ルールを編集</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="878"/>
+ <source>Delete rule</source>
+ <translation type="unfinished">ルールを削除</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="674"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックでルールの詳細を確認できます)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation type="obsolete">ルール名を検索</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="704"/>
+ <source>Application rules</source>
+ <translation type="unfinished">アプリケーションのルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="806"/>
+ <source>Permanent</source>
+ <translation type="unfinished">永久ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="815"/>
+ <source>Temporary</source>
+ <translation type="unfinished">一時ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="933"/>
+ <source>Hosts</source>
+ <translation type="unfinished">ホスト</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックで詳細を確認できます)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation type="obsolete">記録した全てのホストを消去</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1020"/>
+ <source>Applications</source>
+ <translation type="unfinished">アプリケーション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation type="obsolete">記録した全てのアプリケーション履歴を消去</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1127"/>
+ <source>Addresses</source>
+ <translation type="unfinished">アドレス</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation type="obsolete">記録した全てのアドレスを消去</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1214"/>
+ <source>Ports</source>
+ <translation type="unfinished">ポート</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation type="obsolete">記録した全てのポートを消去</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1298"/>
+ <source>Users</source>
+ <translation type="unfinished">ユーザー</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation type="obsolete">記録した全てのユーザー履歴を消去</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1404"/>
+ <source>Connections</source>
+ <translation type="unfinished">通過</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1459"/>
+ <source>Dropped</source>
+ <translation type="unfinished">ブロック</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1514"/>
+ <source>Uptime</source>
+ <translation type="unfinished">実行時間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1639"/>
+ <source>Version</source>
+ <translation type="unfinished">バージョン</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(項目をダブルクリックするとルールの詳細が確認できます)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="912"/>
+ <source>Delete connections that matched this rule</source>
+ <translation type="unfinished">このルールにマッチした接続を削除します</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="797"/>
+ <source>All applications</source>
+ <translation type="unfinished">全てのアプリケーション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation type="unfinished">ダッシュボードを開く</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation type="unfinished">ヘルプ</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation type="unfinished">終了</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation type="unfinished">有効化</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation type="unfinished">無効化</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation type="unfinished">再起動するまで</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation type="unfinished">永久に</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation type="unfinished">許可</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation type="unfinished">拒否</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation type="unfinished">外部への接続</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation type="unfinished">プロセスの実行元:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation type="unfinished">次の実行ファイルを</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation type="unfinished">次のコマンドラインを</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation type="unfinished"><b>リモート</b>プロセス %s は <b>%s</b> で実行中です</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="unfinished">は <b>%s</b> の %s ポート %d 番に接続しています</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation type="unfinished">は<b>%s</b> を %sの %s ポート %dで解決しようとしています</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="105"/>
+ <source>New outgoing connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/>
+ <source>Exception saving config: {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>Warning</source>
+ <translation type="unfinished">警告</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation type="unfinished">データベースの保存ファイルを選択するか、方式「メモリ内」を選択する必要があります。</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>DB type changed</source>
+ <translation type="unfinished">データベース方式が変更されました</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation type="unfinished">GUIを再起動後反映されます</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/>
+ <source>Server address can not be empty</source>
+ <translation type="unfinished">サーバーアドレスは空白にすることはできません</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/>
+ <source>Error loading {0} configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/>
+ <source>Configuration applied.</source>
+ <translation type="unfinished">構成は反映されました。</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/>
+ <source>Error applying configuration: {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation type="unfinished"><b>プロセス情報の読み込みでエラーが発生しました:</b> <br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation type="unfinished"><b>プロセス監視の停止中にエラーが発生しました:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation type="unfinished">読み込み中...</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/>
+ <source>There're no nodes connected.</source>
+ <translation type="unfinished">接続しているノードがありません。</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/>
+ <source>Rule applied.</source>
+ <translation type="unfinished">ルールが反映されました。</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Error applying rule: {0}</source>
+ <translation type="unfinished">ルールの反映に失敗しました: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation type="unfinished">プロトコルを指定するかチェックを外してください</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/>
+ <source>Protocol regexp error</source>
+ <translation type="unfinished">プロトコルの正規表現記法が誤っています</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/>
+ <source>process path can not be empty</source>
+ <translation type="unfinished">プロセスのパスを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/>
+ <source>Process path regexp error</source>
+ <translation type="unfinished">プロセスパスの正規表現記法が誤っています</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/>
+ <source>command line can not be empty</source>
+ <translation type="unfinished">コマンドラインを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/>
+ <source>Command line regexp error</source>
+ <translation type="unfinished">コマンドラインの正規表現記法が誤っています</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/>
+ <source>Dest port can not be empty</source>
+ <translation type="unfinished">宛先ポートを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/>
+ <source>Dst port regexp error</source>
+ <translation type="unfinished">宛先ポートの正規表現記法が誤っています</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/>
+ <source>Dest host can not be empty</source>
+ <translation type="unfinished">宛先ホストを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/>
+ <source>Dst host regexp error</source>
+ <translation type="unfinished">宛先ホストの正規表現記法が誤っています</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation type="unfinished">宛先IP/ネットワークを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/>
+ <source>Dst IP regexp error</source>
+ <translation type="unfinished">宛先IPの正規表現記法が誤っています</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/>
+ <source>User ID can not be empty</source>
+ <translation type="unfinished">ユーザーIDを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/>
+ <source>User ID regexp error</source>
+ <translation type="unfinished">ユーザーIDの正規表現記法が誤っています</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/>
+ <source>Lists field cannot be empty</source>
+ <translation type="unfinished">リスト項目を指定してください</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/>
+ <source>Lists field must be a directory</source>
+ <translation type="unfinished">リスト項目には必ずディレクトリを指定してください</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/>
+ <source><b>Rule not supported</b></source>
+ <translation type="unfinished"><b>ルールをサポートしていません</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/>
+ <source><b>Error loading rule</b></source>
+ <translation type="unfinished"><b>ルールの読み込みに失敗しました</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/>
+ <source>There's already a rule with this name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/>
+ <source>PID field can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/>
+ <source>PID field regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/>
+ <source>Select at least one field.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">名前</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">アドレス</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="176"/>
+ <source>Status</source>
+ <translation type="obsolete">状態</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="177"/>
+ <source>Hostname</source>
+ <translation type="obsolete">ホスト名</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="183"/>
+ <source>Version</source>
+ <translation type="obsolete">バージョン</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="180"/>
+ <source>Rules</source>
+ <translation type="obsolete">ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">時間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">アクション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">有効期間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <translation type="obsolete">ノード</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">有効</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="253"/>
+ <source>Hits</source>
+ <translation type="unfinished">回数</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">プロトコル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation type="unfinished">停止中</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation type="unfinished">無効</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation type="unfinished">実行中</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="568"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation type="unfinished">OpenSnitch ネットワークモニター {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="570"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="731"/>
+ <source>Disable</source>
+ <translation type="unfinished">無効化</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="733"/>
+ <source>Enable</source>
+ <translation type="unfinished">有効化</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Duplicate</source>
+ <translation type="unfinished">複製</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="737"/>
+ <source>Edit</source>
+ <translation type="unfinished">編集</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="738"/>
+ <source>Delete</source>
+ <translation type="unfinished">削除</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="761"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="unfinished"> このルールを消去しようとしています。 </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> Are you sure?</source>
+ <translation type="unfinished"> 宜しいですか?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="891"/>
+ <source>Rule not found by that name and node</source>
+ <translation type="unfinished">該当するルールが見つかりませんでした</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/>
+ <source>Save as CSV</source>
+ <translation type="unfinished">CSVファイルに保存</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="921"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation type="unfinished"><b>エラーr:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="928"/>
+ <source>Warning:</source>
+ <translation type="unfinished">警告:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="717"/>
+ <source>Allow</source>
+ <translation type="unfinished">許可</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="718"/>
+ <source>Deny</source>
+ <translation type="unfinished">拒否</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="722"/>
+ <source>Always</source>
+ <translation type="unfinished">常に</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="723"/>
+ <source>Until reboot</source>
+ <translation type="unfinished">再起動するまで</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> You are about to delete this rule. </source>
+ <translation type="unfinished"> このルールを削除しようとしています。 </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">名前</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">名前</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">アドレス</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">状態</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">ホスト名</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">バージョン</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">時間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">アクション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">有効期間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">ノード</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">有効</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">回数</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">プロトコル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="392"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">名前</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">アドレス</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="379"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">状態</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="380"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">ホスト名</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">バージョン</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="390"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">時間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="395"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">アクション</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="396"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">有効期間</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="391"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">ノード</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="393"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">有効</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">回数</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">プロトコル</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">プロセス</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">宛先</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">ルール</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">ユーザーID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="377"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">最終接続</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">引数</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">宛先IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">宛先ホスト</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">宛先ポート</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="252"/>
+ <source>What</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="709"/>
+ <source>Apply to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="719"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="378"/>
+ <source>Addr</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="382"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="394"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="644"/>
+ <source>New node connected</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="lt">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>opensnitch-qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>Vartotojo ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-weight:600;">Įvykdyta iš</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation>TekstoEtiketė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>Šaltinio IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>Proceso ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>Paskirties IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>Paskirties prievadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>iš šio vykdomojo failo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>iš šios komandinės eilutės</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>šis paskirties prievadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>šis vartotojas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>šis paskirties ip</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation>kartą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation>30 sek.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation>5 min.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation>15 min.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation>30 min.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation>1 val.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="706"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation>amžinai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation>Drausti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation>Leisti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation>iki perkrovimo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Nuostatos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation>Vartotojo sąsaja</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="54"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source>
+ <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation>Numatytasis laiko limitas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation>Iššokančiojo lango numatytoji trukmė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="777"/>
+ <source>Default duration</source>
+ <translation>Numatytoji trukmė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">Acción por defecto de la ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">Acción por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation>Numatytasis tikslas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation>centre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation>viršuje dešinėje</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation>apačioje dešinėje</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation>viršuje kairėje</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation>apačioje kairėje</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="167"/>
+ <source>Prompt dialog default position on screen</source>
+ <translation type="obsolete">Posición por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation>pagal vykdomąjį failą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation>pagal komandinę eilutę</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation>pagal paskirties prievadą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation>pagal paskirties ip</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation>pagal vartotojo ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source>once</source>
+ <translation>kartą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation>30 sek.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation>5 min.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation>15 min.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation>30 min.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation>1 val.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation>amžinai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="923"/>
+ <source>deny</source>
+ <translation>drausti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>allow</source>
+ <translation>leisti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">Išjungti iššokančius langus, rodyti tik įspėjimą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source>Nodes</source>
+ <translation>Mazgai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="740"/>
+ <source>Process monitor method</source>
+ <translation>Proceso stebėjimo metodas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="774"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Numatytoji trukmė bus taikoma, kai nėra prijungtos vartotojo sąsajos.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="899"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>Mazgo adresas </p><p>Numatytoji reikšmė: unix:///tmp/osui.sock (unix:// privaloma, jei tai Unix lizdas) </p><p>Tai taip pat gali būti IP adresas su prievadu: 127.0.0.1:50051</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="902"/>
+ <source>Address</source>
+ <translation>Adresas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1042"/>
+ <source>Default log level</source>
+ <translation>Numatytasis registravimo žurnale lygis</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="950"/>
+ <source>Version</source>
+ <translation>Versija</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="813"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Numatytasis veiksmas bus atliekamas, kai nėra prijungtos vartotojo sąsajos.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="757"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>Žurnalo failas, į kurį įrašomi žurnalo įrašai.<br/></p><p>/dev/stdout spausdins žurnalus į standartinę išvestį.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="760"/>
+ <source>Log file</source>
+ <translation>Žurnalo failas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones.
+
+La ventana emergente sólo contendrá información relativa a la conexión.
+
+Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente
+es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">Interceptar conexiones desconocidas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="832"/>
+ <source>HostName</source>
+ <translation>Kompiuterio vardas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1001"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation>unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="886"/>
+ <source>until restart</source>
+ <translation>iki perkrovimo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source>always</source>
+ <translation>visada</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1013"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation>/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1018"/>
+ <source>/dev/stdout</source>
+ <translation>/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>Taikyti konfigūraciją visiems mazgams</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1057"/>
+ <source>Database</source>
+ <translation>Duomenų bazė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1092"/>
+ <source>In memory</source>
+ <translation>Atmintyje</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1097"/>
+ <source>File</source>
+ <translation>Failas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1363"/>
+ <source>Close</source>
+ <translation>Uždaryti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1374"/>
+ <source>Apply</source>
+ <translation>Taikyti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1385"/>
+ <source>Save</source>
+ <translation>Išsaugoti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation>iki perkrovimo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1111"/>
+ <source>Database type</source>
+ <translation>Duomenų bazės tipas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1118"/>
+ <source>Select</source>
+ <translation>Pasirinkti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Pop-ups default options</source>
+ <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="367"/>
+ <source>Pop-ups default position on screen</source>
+ <translation type="obsolete">Posición en pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="102"/>
+ <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source>
+ <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation>Rodyti išplėstinį rodinį automatiškai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="660"/>
+ <source>Action</source>
+ <translation>Veiksmas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation><html><head/><body><p>Jei pažymėta, iššokantys langai bus rodomi su aktyviu išplėstiniu rodiniu.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation>Trukmė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation>Taip pat filtruoti prisijungimus pagal:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="362"/>
+ <source>If checked, this field will be checked when a pop-up is displayed</source>
+ <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation>Vartotojo ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation>Paskirties prievadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation>Paskirties IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation><html><head/><body><p>Šis laiko limitas yra atgalinis laikmatis, kurį matote, kai rodomas iššokantis dialogo langas.</p><p>Jei į iššokantį dialogo langą neatsakoma, taikomos numatytosios parinktys.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation>Išplėstinis vaizdas leidžia lengvai pasirinkti kelis laukus, kad būtų galima filtruoti prisijungimus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation>Jei pažymėta, šis laukas bus pasirinktas, kai bus rodomas iššokantis langas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation><html><head/><body><p>Iššokančio lango numatytasis veiksmas.</p><p>Kai ruošiamasi užmegzti naują išeinantį ryšį, šis veiksmas bus pasirinktas pagal numatytuosius nustatymus, todėl, jei suveiks laiko limitas, bus taikoma ši parinktis.</p><p><br/></p><p>Kai iššokančiame lange prašoma leisti arba neleisti prisijungti: </p><p>1. Nauji išeinantys ryšiai neleidžiami.</p><p>2. Žinomi ryšiai leidžiami arba neleidžiami pagal vartotojo nustatytas taisykles.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="816"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>Default action when the GUI is disconnected</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="912"/>
+ <source>Debug invalid connections</source>
+ <translation>Debug invalid connections</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation>Iššokantys langai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation>Numatytosios parinktys</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation>Numatytoji padėtis ekrane</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="713"/>
+ <source>any temporary rules</source>
+ <translation>bet kokios laikinos taisyklės</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="478"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="481"/>
+ <source>Don't save rules of duration</source>
+ <translation>Don't save rules of duration</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="463"/>
+ <source>Show events columns</source>
+ <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="596"/>
+ <source>Time</source>
+ <translation>Laikas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="676"/>
+ <source>Destination</source>
+ <translation>Paskirties vieta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="644"/>
+ <source>Protocol</source>
+ <translation>Protokolas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="692"/>
+ <source>Process</source>
+ <translation>Procesas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="612"/>
+ <source>Rule</source>
+ <translation>Taisyklė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="628"/>
+ <source>Node</source>
+ <translation>Mazgas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="584"/>
+ <source>Events tab columns</source>
+ <translation>Events tab columns</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="494"/>
+ <source>Desktop notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="512"/>
+ <source>Use system notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="528"/>
+ <source>Use Qt notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="557"/>
+ <source>Test</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="570"/>
+ <source>System</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="705"/>
+ <source>Theme</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="909"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1196"/>
+ <source>minutes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1222"/>
+ <source>Minutes between events purges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1245"/>
+ <source>days</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1255"/>
+ <source>Maximum days of events to keep</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation>Proceso informacija</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation>įkeliama…</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation>CWD: loading...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation>mem stats: loading...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation>Būsena</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation>Atidaryti failus</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation>I/O Statistics</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation>Memory mapped files</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation>Stack</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation>Aplinkos kintamieji</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation>Application pids</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation>Pradėti arba sustabdyti šio proceso stebėjimą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation>Uždaryti</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/>
+ <source>Rule</source>
+ <translation>Taisyklė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="852"/>
+ <source>Node</source>
+ <translation>Mazgas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="875"/>
+ <source>Apply rule to all nodes</source>
+ <translation>Taikyti taisyklę visiems mazgams</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/>
+ <source>From this command line</source>
+ <translation>Iš šios komandinės eilutės</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="341"/>
+ <source>From this executable</source>
+ <translation>Iš šio vykdomojo failo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/>
+ <source>Action</source>
+ <translation>Veiksmas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="456"/>
+ <source>To this IP / Network</source>
+ <translation>Į šį IP / tinklą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/>
+ <source>once</source>
+ <translation>kartą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="102"/>
+ <source>30s</source>
+ <translation>30 sek.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/>
+ <source>5m</source>
+ <translation>5 min.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/>
+ <source>15m</source>
+ <translation>15 min.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/>
+ <source>30m</source>
+ <translation>30 min.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/>
+ <source>1h</source>
+ <translation>1 val.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source>until restart</source>
+ <translation type="obsolete">hasta reiniciar (el servicio)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/>
+ <source>always</source>
+ <translation>visada</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="366"/>
+ <source>To this port</source>
+ <translation>Į šį prievadą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="247"/>
+ <source>From this user ID</source>
+ <translation>Iš šio vartotojo ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="492"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation>Kelių domenų nurodyti kableliais ar tarpais neleidžiama.
+
+Vietoj jų naudokite reguliariąsias išraiškas:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+arba vieną domeną:
+www.gnu.org - atitiks tik www.gnu.org, nei ftp.gnu.org, nei www2.gnu.org, ...
+gnu.org - atitiks tik gnu.org, www.gnu.org, ftp.gnu.org, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="503"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation>www.domenas.org, .*\.domenas.org</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="396"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation><html><head/><body><p>Leidžiama naudoti tik TCP, UDP arba UDPLITE</p><p>Galite naudoti regexp, t. y.: ^(TCP|UDP)$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="406"/>
+ <source>TCP</source>
+ <translation>TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/>
+ <source>UDP</source>
+ <translation>UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/>
+ <source>UDPLITE</source>
+ <translation>UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/>
+ <source>TCP6</source>
+ <translation>TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/>
+ <source>UDP6</source>
+ <translation>UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/>
+ <source>UDPLITE6</source>
+ <translation>UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="513"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation>Galite nurodyti vieną IP adresą:
+- 192.168.1.1
+
+arba reguliarią išraišką:
+- 192\.168\.1\.[0-9]+
+
+kelių IP adresų:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+Taip pat galite nurodyti potinklį:
+- 192.168.1.0/24
+
+Pastaba: kableliais ar tarpais atskirti IP ar tinklų negalima.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/>
+ <source>LAN</source>
+ <translation>LAN</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/>
+ <source>127.0.0.0/8</source>
+ <translation>127.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>192.168.0.0/24</source>
+ <translation>192.168.0.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/>
+ <source>192.168.1.0/24</source>
+ <translation>192.168.1.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/>
+ <source>192.168.2.0/24</source>
+ <translation>192.168.2.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/>
+ <source>192.168.0.0/16</source>
+ <translation>192.168.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/>
+ <source>169.254.0.0/16</source>
+ <translation>169.254.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/>
+ <source>172.16.0.0/12</source>
+ <translation>172.16.0.0/12</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/>
+ <source>10.0.0.0/8</source>
+ <translation>10.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/>
+ <source>::1/128</source>
+ <translation>::1/128</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/>
+ <source>fc00::/7</source>
+ <translation>fc00::/7</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/>
+ <source>ff00::/8</source>
+ <translation>ff00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/>
+ <source>fe80::/10</source>
+ <translation>fe80::/10</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/>
+ <source>fd00::/8</source>
+ <translation>fd00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/>
+ <source>Duration</source>
+ <translation>Trukmė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="449"/>
+ <source>Protocol</source>
+ <translation>Protokolas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="373"/>
+ <source>To this host</source>
+ <translation>To this host</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/>
+ <source>Deny</source>
+ <translation>Drausti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/>
+ <source>Allow</source>
+ <translation>Leisti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="912"/>
+ <source>Name</source>
+ <translation>Pavadinimas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="884"/>
+ <source>Enable</source>
+ <translation>Įjungti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="928"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation>Taisyklės tikrinamos abėcėlės tvarka, todėl galite jas atitinkamai pavadinę nustatyti jų prioritetus.
+
+000-allow-localhost
+001-deny-broadcast
+...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="935"/>
+ <source>leave blank to autocreate</source>
+ <translation>palikite tuščią, kad sukurtumėte automatiškai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="891"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation>Jei pažymėta, ši taisyklė bus viršesnė už kitas taisykles. Po šios taisyklės nebus tikrinamos jokios kitos taisyklės.
+
+Taisyklę turite pavadinti taip, kad ji būtų tikrinama pirma, nes taisyklės tikrinamos abėcėlės tvarka. Pavyzdžiui:
+
+[x] Prioritetas - 000-prioritetinė-taisyklė
+[ ] Prioritetas - 001-mažesnio prioriteto taisyklė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Priority rule</source>
+ <translation>Prioritetinė taisyklė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="770"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation><html><head/><body><p>Pagal numatytuosius nustatymus taisyklių lauke neribojamos didžiosios raidės, t. y., jei procesas bando prisijungti prie gOOgle.CoM, o jūs turite taisyklę Deny .*google.com, prisijungimas bus užblokuotas.<br/></p><p>Jei pažymėsite šį laukelį, turėsite nurodyti tikslų (domeną, vykdomąjį failą, komandinę eilutę), kurią norite filtruoti.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/>
+ <source>Case-sensitive</source>
+ <translation>Case-sensitive</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="442"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/>
+ <source>until reboot</source>
+ <translation>iki perkrovimo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="658"/>
+ <source>To this list of domains</source>
+ <translation>Į šį domenų sąrašą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Pasirinkite katalogą su blokuojamų arba leidžiamų domenų sąrašais.</p><p>Į tą katalogą įdėkite failus su bet kokiu plėtiniu, kuriuose yra domenų sąrašai.</p><p><br/>Kiekvieno sąrašo įrašo formatas yra toks (pagrindinio kompiuterio formatas): </p><p>127.0.0.0.1 www.domain.com</p><p>arba </p><p>0.0.0.0.0 www.domenas.com</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/>
+ <source>Deny will just discard the connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/>
+ <source>Reject will drop the connection, and kill the socket that initiated it</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/>
+ <source>Allow will allow the connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="221"/>
+ <source>Applications</source>
+ <translation type="unfinished">Programos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="240"/>
+ <source>Is regular expression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="274"/>
+ <source>From this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>is regular expression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="360"/>
+ <source>Network</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/>
+ <source>List of domains/IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="616"/>
+ <source>To this list of network ranges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="623"/>
+ <source>To this list of IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="649"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="684"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="712"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="727"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="764"/>
+ <source>More</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>OpenSnitch tinklo statistika</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="284"/>
+ <source>Save to CSV.</source>
+ <translation>Įrašyti į CSV.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="294"/>
+ <source>Ctrl+S</source>
+ <translation>Ctrl+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="330"/>
+ <source>Create a new rule</source>
+ <translation>Sukurti naują taisyklę</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="360"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">pagrindinio kompiuterio vardas - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="399"/>
+ <source>Status</source>
+ <translation>Statusas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1627"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="437"/>
+ <source>Start or Stop interception</source>
+ <translation>Pradėti arba sustabdyti perėmimą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="482"/>
+ <source>Events</source>
+ <translation>Įvykiai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation>Filtras</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation>Leisti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation type="unfinished">Atmesti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation>Pvz.: firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="724"/>
+ <source>Nodes</source>
+ <translation>Mazgai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1531"/>
+ <source>Rules</source>
+ <translation>Taisyklės</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="833"/>
+ <source>enable</source>
+ <translation>įjungti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="684"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="680"/>
+ <source>Application rules</source>
+ <translation>Application rules</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="782"/>
+ <source>Permanent</source>
+ <translation>Nuolatinė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="791"/>
+ <source>Temporary</source>
+ <translation>Laikina</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="895"/>
+ <source>Hosts</source>
+ <translation>Hosts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(dukart spustelėkite, kad peržiūrėtumėte detalią informaciją apie elementą)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="982"/>
+ <source>Applications</source>
+ <translation>Programos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1089"/>
+ <source>Addresses</source>
+ <translation>Adresai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1176"/>
+ <source>Ports</source>
+ <translation>Prievadai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1260"/>
+ <source>Users</source>
+ <translation>Vartotojai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1366"/>
+ <source>Connections</source>
+ <translation>Connections</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1421"/>
+ <source>Dropped</source>
+ <translation>Dropped</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1476"/>
+ <source>Uptime</source>
+ <translation>Veikimo laikas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1601"/>
+ <source>Version</source>
+ <translation>Versija</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation type="unfinished">Ištrinti visus perimtus įvykius</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="840"/>
+ <source>Edit rule</source>
+ <translation>Redaguoti taisyklę</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="854"/>
+ <source>Delete rule</source>
+ <translation>Ištrinti taisyklę</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(dukart spustelėkite, kad peržiūrėtumėte detalią informaciją apie taisyklę)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="773"/>
+ <source>All applications</source>
+ <translation>Visos programos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation>Statistika</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation>Pagalba</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation>Uždaryti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation>Įjungti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation>Išjungti</translation>
+ </message>
+</context>
+<context>
+ <name>menu_close</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="131"/>
+ <source>Close</source>
+ <translation type="obsolete">Cerrar</translation>
+ </message>
+</context>
+<context>
+ <name>menu_help</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="126"/>
+ <source>Help</source>
+ <translation type="obsolete">Ayuda</translation>
+ </message>
+</context>
+<context>
+ <name>menu_statistics</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="120"/>
+ <source>Statistics</source>
+ <translation type="obsolete">Eventos</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="547"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation>Leisti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation>Drausti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation>amžinai</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation>Outgoing connection</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation>Procesas paleistas iš:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation>iš šios komandinės eilutės</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation>iš šio vykdomojo failo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/>
+ <source>Unknown process</source>
+ <translation type="obsolete">Proceso no encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation>iki perkrovimo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation>į prievadą {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/>
+ <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation>į {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation>iš vartotojo {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation>į {0}.*</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation>į *.{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation type="obsolete">į *{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation><b>Nuotolinis</b> procesas %s veikia <b>%s</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation>jungiasi prie <b>%s</b> per %s prievadą %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation>bando išspręsti <b>%s</b> per %s, %s prievadą %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="108"/>
+ <source>New outgoing connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/>
+ <source>Exception saving config: %s</source>
+ <translation type="obsolete">Error al guarda la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/>
+ <source>Applying configuration on %s ...</source>
+ <translation type="obsolete">Aplicando configuración en %s ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="230"/>
+ <source>Server address can not be empty</source>
+ <translation>Serverio adresas negali būti tuščias</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/>
+ <source>Error loading %s configuration</source>
+ <translation type="obsolete">Error al cargar la configuración %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="477"/>
+ <source>Configuration applied.</source>
+ <translation>Konfigūracija pritaikyta.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/>
+ <source>Error applying configuration: %s</source>
+ <translation type="obsolete">Error al aplicar la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/>
+ <source>Exception saving config: {0}</source>
+ <translation>Išimtis išsaugant konfigūraciją: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="418"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation>Taikoma konfigūracija {0} ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="260"/>
+ <source>Error loading {0} configuration</source>
+ <translation>Įkeliant {0} konfigūraciją įvyko klaida</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="479"/>
+ <source>Error applying configuration: {0}</source>
+ <translation>Taikant konfigūraciją įvyko klaida: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/>
+ <source>Warning</source>
+ <translation>Įspėjimas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation>Turite pasirinkti duomenų bazės failą<br>arba pasirinkite tipą „Atmintyje“.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="351"/>
+ <source>DB type changed</source>
+ <translation>DB tipas pakeistas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="351"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation>Restart the GUI in order effects to take effect</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="509"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation>Užveskite pelės žymeklį virš teksto, kad būtų rodoma pagalba<br><br>Nepamirškite apsilankyti wiki: <a href="{0}">{0}</a></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="127"/>
+ <source>System</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="135"/>
+ <source>Themes not available. Install qt-material: pip3 install qt-material</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/>
+ <source>UI theme changed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/>
+ <source>Restart the GUI in order to apply the new theme</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation><b>Klaida įkeliant proceso informaciją:</b><br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation><b>Sustabdant stebėjimo procesą įvyko klaida:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation>įkeliama…</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="164"/>
+ <source>There're no nodes connected.</source>
+ <translation>Nėra prijungtų mazgų.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Rule applied.</source>
+ <translation>Taisyklė pritaikyta.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/>
+ <source>Error applying rule: %s</source>
+ <translation type="obsolete">Error al aplicar la regla: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="537"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation>protokolas negali būti tuščias arba panaikinkite jo žymėjimą</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="551"/>
+ <source>Protocol regexp error</source>
+ <translation type="unfinished">Protokolo regexp klaida</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="555"/>
+ <source>process path can not be empty</source>
+ <translation>proceso kelias negali būti tuščias</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="569"/>
+ <source>Process path regexp error</source>
+ <translation>Process path regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="573"/>
+ <source>command line can not be empty</source>
+ <translation>komandinė eilutė negali būti tuščia</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="587"/>
+ <source>Command line regexp error</source>
+ <translation>Command line regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="591"/>
+ <source>Dest port can not be empty</source>
+ <translation>Paskirties prievadas negali būti tuščias</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="605"/>
+ <source>Dst port regexp error</source>
+ <translation>Dst port regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="609"/>
+ <source>Dest host can not be empty</source>
+ <translation>Dest host can not be empty</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="623"/>
+ <source>Dst host regexp error</source>
+ <translation>Dst host regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="627"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>Dest IP/Network can not be empty</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="649"/>
+ <source>Dst IP regexp error</source>
+ <translation>Dst IP regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="661"/>
+ <source>User ID can not be empty</source>
+ <translation>Vartotojo ID negali būti tuščias</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="675"/>
+ <source>User ID regexp error</source>
+ <translation>User ID regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="207"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Klaida taikant taisyklę: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/>
+ <source>Lists field cannot be empty</source>
+ <translation>Sąrašų laukas negali būti tuščias</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="751"/>
+ <source>Lists field must be a directory</source>
+ <translation>Lists field must be a directory</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="794"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>Taisyklė nepalaikoma</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="439"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Klaida įkeliant taisyklę</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="181"/>
+ <source>There's already a rule with this name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="679"/>
+ <source>PID field can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="693"/>
+ <source>PID field regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="781"/>
+ <source>Select at least one field.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation>Neveikia</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation>Išjungta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation>Veikia</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="412"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="414"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="899"/>
+ <source> Your are about to delete this rule. </source>
+ <translation> Ketinate ištrinti šią taisyklę. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/>
+ <source> Are you sure?</source>
+ <translation> Ar esate tuo tikras?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="583"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation>OpenSnitch tinklo statistika {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="585"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation>{0} OpenSnitch tinklo statistika</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="24"/>
+ <source>Hits</source>
+ <translation type="unfinished">Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1901"/>
+ <source>Save as CSV</source>
+ <translation>Išsaugoti kaip CSV</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="765"/>
+ <source>Delete</source>
+ <translation>Išrinti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source>always</source>
+ <translation type="obsolete">siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="580"/>
+ <source><b>Error:</b><br><br>{0}</source>
+ <translation type="obsolete"><b>Error:</b><br><br>{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="758"/>
+ <source>Disable</source>
+ <translation>Išjungti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="760"/>
+ <source>Enable</source>
+ <translation>Įjungti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="763"/>
+ <source>Duplicate</source>
+ <translation>Duplicate</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="764"/>
+ <source>Edit</source>
+ <translation>Redaguoti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="918"/>
+ <source>Rule not found by that name and node</source>
+ <translation>Taisyklė pagal šį pavadinimą ir mazgą nerasta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation><b>Klaida:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="955"/>
+ <source>Warning:</source>
+ <translation>Įspėjimas:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="744"/>
+ <source>Allow</source>
+ <translation>Leisti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="745"/>
+ <source>Deny</source>
+ <translation>Drausti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="749"/>
+ <source>Always</source>
+ <translation>Visada</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="750"/>
+ <source>Until reboot</source>
+ <translation>Iki perkrovimo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/>
+ <source> You are about to delete this rule. </source>
+ <translation> Ketinate ištrinti šią taisyklę. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>LastConnection</source>
+ <translation type="obsolete">Última Conexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">ÚltimaConexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="285"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Pavadinimas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="286"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Adresas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="287"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Būsena</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Versija</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Taisyklės</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="292"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Laikas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Veiksmas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Trukmė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Mazgas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Įjungta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Hits</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Protokolas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Procesas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Paskirties vieta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Taisyklė</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Vartotojo ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>PaskutinisPrisijungimas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Args</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>PaskIP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DstHost</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>PaskPrievadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="652"/>
+ <source>New node connected</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="23"/>
+ <source>What</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="289"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">Veikimo laikas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Apply to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="746"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="774"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="obsolete"> Estás a punto de borrar esta regla. </translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="776"/>
+ <source> Are you sure?</source>
+ <translation type="obsolete"> ¿Estás seguro?</translation>
+ </message>
+</context>
+<context>
+ <name>stats_disabled</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="74"/>
+ <source>Disabled</source>
+ <translation type="obsolete">Deshabilitado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_notrunning</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="73"/>
+ <source>Not running</source>
+ <translation type="obsolete">Parado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_running</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="75"/>
+ <source>Running</source>
+ <translation type="obsolete">Interceptando</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="409"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de red OpenSnitch</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="411"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nb_NO">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>opensnitch-qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>Brukerid</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-weight:600;">Utført fra</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation>TextLabel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>Kilde-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>Prosess-ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>Mål-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>Målport</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>fra denne kjørbare fil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>fra denne kommandolinje</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>denne målport</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>denne bruker</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>denne mål-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation>en gang</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation>1t</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="706"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation>til evig tid</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation>Nekt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation>Tillat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation>til omstart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation>fra denne PID</translation>
+ </message>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Innstillinger</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation>Grensesnitt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="54"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source>
+ <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation>Forvalgt utløpstid</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation>Forvalgt varighet for sprettoppvindu</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="754"/>
+ <source>Default duration</source>
+ <translation>Forvalgt varighet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">Acción por defecto de la ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">Acción por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation>Forvalgt mål</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation>midtstilt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation>øvre høyre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation>nedre høyre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation>øvre venstre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation>nedre venstre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="167"/>
+ <source>Prompt dialog default position on screen</source>
+ <translation type="obsolete">Posición por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation>av kjørbar fil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation>av kommandolinje</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation>av målport</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation>av mål-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation>av brukerid</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="863"/>
+ <source>once</source>
+ <translation>en gang</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation>1t</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation>til evig tid</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="905"/>
+ <source>deny</source>
+ <translation>nekt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="914"/>
+ <source>allow</source>
+ <translation>tillat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">Deshabilitar ventanas emergentes,
+sólo mostrar alerta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="711"/>
+ <source>Nodes</source>
+ <translation>Noder</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="717"/>
+ <source>Process monitor method</source>
+ <translation>Prosessmonitoreringsmetode</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="751"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Forvalgt varighet tar effekt når det ikke er noe brukergrensesnitt tilkoblet.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>Adressen til noden.</p><p>Forvalgt: unix:///tmp/osui.sock (unix:// er påkrevd hvis det er en Unix-socket)</p><p>Det kan også være en IP-adresse med port: 127.0.0.1:50051</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="884"/>
+ <source>Address</source>
+ <translation>Adresse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1024"/>
+ <source>Default log level</source>
+ <translation>Forvalgt loggnivå</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>Version</source>
+ <translation>Versjon</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Forvalgt handling når det ikke er noe brukergrensesnitt tilkoblet.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>Loggfiler å skrive logger til.<br/></p><p>/dev/stdout skriver logger til standard-ut.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="737"/>
+ <source>Log file</source>
+ <translation>Loggfil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones.
+
+La ventana emergente sólo contendrá información relativa a la conexión.
+
+Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente
+es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">Interceptar conexiones desconocidas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="809"/>
+ <source>HostName</source>
+ <translation>HostName</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="983"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation>unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="868"/>
+ <source>until restart</source>
+ <translation>frem til omstart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="873"/>
+ <source>always</source>
+ <translation>alltid</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="995"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation>/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1000"/>
+ <source>/dev/stdout</source>
+ <translation>/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="767"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>Anvend oppsett for alle noder</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1039"/>
+ <source>Database</source>
+ <translation>Database</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1074"/>
+ <source>In memory</source>
+ <translation>I minnet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1079"/>
+ <source>File</source>
+ <translation>Fil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1345"/>
+ <source>Close</source>
+ <translation>Lukk</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1356"/>
+ <source>Apply</source>
+ <translation>Anvend</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1367"/>
+ <source>Save</source>
+ <translation>Lagre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation>frem til omstart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1093"/>
+ <source>Database type</source>
+ <translation>Databasetype</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1100"/>
+ <source>Select</source>
+ <translation>Velg</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Pop-ups default options</source>
+ <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="367"/>
+ <source>Pop-ups default position on screen</source>
+ <translation type="obsolete">Posición en pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="102"/>
+ <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source>
+ <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation>Vis avansert fremvisning som forvalg</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="665"/>
+ <source>Action</source>
+ <translation>Handling</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation>Varighet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="362"/>
+ <source>If checked, this field will be checked when a pop-up is displayed</source>
+ <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation>Bruker-ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation>Målport</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation>Mål-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>Forvalgt handling når det grafiske brukergrensesnittet er frakoblet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="894"/>
+ <source>Debug invalid connections</source>
+ <translation>Feilsøk ugyldige forbindelser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation>Forvalgte innstillinger</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation>Forvalgt posisjon på skjermen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="479"/>
+ <source>any temporary rules</source>
+ <translation>alle midertidige regler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="492"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="495"/>
+ <source>Don't save rules of duration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="463"/>
+ <source>Show events columns</source>
+ <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="601"/>
+ <source>Time</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>Destination</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="649"/>
+ <source>Protocol</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="697"/>
+ <source>Process</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="617"/>
+ <source>Rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="633"/>
+ <source>Node</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="723"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Si se selecciona opensnitch te preguntará para permitir o denegar conexiones que no tienen un PID asociado. Esto puede pasar por diferentes motivos, principalmente debido a conexiones inválidas.</p><p>La ventana emergente sólo contendrá información de la conexión.</p><p>Hay algunas situaciones en las que estas conexiones son válidas, por ejemplo al establecer un túnel VPN con wireguard.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="589"/>
+ <source>Events tab columns</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="508"/>
+ <source>Desktop notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="526"/>
+ <source>Use system notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="542"/>
+ <source>Use Qt notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="571"/>
+ <source>Test</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1178"/>
+ <source>minutes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1204"/>
+ <source>Minutes between events purges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1227"/>
+ <source>days</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1237"/>
+ <source>Maximum days of events to keep</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="14"/>
+ <source>Rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/>
+ <source>Node</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/>
+ <source>Apply rule to all nodes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/>
+ <source>From this command line</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/>
+ <source>From this executable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/>
+ <source>Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/>
+ <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source>
+ <translation type="obsolete">/ruta/al/ejecutable, .*/bin/executable[0-9\.]+$, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/>
+ <source>To this IP / Network</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/>
+ <source>once</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/>
+ <source>30s</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/>
+ <source>5m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/>
+ <source>15m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/>
+ <source>30m</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/>
+ <source>1h</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source>until restart</source>
+ <translation type="obsolete">hasta reiniciar (el servicio)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/>
+ <source>always</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/>
+ <source>To this port</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/>
+ <source>From this user ID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="430"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/>
+ <source>TCP</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/>
+ <source>UDP</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/>
+ <source>UDPLITE</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>TCP6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/>
+ <source>UDP6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/>
+ <source>UDPLITE6</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/>
+ <source>LAN</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/>
+ <source>127.0.0.0/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/>
+ <source>192.168.0.0/24</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/>
+ <source>192.168.1.0/24</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/>
+ <source>192.168.2.0/24</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/>
+ <source>192.168.0.0/16</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/>
+ <source>169.254.0.0/16</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/>
+ <source>172.16.0.0/12</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/>
+ <source>10.0.0.0/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/>
+ <source>::1/128</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/>
+ <source>fc00::/7</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/>
+ <source>ff00::/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/>
+ <source>fe80::/10</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/>
+ <source>fd00::/8</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/>
+ <source>Duration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/>
+ <source>Protocol</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/>
+ <source>To this host</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/>
+ <source>Deny</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/>
+ <source>Allow</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="713"/>
+ <source>leave blank to autocreate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="54"/>
+ <source>Priority rule</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="77"/>
+ <source>Case-sensitive</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/>
+ <source>until reboot</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/>
+ <source>To this list of domains</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Selecciona un directorio con listas de dominios para permitir o denegar.</p><p>Mete dentro de este directorio ficheros con cualquier extensión que contengan listas de dominios.</p><p><br/>El formato de cada dominio de la lista tiene que estar en formato hosts, así:</p><p>127.0.0.1 www.domain.com</p><p>o </p><p>0.0.0.0 www.domain.com</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/>
+ <source>Applications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/>
+ <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="246"/>
+ <source>From this PID</source>
+ <translation>Fra denne PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/>
+ <source>Network</source>
+ <translation>Nettverk</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/>
+ <source>List of domains/IPs</source>
+ <translation>Liste med domener/IP-nummer</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>To this list of network ranges</source>
+ <translation>Til denne listen med nettverkområder</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/>
+ <source>To this list of IPs</source>
+ <translation>Til denne listen med IP-adresser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="608"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="636"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="651"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation>Til denne listen med domener
+(regulæruttrykk)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/>
+ <source>Reject</source>
+ <translation>Avvis</translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>OpenSnitch nettverkstatistikk</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="290"/>
+ <source>Save to CSV.</source>
+ <translation>Lagre til CSV.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="300"/>
+ <source>Ctrl+S</source>
+ <translation>Ctrl+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="351"/>
+ <source>Create a new rule</source>
+ <translation>Lag ny regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="381"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">vertsnavn - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="420"/>
+ <source>Status</source>
+ <translation>Status</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1665"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="464"/>
+ <source>Start or Stop interception</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="509"/>
+ <source>Events</source>
+ <translation>Hendelser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation>Filter</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation>Tillat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation>Nekt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation>F.eks.: firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="748"/>
+ <source>Nodes</source>
+ <translation>Noder</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en la columna Dirección para ver los detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1569"/>
+ <source>Rules</source>
+ <translation>Regler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="857"/>
+ <source>enable</source>
+ <translation>aktiver</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="684"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation type="obsolete">buscar regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="704"/>
+ <source>Application rules</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="806"/>
+ <source>Permanent</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="815"/>
+ <source>Temporary</source>
+ <translation>Midlertidig</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="933"/>
+ <source>Hosts</source>
+ <translation>Verter</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en un dominio para ver detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1020"/>
+ <source>Applications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1127"/>
+ <source>Addresses</source>
+ <translation>Adresser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1214"/>
+ <source>Ports</source>
+ <translation>Porter</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1298"/>
+ <source>Users</source>
+ <translation>Brukere</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1404"/>
+ <source>Connections</source>
+ <translation>Forbindelser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1459"/>
+ <source>Dropped</source>
+ <translation>Droppet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1514"/>
+ <source>Uptime</source>
+ <translation>Oppetid</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1639"/>
+ <source>Version</source>
+ <translation>Versjon</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="864"/>
+ <source>Edit rule</source>
+ <translation>Endre regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="878"/>
+ <source>Delete rule</source>
+ <translation>Slett regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation type="obsolete">Borrar todos los hosts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation type="obsolete">Borrar todos las aplicaciones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation type="obsolete">Borrar todas las direcciones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation type="obsolete">Borrar todos los puertos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation type="obsolete">Borrar todos los usuarios</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete">(Doble click en una fila para editar una regla)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="912"/>
+ <source>Delete connections that matched this rule</source>
+ <translation>Slett forbindelser som passer til denne regel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="797"/>
+ <source>All applications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation>Avvis</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation>0</translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation>Statistikk</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation>Hjelp</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation>Lukk</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation>Aktiver</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation>Deaktiver</translation>
+ </message>
+</context>
+<context>
+ <name>menu_close</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="131"/>
+ <source>Close</source>
+ <translation type="obsolete">Cerrar</translation>
+ </message>
+</context>
+<context>
+ <name>menu_help</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="126"/>
+ <source>Help</source>
+ <translation type="obsolete">Ayuda</translation>
+ </message>
+</context>
+<context>
+ <name>menu_statistics</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="120"/>
+ <source>Statistics</source>
+ <translation type="obsolete">Eventos</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation>Tillat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation>Nekt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation>for alltid</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation>Utgående forbindelse</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation>Prosess startet fra:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation>fra denne kommandolinjen</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/>
+ <source>Unknown process</source>
+ <translation type="obsolete">Proceso no encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation>frem til omstart</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation>til port {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/>
+ <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation>til {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation>fra bruker {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation>til {0}.*</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation>til *.{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation type="obsolete">a *{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="105"/>
+ <source>New outgoing connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/>
+ <source>Exception saving config: %s</source>
+ <translation type="obsolete">Error al guarda la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/>
+ <source>Applying configuration on %s ...</source>
+ <translation type="obsolete">Aplicando configuración en %s ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/>
+ <source>Server address can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/>
+ <source>Error loading %s configuration</source>
+ <translation type="obsolete">Error al cargar la configuración %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/>
+ <source>Configuration applied.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/>
+ <source>Error applying configuration: %s</source>
+ <translation type="obsolete">Error al aplicar la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/>
+ <source>Exception saving config: {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/>
+ <source>Error loading {0} configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/>
+ <source>Error applying configuration: {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>DB type changed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/>
+ <source>There're no nodes connected.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/>
+ <source>Rule applied.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/>
+ <source>Error applying rule: %s</source>
+ <translation type="obsolete">Error al aplicar la regla: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/>
+ <source>Protocol regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/>
+ <source>process path can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/>
+ <source>Process path regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/>
+ <source>command line can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/>
+ <source>Command line regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/>
+ <source>Dest port can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/>
+ <source>Dst port regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/>
+ <source>Dest host can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/>
+ <source>Dst host regexp error</source>
+ <translation>Feil med målvert-regulæruttrykk</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>Mål-IP/nettverk kan ikke være blank</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/>
+ <source>Dst IP regexp error</source>
+ <translation>Feil med regulæruttrykk for mål-IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/>
+ <source>User ID can not be empty</source>
+ <translation>Bruker-ID kan ikke være blank</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/>
+ <source>User ID regexp error</source>
+ <translation>Feil med regulæruttrykk for bruker-ID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Feil ved anvending av regel: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/>
+ <source>Lists field cannot be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/>
+ <source>Lists field must be a directory</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>Regelen støttes ikke</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Feil ved regellasting</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/>
+ <source>There's already a rule with this name.</source>
+ <translation>Det er allerede en regel med dette navnet.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/>
+ <source>PID field can not be empty</source>
+ <translation>PID-feltet kan ikke være blankt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/>
+ <source>PID field regexp error</source>
+ <translation>Feil med regulæruttrykk for PID-felt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/>
+ <source>Select at least one field.</source>
+ <translation>Velg minst ett felt.</translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation>Utkoblet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation>Kjører</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="412"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="414"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="761"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> Are you sure?</source>
+ <translation> Er du sikker?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="568"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="570"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="176"/>
+ <source>Status</source>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="177"/>
+ <source>Hostname</source>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="183"/>
+ <source>Version</source>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="180"/>
+ <source>Rules</source>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="253"/>
+ <source>Hits</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1837"/>
+ <source>Save as CSV</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="738"/>
+ <source>Delete</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source>always</source>
+ <translation type="obsolete">siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="580"/>
+ <source><b>Error:</b><br><br>{0}</source>
+ <translation type="obsolete"><b>Error:</b><br><br>{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="731"/>
+ <source>Disable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="733"/>
+ <source>Enable</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Duplicate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="737"/>
+ <source>Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="891"/>
+ <source>Rule not found by that name and node</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="921"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="928"/>
+ <source>Warning:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="717"/>
+ <source>Allow</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="718"/>
+ <source>Deny</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="722"/>
+ <source>Always</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="723"/>
+ <source>Until reboot</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1244"/>
+ <source> You are about to delete this rule. </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="174"/>
+ <source>LastConnection</source>
+ <translation type="obsolete">ÚltimaConexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">ÚltimaConexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="392"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="379"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="380"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="390"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="395"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="396"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="391"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="393"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="377"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="175"/>
+ <source>Addr</source>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="181"/>
+ <source>Connections</source>
+ <translation type="obsolete">Conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="182"/>
+ <source>Dropped</source>
+ <translation type="obsolete">Rechazadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="252"/>
+ <source>What</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="709"/>
+ <source>Apply to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="719"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="378"/>
+ <source>Addr</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Adr.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="382"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Oppetid</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Forbindelser</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Droppet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Hva</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="394"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Prosedyre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="644"/>
+ <source>New node connected</source>
+ <translation>Koblet opp ny node</translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="774"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="obsolete"> Estás a punto de borrar esta regla. </translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="776"/>
+ <source> Are you sure?</source>
+ <translation type="obsolete"> ¿Estás seguro?</translation>
+ </message>
+</context>
+<context>
+ <name>stats_disabled</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="74"/>
+ <source>Disabled</source>
+ <translation type="obsolete">Deshabilitado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_notrunning</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="73"/>
+ <source>Not running</source>
+ <translation type="obsolete">Parado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_running</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="75"/>
+ <source>Running</source>
+ <translation type="obsolete">Interceptando</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="409"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de red OpenSnitch</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="411"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="pt_BR">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>opensnitch-qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>ID do usuário</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-weight:600;">Executado de</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation>TextLabel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>IP de origem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>ID de processo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>IP de destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>Porta Dst</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="226"/>
+ <source>(/path/to/bin/chromium)</source>
+ <translation type="obsolete">(/caminho/para/bin/chromium)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="271"/>
+ <source>Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</source>
+ <translation type="obsolete">O navegador da Web Chromium deseja se conectar a www.evilsocket.net na porta tcp 443. E talvez a www.goodsocket.net na porta 344</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>a partir deste executável</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>a partir desta linha de comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>esta porta de destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>este usuário</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>este ip de destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation>uma vez</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="706"/>
+ <source>for this session</source>
+ <translation type="obsolete">para esta sessão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation>para sempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation>Negar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation>até reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation>a partir desse PID</translation>
+ </message>
+</context>
+<context>
+ <name>New node connected</name>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Preferências</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation>UI</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="54"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Este tempo limite é a contagem regressiva que você vê quando uma caixa de diálogo pop-up é exibida.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation>Tempo limite padrão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation>Duração padrão do pop-up</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="754"/>
+ <source>Default duration</source>
+ <translation>Duração padrão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">Ação padrão de pop-up</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">Ação padrão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation>Alvo padrão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation>centro</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation>superior direito</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation>inferior direito</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation>superior esquerdo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation>inferior esquerdo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="167"/>
+ <source>Prompt dialog default position on screen</source>
+ <translation type="obsolete">Posição padrão da caixa de diálogo de prompt na tela</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation>por executável</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation>por linha de comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation>por porta de destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation>por ip de destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation>por id de usuário</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="863"/>
+ <source>once</source>
+ <translation>uma vez</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>for this session</source>
+ <translation type="obsolete">para esta sessão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation>para sempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="905"/>
+ <source>deny</source>
+ <translation>negar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="914"/>
+ <source>allow</source>
+ <translation>permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="411"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">Desativar pop-ups, exibir apenas um alerta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="711"/>
+ <source>Nodes</source>
+ <translation>Nodes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="717"/>
+ <source>Process monitor method</source>
+ <translation>Método de monitoramento de processo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="751"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>A duração padrão ocorrerá quando não houver interface do usuário conectada.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>Endereço do node</p><p>Padrão: unix:///tmp/osui.sock (unix:// é obrigatório se for um soquete Unix)</p><p>Também pode ser um endereço IP com a porta: 127.0.0.1:50051</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="884"/>
+ <source>Address</source>
+ <translation>Endereço</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1024"/>
+ <source>Default log level</source>
+ <translation>Nível de registro padrão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>Version</source>
+ <translation>Versão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>A ação padrão ocorrerá quando não houver interface do usuário conectada.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>audit</source>
+ <translation type="obsolete">auditar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>Arquivo de log para gravar logs.<br/></p><p>/dev/stdout irá imprimir registros na saída padrão.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="737"/>
+ <source>Log file</source>
+ <translation>Arquivo de log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="788"/>
+ <source>IMPORTANT</source>
+ <translation type="obsolete">IMPORTANTE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>WARNING</source>
+ <translation type="obsolete">ADVERTÊNCIA</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="798"/>
+ <source>ERROR</source>
+ <translation type="obsolete">ERRO</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Se marcado, o opensnitch solicitará que você permita ou negue conexões que não tenham um PID associado, devido a vários motivos.&lt;/p&gt;&lt;p&gt;A caixa de diálogo pop-up conterá apenas informações sobre a conexão de rede.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">Interceptar conexões desconhecidas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="809"/>
+ <source>HostName</source>
+ <translation>HostName</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="983"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation>unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="868"/>
+ <source>until restart</source>
+ <translation>até reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="873"/>
+ <source>always</source>
+ <translation>sempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="995"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation>/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1000"/>
+ <source>/dev/stdout</source>
+ <translation>/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="767"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>Aplicar configuração a todos os nodes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1039"/>
+ <source>Database</source>
+ <translation>Base de dados</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="630"/>
+ <source>Database name</source>
+ <translation type="obsolete">Nome do banco de dados</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1074"/>
+ <source>In memory</source>
+ <translation>Na memória</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1079"/>
+ <source>File</source>
+ <translation>Arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="669"/>
+ <source>/path/to/the/file.db</source>
+ <translation type="obsolete">/caminho/para/o/arquivo.db</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1345"/>
+ <source>Close</source>
+ <translation>Fechar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1356"/>
+ <source>Apply</source>
+ <translation>Aplicar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1367"/>
+ <source>Save</source>
+ <translation>Salvar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation>até reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1093"/>
+ <source>Database type</source>
+ <translation>Tipo de banco de dados</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1100"/>
+ <source>Select</source>
+ <translation>Selecionar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Configure the</source>
+ <translation type="obsolete">Configure o</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Pop-ups default options</source>
+ <translation type="obsolete">Opções padrão de pop-ups</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="367"/>
+ <source>Pop-ups default position on screen</source>
+ <translation type="obsolete">Posição padrão dos pop-ups na tela</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="105"/>
+ <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>A visualização avançada permite que você aplique mais filtros em uma conexão</p><p>quando um pop-up aparece.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation>Mostrar visualização avançada por padrão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="665"/>
+ <source>Action</source>
+ <translation>Ação</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation><html><head/><body><p>Se marcado, os pop-ups serão exibidos com a visualização avançada ativa.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation>Duração</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation><html><head/><body><p>Por padrão, quando um novo pop-up aparece, em sua forma mais simples, você será capaz de filtrar conexões ou aplicativos por uma propriedade da conexão (executável, porta, IP, etc).</p><p>Com essas opções, você pode escolher vários campos para filtrar conexões para.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation>Filtre as conexões também por:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="365"/>
+ <source>If checked, this field will be checked when a pop-up is displayed</source>
+ <translation type="obsolete">Se marcado, este campo será verificado quando um pop-up for exibido</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation>ID do usuário</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation>Porta de destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation>IP de destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation><html><head/><body><p>Este tempo limite é a contagem regressiva que você vê quando uma caixa de diálogo pop-up é exibida.</p><p>Se o pop-up não for respondido, as opções padrão serão aplicadas.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation>A visualização avançada permite que você selecione facilmente vários campos para filtrar conexões</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation>Se marcado, este campo será selecionado quando um pop-up for exibido</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation><html><head/><body><p>Ação padrão de pop-up.</p><p>Quando uma nova conexão de saída está prestes a ser estabelecida, esta ação será selecionada por padrão, então se o tempo limite disparar, esta é a opção que será aplicada.</p><p><br/></p><p>Enquanto um pop-up pede ao usuário para permitir ou negar uma conexão:</p><p>1. novas conexões de saída são negadas.</p><p>2. conexões conhecidas são permitidas ou negadas com base nas regras definidas pelo usuário.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="793"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>Ação padrão quando a GUI é desconectada</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="894"/>
+ <source>Debug invalid connections</source>
+ <translation>Depurar conexões inválidas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation>Pop-ups</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation>Opções padrão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation>Posição padrão na tela</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="479"/>
+ <source>any temporary rules</source>
+ <translation>quaisquer regras temporárias</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="492"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation><html><head/><body><p>Quando esta opção é selecionada, as regras da duração selecionada não serão adicionadas à lista de regras temporárias na GUI.</p><p><br/></p><p>As regras temporárias ainda serão válidas e você pode usá-las quando solicitado a permitir/negar uma nova conexão.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="495"/>
+ <source>Don't save rules of duration</source>
+ <translation>Não salve regras de duração</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="601"/>
+ <source>Time</source>
+ <translation>Tempo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="681"/>
+ <source>Destination</source>
+ <translation>Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="649"/>
+ <source>Protocol</source>
+ <translation>Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="697"/>
+ <source>Process</source>
+ <translation>Processo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="617"/>
+ <source>Rule</source>
+ <translation>Regra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="633"/>
+ <source>Node</source>
+ <translation>Node</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="723"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source>
+ <translation type="obsolete">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Se marcado, o opensnitch solicitará que você permita ou negue conexões que não tenham um PID asocciado, devido a vários motivos, principalmente devido a conexões de mau estado.&lt;/p&gt;&lt;p&gt;A caixa de diálogo pop-up conterá apenas informações sobre a conexão de rede.&lt;/p&gt;&lt;p&gt;Existem alguns cenários em que essas conexões são válidas, como ao estabelecer uma VPN usando wireguard.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="589"/>
+ <source>Events tab columns</source>
+ <translation>Colunas da guia de eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation>por PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation><html><head/><body><p>Se marcado, o OpenSnitch solicitará que você permita ou negue conexões que não tenham um PID associado, devido a vários motivos, principalmente devido a conexões ruins.</p><p>A caixa de diálogo pop-up conterá apenas informações sobre a conexão de rede.</p><p>Existem alguns cenários em que essas conexões são válidas, como ao estabelecer uma VPN usando o WireGuard.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation>Desativar pop-ups, exibir apenas uma notificação</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="508"/>
+ <source>Desktop notifications</source>
+ <translation>Notificações da área de trabalho</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="526"/>
+ <source>Use system notifications</source>
+ <translation>Usar notificações do sistema</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="542"/>
+ <source>Use Qt notifications</source>
+ <translation>Usar notificações do Qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="571"/>
+ <source>Test</source>
+ <translation>Testar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1178"/>
+ <source>minutes</source>
+ <translation>minutos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1204"/>
+ <source>Minutes between events purges</source>
+ <translation>Minutos entre expurgos de eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1227"/>
+ <source>days</source>
+ <translation>dias</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1237"/>
+ <source>Maximum days of events to keep</source>
+ <translation>Máximo de dias de eventos para manter</translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation>Detalhes do processo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation>carregando...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation>CWD: carregando...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation>estatísticas mem: carregando...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation>Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation>Abrir arquivos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation>Estatísticas de I/O</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation>Arquivos mapeados na memória</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation>Pilha</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation>Variáveis de ambiente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation>Aplicação pids</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation>Inicie ou pare de monitorar este processo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation>Fechar</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="14"/>
+ <source>Rule</source>
+ <translation>Regra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="118"/>
+ <source>Node</source>
+ <translation>Node</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="141"/>
+ <source>Apply rule to all nodes</source>
+ <translation>Aplicar regra a todos os nodes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/>
+ <source>From this command line</source>
+ <translation>Para esta linha de comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="206"/>
+ <source>From this executable</source>
+ <translation>Para este executável</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="751"/>
+ <source>Action</source>
+ <translation>Ação</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/>
+ <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source>
+ <translation type="obsolete">/caminho/para/o/executavel, .*/bin/executable[0-9\.]+$, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="383"/>
+ <source>To this IP / Network</source>
+ <translation>Para este IP / Rede</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="792"/>
+ <source>once</source>
+ <translation>uma vez</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="797"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="802"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="807"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="812"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="817"/>
+ <source>1h</source>
+ <translation>1h</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source>until restart</source>
+ <translation type="obsolete">até reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="827"/>
+ <source>always</source>
+ <translation>sempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="293"/>
+ <source>To this port</source>
+ <translation>Para esta porta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="169"/>
+ <source>From this user ID</source>
+ <translation>Para este ID de usuário</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="419"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation>Vírgulas ou espaços não podem especificar vários domínios.
+
+Em vez disso, use expressões regulares:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+ou um único domínio:
+www.gnu.org - só vai filtrar www.gnu.org, não filtrará ftp.gnu.org, nem www2.gnu.org, ...
+gnu.org - só vai filtrar gnu.org, não filtrará www.gnu.org, nem ftp.gnu.org, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="430"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation>www.dominio.org, .*\.dominio.org</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation><html><head/><body><p>Apenas TCP, UDP ou UDPLITE são permitidos</p><p>Você pode usar expressão regulares, ou seja: ^(TCP|UDP)$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/>
+ <source>TCP</source>
+ <translation>TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/>
+ <source>UDP</source>
+ <translation>UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/>
+ <source>UDPLITE</source>
+ <translation>UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>TCP6</source>
+ <translation>TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/>
+ <source>UDP6</source>
+ <translation>UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="358"/>
+ <source>UDPLITE6</source>
+ <translation>UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="440"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation>Você pode especificar um único IP:
+- 192.168.1.1
+
+ou uma expressão regular:
+- 192\.168\.1\.[0-9]+
+
+vários IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+Você também pode especificar uma sub-rede:
+- 192.168.1.0/24
+
+Nota: Vírgulas ou espaços não são permitidos para separar IPs ou redes.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="459"/>
+ <source>LAN</source>
+ <translation>LAN</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="464"/>
+ <source>127.0.0.0/8</source>
+ <translation>127.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="469"/>
+ <source>192.168.0.0/24</source>
+ <translation>192.168.0.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="474"/>
+ <source>192.168.1.0/24</source>
+ <translation>192.168.1.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="479"/>
+ <source>192.168.2.0/24</source>
+ <translation>192.168.2.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="484"/>
+ <source>192.168.0.0/16</source>
+ <translation>192.168.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="489"/>
+ <source>169.254.0.0/16</source>
+ <translation>169.254.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/>
+ <source>172.16.0.0/12</source>
+ <translation>172.16.0.0/12</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="499"/>
+ <source>10.0.0.0/8</source>
+ <translation>10.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="504"/>
+ <source>::1/128</source>
+ <translation>::1/128</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="509"/>
+ <source>fc00::/7</source>
+ <translation>fc00::/7</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/>
+ <source>ff00::/8</source>
+ <translation>ff00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="519"/>
+ <source>fe80::/10</source>
+ <translation>fe80::/10</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="524"/>
+ <source>fd00::/8</source>
+ <translation>fd00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="784"/>
+ <source>Duration</source>
+ <translation>Duração</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="376"/>
+ <source>Protocol</source>
+ <translation>Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="300"/>
+ <source>To this host</source>
+ <translation>Para este host</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="843"/>
+ <source>Deny</source>
+ <translation>Negar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="877"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Name</source>
+ <translation>Nome</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/>
+ <source>Enable</source>
+ <translation>Habilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="706"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation>As regras são verificadas em ordem alfabética, para que você possa nomeá-las de acordo para priorizá-las.
+
+000-permitir-localhost
+001-negar-transmissão
+...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="713"/>
+ <source>leave blank to autocreate</source>
+ <translation>deixe em branco para criar automaticamente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="46"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation>Se marcada, esta regra terá precedência sobre o resto das regras. Nenhuma outra regra será verificada após esta.
+
+Você deve nomear a regra de forma que ela seja verificada primeiro, porque eles são verificados em ordem alfabética. Por exemplo:
+
+[x] Prioridade - 000-regra-prioritaria
+[ ] Prioridade - 001-regra-menos-prioritaria</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="54"/>
+ <source>Priority rule</source>
+ <translation>Regra de prioridade</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="74"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation><html><head/><body><p>Por padrão, o campo das regras não diferencia maiúsculas de minúsculas, ou seja, se um processo tentar acessar gOOgle.CoM e você tiver uma regra para Negar. *Google.com, a conexão será bloqueada.<br/></p><p>Se você marcar esta caixa, deverá especificar a string exata (domínio, executável, linha de comando) que deseja filtrar.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="77"/>
+ <source>Case-sensitive</source>
+ <translation>Sensível a maiúsculas e minúsculas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="369"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation><html><head/><body><p>Você pode especificar várias portas usando expressões regulares:</p><p><br/></p><p>- 53, 80 o 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 o 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="822"/>
+ <source>until reboot</source>
+ <translation>até reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="583"/>
+ <source>To this list of domains</source>
+ <translation>Para esta lista de domínios</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Selecione um diretório com listas de domínios para bloquear ou permitir.</p><p>Coloque dentro desse diretório arquivos com qualquer extensão que contenha listas de domínios.</p><p><br/>O formato de cada entrada de uma lista é o seguinte (formato de hosts):</p><p>127.0.0.1 www.dominio.com</p><p>ou </p><p>0.0.0.0 www.dominio.com</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="163"/>
+ <source>Applications</source>
+ <translation>Aplicativos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="216"/>
+ <source><html><head/><body><p>This field will only match the executable path. It is not modifiable by the user.<br/></p><p>You can use regular expressions to deny executions from /tmp for example:<br/></p><p>^/tmp/.*$</p></body></html></source>
+ <translation><html><head/><body><p>Este campo irá corresponder apenas ao caminho do executável. Não é modificável pelo usuário.<br/></p><p>Você pode usar expressões regulares para negar execuções de /tmp, por exemplo:<br/></p><p>^/tmp/.*$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation><html><head/><body><p>Este campo irá conter e corresponder à linha de comando que foi executada pelo usuário.<br/></p><p>Se o usuário digitou o comando, apenas o comando aparecerá:</p><p>telnet 1.2.3.4<br/></p><p>Se o usuário digitou o caminho absoluto ou relativo para o comando, é isso que aparecerá:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="246"/>
+ <source>From this PID</source>
+ <translation>A partir deste PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="287"/>
+ <source>Network</source>
+ <translation>Rede</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="536"/>
+ <source>List of domains/IPs</source>
+ <translation>Lista de domínios/IPs</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>To this list of network ranges</source>
+ <translation>Para esta lista de intervalos de rede</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="549"/>
+ <source>To this list of IPs</source>
+ <translation>Para esta lista de IPs</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="574"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Selecione um diretório com arquivos contendo uma lista de IPs para bloquear ou permitir:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>Um IP por linha. Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="608"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Selecione um diretório com arquivos contendo uma lista de intervalos de rede para bloquear ou permitir:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>Um intervalo de rede por linha. Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="636"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Selecione um diretório com listas de domínios para bloquear ou permitir.</p><p>Coloque dentro desse diretório arquivos com qualquer extensão contendo listas de domínios.</p><p><br/>O formato de cada entrada de uma lista é o seguinte (formato de hosts):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="651"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation>Para esta lista de domínios
+(expressões regulares)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="677"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Selecione um diretório com arquivos contendo expressões regulares de domínios para bloquear ou permitir:</p><p>.*\.example\.com</p><p>Você também pode usar um domínio como: &quot;example.com&quot; , e vai combinar whatever.example.com, whatever.example.com.localdomain, etc.</p><p>Um domínio por linha. Linhas vazias ou iniciadas com # são ignoradas.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="857"/>
+ <source>Reject</source>
+ <translation>Rejeitar</translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>Estatísticas da rede OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="290"/>
+ <source>Save to CSV.</source>
+ <translation>Salvar em CSV.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="300"/>
+ <source>Ctrl+S</source>
+ <translation>Ctrl+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="351"/>
+ <source>Create a new rule</source>
+ <translation>Crie uma nova regra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="381"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="420"/>
+ <source>Status</source>
+ <translation>Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1668"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="464"/>
+ <source>Start or Stop interception</source>
+ <translation>Iniciar ou parar a interceptação</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="509"/>
+ <source>Events</source>
+ <translation>Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation>Filtro</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation>Negar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation>Ex.: firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="751"/>
+ <source>Nodes</source>
+ <translation>Nodes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes na coluna endereço para ver os detalhes de um node)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1572"/>
+ <source>Rules</source>
+ <translation>Regras</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="860"/>
+ <source>enable</source>
+ <translation>habilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="674"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes na coluna Nome para ver os detalhes de uma regra)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation type="obsolete">nome da regra de pesquisa</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="707"/>
+ <source>Application rules</source>
+ <translation>Regras de aplicação</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="809"/>
+ <source>Permanent</source>
+ <translation>Permanente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="818"/>
+ <source>Temporary</source>
+ <translation>Temporário</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="936"/>
+ <source>Hosts</source>
+ <translation>Hosts</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes para ver os detalhes de um item)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1023"/>
+ <source>Applications</source>
+ <translation>Aplicativos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1130"/>
+ <source>Addresses</source>
+ <translation>Endereços</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1217"/>
+ <source>Ports</source>
+ <translation>Portas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1301"/>
+ <source>Users</source>
+ <translation>Usuários</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1407"/>
+ <source>Connections</source>
+ <translation>Conexões</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1462"/>
+ <source>Dropped</source>
+ <translation>Dropado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1517"/>
+ <source>Uptime</source>
+ <translation>Tempo de atividade</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1642"/>
+ <source>Version</source>
+ <translation>Versão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation>Excluir todos os eventos interceptados</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="867"/>
+ <source>Edit rule</source>
+ <translation>Editar regra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="881"/>
+ <source>Delete rule</source>
+ <translation>Excluir regra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation type="obsolete">Excluir todos os hosts interceptadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation type="obsolete">Excluir todos os aplicativos interceptadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation type="obsolete">Excluir todos os endereços interceptados</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation type="obsolete">Excluir todas as portas interceptadas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation type="obsolete">Excluir todos os usuários interceptados</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(clique duas vezes em uma linha para ver os detalhes de uma regra)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="915"/>
+ <source>Delete connections that matched this rule</source>
+ <translation>Exclua conexões que correspondam a esta regra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="800"/>
+ <source>All applications</source>
+ <translation>Todos os aplicativos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation>Rejeitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation>0</translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation>Estatísticas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation>Ajuda</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation>Fechar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation>Habilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation>Desabilitar</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="518"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation>As notificações do sistema não estão disponíveis, você precisa instalar o python3-notify2.</translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation>até reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation>para sempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation>Negar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/>
+ <source>Unknown process</source>
+ <translation type="obsolete">Processo desconhecido</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation>Conexão de saída</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation>Processo lançado de:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation>a partir deste executável</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation>a partir desta linha de comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation>para a porta {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation>para {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation>do usuário {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation>para {0}.*</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation>para *.{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation type="obsolete">para *{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation><b>Processo remoto</b> %s rodando em <b>%s</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation>está conectando a <b>%s</b> em %s na porta %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation>está tentando resolver <b>%s</b> via %s, %s porta %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation>a partir desse PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="108"/>
+ <source>New outgoing connection</source>
+ <translation>Nova conexão de saída</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="208"/>
+ <source>Server address can not be empty</source>
+ <translation>O endereço do servidor não pode estar vazio</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="448"/>
+ <source>Configuration applied.</source>
+ <translation>Configuração aplicada.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="299"/>
+ <source>Exception saving config: {0}</source>
+ <translation>Configuração de salvamento de exceção: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="389"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation>Aplicando configuração em {0} ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="238"/>
+ <source>Error loading {0} configuration</source>
+ <translation>Erro ao carregar configuração de {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="450"/>
+ <source>Error applying configuration: {0}</source>
+ <translation>Erro ao aplicar configuração: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>Warning</source>
+ <translation>Aviso</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="323"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation>Você deve selecionar um arquivo para o banco de dados&lt;br&gt;ou escolher o tipo &quot;Na memória&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>DB type changed</source>
+ <translation>Tipo de banco de dados alterado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="329"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation>Reinicie a GUI para que os efeitos tenham efeito</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="480"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation>Passe o mouse sobre os textos para exibir a ajuda&lt;br&gt;&lt;br&gt;Não se esqueça de visitar a wiki: &lt;a href=&quot;{0}&quot;&gt;{0}&lt;/a&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation><b>Erro ao carregar as informações do processo:</b> <br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation><b>Erro ao parar o processo de monitoramento:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation>carregando...</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="162"/>
+ <source>There're no nodes connected.</source>
+ <translation>Não há nodes conectados.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="203"/>
+ <source>Rule applied.</source>
+ <translation>Regra aplicada.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="527"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation>protocolo não pode estar vazio ou desmarque-o</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="541"/>
+ <source>Protocol regexp error</source>
+ <translation>Erro de expressão de protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="545"/>
+ <source>process path can not be empty</source>
+ <translation>o caminho do processo não pode estar vazio</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="559"/>
+ <source>Process path regexp error</source>
+ <translation>Erro de expressão regular do caminho do processo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="563"/>
+ <source>command line can not be empty</source>
+ <translation>a linha de comando não pode estar vazia</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="577"/>
+ <source>Command line regexp error</source>
+ <translation>Erro de expressão regular da linha de comando</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="581"/>
+ <source>Dest port can not be empty</source>
+ <translation>A porta de destino não pode estar vazia</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="595"/>
+ <source>Dst port regexp error</source>
+ <translation>Erro de expressão regular da porta Dst</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="599"/>
+ <source>Dest host can not be empty</source>
+ <translation>Dest host não pode estar vazio</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="613"/>
+ <source>Dst host regexp error</source>
+ <translation>Erro de expressão regular do host Dst</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="617"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>O IP/rede de destino não pode estar vazio</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="639"/>
+ <source>Dst IP regexp error</source>
+ <translation>Erro de expressão regular de IP Dst</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="651"/>
+ <source>User ID can not be empty</source>
+ <translation>O ID do usuário não pode estar vazio</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="665"/>
+ <source>User ID regexp error</source>
+ <translation>Erro de expressão regular do ID do usuário</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Erro ao aplicar regra: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="777"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>Regra não suportada</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="739"/>
+ <source>Lists field cannot be empty</source>
+ <translation>O campo de listas não pode estar vazio</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="741"/>
+ <source>Lists field must be a directory</source>
+ <translation>O campo de listas deve ser um diretório</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="429"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Erro ao carregar regra</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="179"/>
+ <source>There's already a rule with this name.</source>
+ <translation>Já existe uma regra com este nome.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="669"/>
+ <source>PID field can not be empty</source>
+ <translation>O campo PID não pode ficar vazio</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="683"/>
+ <source>PID field regexp error</source>
+ <translation>Erro de expressão regular do campo PID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="764"/>
+ <source>Select at least one field.</source>
+ <translation>Selecione pelo menos um campo.</translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Not running</source>
+ <translation>Desativado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Disabled</source>
+ <translation>Desativado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Running</source>
+ <translation>Ativo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="886"/>
+ <source> Your are about to delete this rule. </source>
+ <translation> Você está prestes a excluir esta regra. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1258"/>
+ <source> Are you sure?</source>
+ <translation> Você tem certeza?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="572"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation>Estatísticas da rede OpenSnitch {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="574"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation>Estatísticas da rede OpenSnitch para {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">Nome</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">Endereço</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="176"/>
+ <source>Status</source>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="183"/>
+ <source>Version</source>
+ <translation type="obsolete">Versão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="180"/>
+ <source>Rules</source>
+ <translation type="obsolete">Regras</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">Tempo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">Ação</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">Duração</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="24"/>
+ <source>Hits</source>
+ <translation>Acertos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1850"/>
+ <source>Save as CSV</source>
+ <translation>Salvar como CSV</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">Ativado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="752"/>
+ <source>Delete</source>
+ <translation>Deletar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="580"/>
+ <source><b>Error:</b><br><br>{0}</source>
+ <translation type="obsolete">&lt;b&gt;Erro:&lt;/b&gt;&lt;br&gt;&lt;br&gt;{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="745"/>
+ <source>Disable</source>
+ <translation>Desabilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="747"/>
+ <source>Enable</source>
+ <translation>Habilitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="750"/>
+ <source>Duplicate</source>
+ <translation>Duplicado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="751"/>
+ <source>Edit</source>
+ <translation>Editar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="905"/>
+ <source>Rule not found by that name and node</source>
+ <translation>Regra não encontrada por esse nome e node</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="935"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation><b>Erro:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="942"/>
+ <source>Warning:</source>
+ <translation>Atenção:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="731"/>
+ <source>Allow</source>
+ <translation>Permitir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="732"/>
+ <source>Deny</source>
+ <translation>Negar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Always</source>
+ <translation>Sempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="737"/>
+ <source>Until reboot</source>
+ <translation>Até reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1258"/>
+ <source> You are about to delete this rule. </source>
+ <translation> Você está prestes a excluir esta regra. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <translation type="obsolete">Regra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">Nome</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nome</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Endereço</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Versão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regras</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Tempo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Ação</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Duração</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Ativado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Acertos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="281"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Nome</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="282"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Endereço</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="283"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="284"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="382"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Versão</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="379"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Regras</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Tempo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="289"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Ação</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="290"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Duração</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="291"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Node</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="292"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="401"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Acessos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Processo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Regra</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>UltimaConexao</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Args</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DstIP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DstHost</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>TempoAtividade</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="179"/>
+ <source>Uptime</source>
+ <translation type="obsolete">Tempo de atividade</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="181"/>
+ <source>Connections</source>
+ <translation type="obsolete">Conexões</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="182"/>
+ <source>Dropped</source>
+ <translation type="obsolete">Dropado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="23"/>
+ <source>What</source>
+ <translation>Qual</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="733"/>
+ <source>Reject</source>
+ <translation>Rejeitar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="723"/>
+ <source>Apply to</source>
+ <translation>Aplicar para</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation>Nome da rede</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="285"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Tempo de atividade</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="380"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Conexoes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="381"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Dropado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="400"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Qual</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Precedencia</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="644"/>
+ <source>New node connected</source>
+ <translation>Novo node conectado</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="ro_RO">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>opensnitch-qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>din acest executabil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>din această linie de comandă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>acest port de destinație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>acest utilizator</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>această adresă IP de destinație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="842"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="723"/>
+ <source>once</source>
+ <translation>o dată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>1h</source>
+ <translation>1o</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>until reboot</source>
+ <translation>până la repornire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>forever</source>
+ <translation>mereu</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="784"/>
+ <source>Deny</source>
+ <translation>Refuză</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="813"/>
+ <source>Allow</source>
+ <translation>Permite</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>ID utilizator</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-weight:600;">Executat din</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation>EtichetăText</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>Adresă IP sursă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>ID proces</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>Adresă IP destinație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>Port destinație</translation>
+ </message>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Preferințe</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="417"/>
+ <source>UI</source>
+ <translation>Interfață utilizator</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="361"/>
+ <source>Show advanced view by default</source>
+ <translation>Arată implicit vizualizarea avansată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="695"/>
+ <source>once</source>
+ <translation>o dată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="226"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="231"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="236"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="241"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="246"/>
+ <source>1h</source>
+ <translation>1o</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="251"/>
+ <source>until reboot</source>
+ <translation>până la repornire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="256"/>
+ <source>forever</source>
+ <translation>mereu</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="526"/>
+ <source>Action</source>
+ <translation>Acțiune</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="325"/>
+ <source>Default target</source>
+ <translation>Țintă implicită</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="377"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation><html><head/><body><p>Dacă este bifată, ferestrele de notificare care apar vor fi afișate cu vizualizarea avansată activă.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="737"/>
+ <source>deny</source>
+ <translation>refuză</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="746"/>
+ <source>allow</source>
+ <translation>permite</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="290"/>
+ <source>by executable</source>
+ <translation>după executabil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="295"/>
+ <source>by command line</source>
+ <translation>după linia de comandă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="300"/>
+ <source>by destination port</source>
+ <translation>după portul de destinație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="305"/>
+ <source>by destination ip</source>
+ <translation>după adresa IP de destinație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="310"/>
+ <source>by user id</source>
+ <translation>după identificatorul utilizatorului</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="186"/>
+ <source>center</source>
+ <translation>centru</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="191"/>
+ <source>top right</source>
+ <translation>sus la dreapta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="196"/>
+ <source>bottom right</source>
+ <translation>jos la dreapta</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="201"/>
+ <source>top left</source>
+ <translation>sus la stânga</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="206"/>
+ <source>bottom left</source>
+ <translation>jos la stânga</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="342"/>
+ <source>Pop-up default duration</source>
+ <translation>Durată implicită fereastră de notificare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="345"/>
+ <source>Duration</source>
+ <translation>Durată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="270"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="273"/>
+ <source>Filter connections also by:</source>
+ <translation>Filtrează conexiunile și după:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>User ID</source>
+ <translation>ID utilizator</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination port</source>
+ <translation>Port destinație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="129"/>
+ <source>Destination IP</source>
+ <translation>Adresă IP destinație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation>Dezactivează ferestrele de notificare, arată doar o alertă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="396"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="399"/>
+ <source>Default timeout</source>
+ <translation>Durată implicită pentru alegere</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="540"/>
+ <source>Nodes</source>
+ <translation>Noduri</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="546"/>
+ <source>Process monitor method</source>
+ <translation>Metodă monitorizare procese</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="563"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>Fișierul jurnal unde să se scrie jurnalizări.<br/></p><p>/dev/stdout va tipări jurnalizările pe ieșirea standard.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="566"/>
+ <source>Log file</source>
+ <translation>Fișier de jurnalizare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="580"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Durata implicită va fi folosită când nu este conectată nicio interfață grafică.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="583"/>
+ <source>Default duration</source>
+ <translation>Durată implicită</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="596"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>Aplică configurația la toate nodurile</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="619"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Acțiunea implicită va fi folosită când nu este conectată nicio interfață grafică.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="638"/>
+ <source>HostName</source>
+ <translation>NumeGazdă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="700"/>
+ <source>until restart</source>
+ <translation>până la repornire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="705"/>
+ <source>always</source>
+ <translation>întotdeauna</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="713"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="716"/>
+ <source>Address</source>
+ <translation>Adresă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="764"/>
+ <source>Version</source>
+ <translation>Versiune</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="815"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation>unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="827"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation>/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="832"/>
+ <source>/dev/stdout</source>
+ <translation>/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="856"/>
+ <source>Default log level</source>
+ <translation>Nivel implicit de jurnalizare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="871"/>
+ <source>Database</source>
+ <translation>Bază de date</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="918"/>
+ <source>Database type</source>
+ <translation>Tip bază de date</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="925"/>
+ <source>Select</source>
+ <translation>Selectare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="946"/>
+ <source>In memory</source>
+ <translation>În memorie</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="951"/>
+ <source>File</source>
+ <translation>Fișier</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1008"/>
+ <source>Close</source>
+ <translation>Închide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1019"/>
+ <source>Apply</source>
+ <translation>Aplică</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1030"/>
+ <source>Save</source>
+ <translation>Salvează</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="358"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation>Vizualizarea avansată vă permite să selectați cu ușurință câmpuri multiple pentru a filtra conexiunile</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="126"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation>Dacă este bifată, acest câmp va fi selectat când o fereastră de notificare este afișată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="166"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="622"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>Acțiune implicită când interfața grafică cu utilizatorul este deconectată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="726"/>
+ <source>Debug invalid connections</source>
+ <translation>Depanează conexiunile nevalide</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation>Ferestre de notificare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="80"/>
+ <source>Default options</source>
+ <translation>Opțiuni implicite</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="332"/>
+ <source>Default position on screen</source>
+ <translation>Compozziția implicită pe ecran</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="437"/>
+ <source>any temporary rules</source>
+ <translation>oricare regulă temporară</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="450"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="453"/>
+ <source>Don't save rules of duration</source>
+ <translation>Nu salva regulile de durată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="466"/>
+ <source>Time</source>
+ <translation>Timp</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="476"/>
+ <source>Destination</source>
+ <translation>Destinație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="486"/>
+ <source>Protocol</source>
+ <translation>Protocol</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="496"/>
+ <source>Process</source>
+ <translation>Proces</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="506"/>
+ <source>Rule</source>
+ <translation>Regulă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="516"/>
+ <source>Node</source>
+ <translation>Nod</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="723"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source>
+ <translation><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="460"/>
+ <source>Events tab columns</source>
+ <translation>Coloane etichete evenimente</translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation>Detalii proces</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation>Se încarcă...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation>CWD: loading...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation>Statistici memorie: se încarcă...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation>Stare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation>Deschidere fișiere</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation>Statistici intrare/ieșire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation>Fișiere cartografiate în memorie</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation>Stivă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation>Variabile de mediu</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation>Identificatori procese aplicație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation>Pornește sau oprește monitorizarea acestui proces</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation>Închide</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="14"/>
+ <source>Rule</source>
+ <translation>Regulă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="22"/>
+ <source>Node</source>
+ <translation>Nod</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="45"/>
+ <source>Apply rule to all nodes</source>
+ <translation>Aplică regula la toate nodurile</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="115"/>
+ <source>To this IP / Network</source>
+ <translation>Pentru această adresă IP / rețea</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/>
+ <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source>
+ <translation>/cale/către/executabil, .*/bin/executabil[0-9\.]+$, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="158"/>
+ <source>Action</source>
+ <translation>Acțiune</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/>
+ <source>To this port</source>
+ <translation>Pentru acest port</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="172"/>
+ <source>To this list of domains</source>
+ <translation>Pentru această listă de domenii</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="195"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="214"/>
+ <source>LAN</source>
+ <translation>Rețea locală (LAN)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="219"/>
+ <source>127.0.0.0/8</source>
+ <translation>127.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="224"/>
+ <source>192.168.0.0/24</source>
+ <translation>192.168.0.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="229"/>
+ <source>192.168.1.0/24</source>
+ <translation>192.168.1.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="234"/>
+ <source>192.168.2.0/24</source>
+ <translation>192.168.2.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="239"/>
+ <source>192.168.0.0/16</source>
+ <translation>192.168.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="244"/>
+ <source>169.254.0.0/16</source>
+ <translation>169.254.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="249"/>
+ <source>172.16.0.0/12</source>
+ <translation>172.16.0.0/12</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/>
+ <source>10.0.0.0/8</source>
+ <translation>10.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="259"/>
+ <source>::1/128</source>
+ <translation>::1/128</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/>
+ <source>fc00::/7</source>
+ <translation>fc00::/7</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="269"/>
+ <source>ff00::/8</source>
+ <translation>ff00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="274"/>
+ <source>fe80::/10</source>
+ <translation>fe80::/10</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="279"/>
+ <source>fd00::/8</source>
+ <translation>fd00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="310"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="318"/>
+ <source>once</source>
+ <translation>o dată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="323"/>
+ <source>30s</source>
+ <translation>30s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="328"/>
+ <source>5m</source>
+ <translation>5m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="333"/>
+ <source>15m</source>
+ <translation>15m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="338"/>
+ <source>30m</source>
+ <translation>30m</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="343"/>
+ <source>1h</source>
+ <translation>1o</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>until reboot</source>
+ <translation>până la repornire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="353"/>
+ <source>always</source>
+ <translation>întotdeauna</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="364"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="375"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation>www.domeniu.org, .*\.domeniu.org</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="382"/>
+ <source>To this host</source>
+ <translation>Pentru această gazdă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="396"/>
+ <source>Duration</source>
+ <translation>Durată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="406"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/>
+ <source>TCP</source>
+ <translation>TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/>
+ <source>UDP</source>
+ <translation>UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/>
+ <source>UDPLITE</source>
+ <translation>UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/>
+ <source>TCP6</source>
+ <translation>TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="436"/>
+ <source>UDP6</source>
+ <translation>UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="441"/>
+ <source>UDPLITE6</source>
+ <translation>UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="449"/>
+ <source>Protocol</source>
+ <translation>Protocol</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="456"/>
+ <source>From this executable</source>
+ <translation>De la acest executabil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="471"/>
+ <source>Deny</source>
+ <translation>Refuză</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="494"/>
+ <source>Allow</source>
+ <translation>Permite</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="507"/>
+ <source>From this command line</source>
+ <translation>De la această linie de comandă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="514"/>
+ <source>From this user ID</source>
+ <translation>De la acest ID utilizator</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/>
+ <source>Name</source>
+ <translation>Nume</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="566"/>
+ <source>Enable</source>
+ <translation>Activează</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="604"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation>Regulile sunt verificate în ordine alfabetică, așa că puteți să le numiți ca atare pentru a le prioritiza.
+
+000-permite-localhost
+001-respinge-broadcast
+...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="611"/>
+ <source>leave blank to autocreate</source>
+ <translation>lăsați gol pentru creare automată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="620"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="628"/>
+ <source>Priority rule</source>
+ <translation>Regulă prioritate</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="648"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="651"/>
+ <source>Case-sensitive</source>
+ <translation>Sensibil la majuscule</translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>Statistici de rețea OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="105"/>
+ <source>Save to CSV.</source>
+ <translation>Salvează într-un fișier CSV.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="115"/>
+ <source>Ctrl+S</source>
+ <translation>Ctrl+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="166"/>
+ <source>Create a new rule</source>
+ <translation>Creare regulă nouă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="196"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="235"/>
+ <source>Status</source>
+ <translation>Stare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1697"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="279"/>
+ <source>Start or Stop interception</source>
+ <translation>Porniți sau opriți interceptarea</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="324"/>
+ <source>Events</source>
+ <translation>Evenimente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="344"/>
+ <source>Filter</source>
+ <translation>Filtru</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="357"/>
+ <source>Allow</source>
+ <translation>Permite</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="366"/>
+ <source>Deny</source>
+ <translation>Refuză</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="384"/>
+ <source>Ex.: firefox</source>
+ <translation>De exemplu: firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="411"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="416"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="421"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="426"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="439"/>
+ <source>Delete all intercepted events</source>
+ <translation>Șterge toate evenimentele de interceptare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="825"/>
+ <source>Nodes</source>
+ <translation>Noduri</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1601"/>
+ <source>Rules</source>
+ <translation>Reguli</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="610"/>
+ <source>enable</source>
+ <translation>Activează</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="617"/>
+ <source>Edit rule</source>
+ <translation>Editare regulă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="631"/>
+ <source>Delete rule</source>
+ <translation>Șterge regula</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:7pt;">(faceți clic dublu pe un rând pentru a vizualiza detaliile regulii)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation>Căutare nume regulă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="781"/>
+ <source>Application rules</source>
+ <translation>Reguli aplicație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="796"/>
+ <source>Permanent</source>
+ <translation type="unfinished">Permanent</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="810"/>
+ <source>Temporary</source>
+ <translation>Temporar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="876"/>
+ <source>Hosts</source>
+ <translation>Gazde</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:7pt;">(faceți clic dublu pentru a vizualiza detaliile unui element)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="984"/>
+ <source>Applications</source>
+ <translation>Aplicații</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation>Șterge toate aplicațiile interceptate</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1109"/>
+ <source>Addresses</source>
+ <translation>Adrese</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1211"/>
+ <source>Ports</source>
+ <translation>Porturi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1313"/>
+ <source>Users</source>
+ <translation>Utilizatori</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1436"/>
+ <source>Connections</source>
+ <translation>Conexiuni</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1491"/>
+ <source>Dropped</source>
+ <translation>Aruncate</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1546"/>
+ <source>Uptime</source>
+ <translation>Durată activitate</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1671"/>
+ <source>Version</source>
+ <translation>Versiune</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="665"/>
+ <source>Delete connections that matched this rule</source>
+ <translation>Șterge toate conexiunile care se potrivesc cu această regulă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="712"/>
+ <source>All applications</source>
+ <translation>Toate aplicațiile</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation>Șterge toate gazdele interceptate</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation>Șterge toate adresele interceptate</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation>Șterge toate porturile interceptate</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation>Șterge toți utilizatorii interceptați</translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="39"/>
+ <source>Statistics</source>
+ <translation>Statistici</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="40"/>
+ <source>Enable</source>
+ <translation>Activează</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="41"/>
+ <source>Disable</source>
+ <translation>Dezactivează</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="42"/>
+ <source>Help</source>
+ <translation>Ajutor</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Close</source>
+ <translation>Închide</translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="51"/>
+ <source>until reboot</source>
+ <translation>până la repornire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="53"/>
+ <source>forever</source>
+ <translation>mereu</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="89"/>
+ <source>Allow</source>
+ <translation>Permite</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="90"/>
+ <source>Deny</source>
+ <translation>Refuză</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="265"/>
+ <source>Outgoing connection</source>
+ <translation>Conexiune de ieșire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="270"/>
+ <source>Process launched from:</source>
+ <translation>Procesul a fost lansat din:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="299"/>
+ <source>from this executable</source>
+ <translation>de la acest executabil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="301"/>
+ <source>from this command line</source>
+ <translation>de la această linie de comandă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="305"/>
+ <source>to port {0}</source>
+ <translation>către portul {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="364"/>
+ <source>to {0}</source>
+ <translation>către {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="308"/>
+ <source>from user {0}</source>
+ <translation>de la utilizatorul {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>to {0}.*</source>
+ <translation>către {0}.*</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="374"/>
+ <source>to *.{0}</source>
+ <translation>către *.{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation>către *{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="411"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation>Procesul %s<b>telecomandat</b> rulează pe <b>%s</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="415"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation>se conectează la <b>%s</b> pe %s portul %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="421"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation>încearcă să rezolve <b>%s</b> prin %s, %s portul %d</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="260"/>
+ <source>Exception saving config: {0}</source>
+ <translation>Exception saving config: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="279"/>
+ <source>Warning</source>
+ <translation>Avertisment</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="279"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation>You must select a file for the database<br>or choose "In memory" type.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="285"/>
+ <source>DB type changed</source>
+ <translation>Tipul bazei de date a fost schimbat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="285"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation>Reporniți interfața grafică cu utilizatorul pentru ca modificările să aibă efect</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="340"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation>Se aplică configurația pe {0} ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="169"/>
+ <source>Server address can not be empty</source>
+ <translation>Adresa servitorului nu poate fi goală</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="199"/>
+ <source>Error loading {0} configuration</source>
+ <translation>Error loading {0} configuration</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="385"/>
+ <source>Configuration applied.</source>
+ <translation>Configurația a fost aplicată.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/>
+ <source>Error applying configuration: {0}</source>
+ <translation>Eroare la aplicarea configurației: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="416"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="96"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation><b>Eroare la încărcarea informațiilor procesului:</b> <br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="115"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation><b>Eroare la oprirea monitorizării procesului:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="155"/>
+ <source>loading...</source>
+ <translation>Se încarcă...</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="124"/>
+ <source>There're no nodes connected.</source>
+ <translation>Nu există niciun nod conectat.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="135"/>
+ <source>Rule applied.</source>
+ <translation>Regulă aplicată.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="137"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Eroare la aplicarea regulii: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="290"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Eroare la încărcarea regulii</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="395"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation>Protocolul nu poate fi gol, sau debifați-l</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="409"/>
+ <source>Protocol regexp error</source>
+ <translation>Protocol regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="413"/>
+ <source>process path can not be empty</source>
+ <translation>Calea procesului nu poate fi goală</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="427"/>
+ <source>Process path regexp error</source>
+ <translation>Process path regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="431"/>
+ <source>command line can not be empty</source>
+ <translation>Linia de comandă nu poate fi goală</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="445"/>
+ <source>Command line regexp error</source>
+ <translation>Command line regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="449"/>
+ <source>Dest port can not be empty</source>
+ <translation>Dest port can not be empty</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="463"/>
+ <source>Dst port regexp error</source>
+ <translation>Dst port regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="467"/>
+ <source>Dest host can not be empty</source>
+ <translation>Dest host can not be empty</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="481"/>
+ <source>Dst host regexp error</source>
+ <translation>Dst host regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="485"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>Dest IP/Network can not be empty</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="507"/>
+ <source>Dst IP regexp error</source>
+ <translation>Dst IP regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="519"/>
+ <source>User ID can not be empty</source>
+ <translation>User ID can not be empty</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="533"/>
+ <source>User ID regexp error</source>
+ <translation>User ID regexp error</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="537"/>
+ <source>Lists field cannot be empty</source>
+ <translation>Lists field cannot be empty</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="539"/>
+ <source>Lists field must be a directory</source>
+ <translation>Lists field must be a directory</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="573"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>Regula nu este sprijinită</b></translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="284"/>
+ <source>Not running</source>
+ <translation>Nu rulează</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="285"/>
+ <source>Disabled</source>
+ <translation>Dezactivată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="286"/>
+ <source>Running</source>
+ <translation>Rulează</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="475"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation>OpenSnitch Network Statistics {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="477"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation>OpenSnitch Network Statistics for {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="591"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation><b>Eroare:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="598"/>
+ <source>Warning:</source>
+ <translation>Avertisment:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="640"/>
+ <source>Allow</source>
+ <translation>Permite</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="641"/>
+ <source>Deny</source>
+ <translation>Refuză</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="644"/>
+ <source>Always</source>
+ <translation>Întotdeauna</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="645"/>
+ <source>Until reboot</source>
+ <translation>Până la repornire</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="653"/>
+ <source>Disable</source>
+ <translation>Dezactivează</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="655"/>
+ <source>Enable</source>
+ <translation>Activează</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="658"/>
+ <source>Duplicate</source>
+ <translation>Duplică</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="659"/>
+ <source>Edit</source>
+ <translation>Editare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="660"/>
+ <source>Delete</source>
+ <translation>Șterge</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="668"/>
+ <source> Your are about to delete this rule. </source>
+ <translation> Sunteți pe cale să ștergeți aceasă regulă. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1003"/>
+ <source> Are you sure?</source>
+ <translation> Sigur doriți acest lucru?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="786"/>
+ <source>Rule not found by that name and node</source>
+ <translation>Regula nu a putut fi găsită după acel nume și nod</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1003"/>
+ <source> You are about to delete this rule. </source>
+ <translation> Sunteți pe cale să ștergeți această regulă. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1490"/>
+ <source>Save as CSV</source>
+ <translation>Save as CSV</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="261"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Nume</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="262"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Adresă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="263"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Stare</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="264"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Nume gazdă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="265"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Versiune</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="266"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Reguli</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="267"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Timp</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="268"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Acțiune</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="269"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Durată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="270"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Nod</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="271"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Activată</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="272"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Atingeri</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="273"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Protocol</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="274"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Proces</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="276"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Destinație</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="280"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Regulă</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="281"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>IdentificatorUtilizator</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="282"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>UltimaConexiune</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="275"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Argumente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="277"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DstIP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="278"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DstHost</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="279"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>DstPort</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru_RU">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>opensnitch-qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>ID пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-weight:600;">Выполнено из</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation>Заметка</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>Исходный IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>ID процесса</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>IP назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>Порт назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>из этого исполняемого файла</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>из этой командной строки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>этот порт назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>этот пользователь</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>этот ip-адрес назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation>один раз</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation>30 секунд</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation>5 минут</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation>15 минут</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation>30 минут</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation>1 час</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="706"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation>постоянно</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation>Запретить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation>Разрешить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation>до перезагрузки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Настройки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation>Пользовательский интерфейс</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="54"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source>
+ <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation>Тайм-аут по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation>Продолжительность всплывающего окна по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="777"/>
+ <source>Default duration</source>
+ <translation>Продолжительность по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">Acción por defecto de la ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">Acción por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation>Цель по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation>в центре</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation>в правом верхнем углу</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation>в нижнем правом углу</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation>в левом верхнем углу</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation>в нижнем левом углу</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="167"/>
+ <source>Prompt dialog default position on screen</source>
+ <translation type="obsolete">Posición por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation>исполняемым файлом</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation>по командной строке</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation>по порту назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation>по IP-адресу назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation>по ID пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source>once</source>
+ <translation>один раз</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation>30 секунд</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation>5 минут</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation>15 минут</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation>30 минут</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation>1 час</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation>постоянно</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="923"/>
+ <source>deny</source>
+ <translation>запрещено</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>allow</source>
+ <translation>разрешено</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">Отключить всплывающие окна, отображать только предупреждение</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source>Nodes</source>
+ <translation>Узлы</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="740"/>
+ <source>Process monitor method</source>
+ <translation>Метод мониторинга процесса</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="774"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Продолжительность по умолчанию будет иметь место, когда пользовательский интерфейс не подключен.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="899"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>Адрес узла.</p><p>По умолчанию: unix:///tmp/osui.sock (unix:// является обязательным, если это Unix сокет) </p><p>Это также может быть IP-адрес с портом: 127.0.0.1:50051</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="902"/>
+ <source>Address</source>
+ <translation>Адрес</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1042"/>
+ <source>Default log level</source>
+ <translation>Уровень логирования по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="950"/>
+ <source>Version</source>
+ <translation>Версия</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="813"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Действие по умолчанию выполняется при отсутствии подключенного пользовательского интерфейса.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="757"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>Лог файл для логирования.<br/></p><p>/dev/stdout выводит логи на стандартный вывод.</p></body> </html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="760"/>
+ <source>Log file</source>
+ <translation>Лог файл</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones.
+
+La ventana emergente sólo contendrá información relativa a la conexión.
+
+Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente
+es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">Interceptar conexiones desconocidas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="832"/>
+ <source>HostName</source>
+ <translation>Имя хоста</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1001"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation>unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="886"/>
+ <source>until restart</source>
+ <translation>до перезапуска</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source>always</source>
+ <translation>всегда</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1013"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation>/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1018"/>
+ <source>/dev/stdout</source>
+ <translation>/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>Применить конфигурацию ко всем узлам</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1057"/>
+ <source>Database</source>
+ <translation>База данных</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1092"/>
+ <source>In memory</source>
+ <translation>В памяти</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1097"/>
+ <source>File</source>
+ <translation>Файл</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1363"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1374"/>
+ <source>Apply</source>
+ <translation>Применить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1385"/>
+ <source>Save</source>
+ <translation>Сохранить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation>до перезагрузки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1111"/>
+ <source>Database type</source>
+ <translation>Тип базы данных</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1118"/>
+ <source>Select</source>
+ <translation>Выбрать</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Pop-ups default options</source>
+ <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="367"/>
+ <source>Pop-ups default position on screen</source>
+ <translation type="obsolete">Posición en pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="102"/>
+ <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source>
+ <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation>Показывать расширенный вид по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="660"/>
+ <source>Action</source>
+ <translation>Действие</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation><html><head/><body><p>Если этот флажок установлен, всплывающие окна будут отображаться с активным расширенным видом.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation>Длительность</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation><html><head/><body><p>По умолчанию, когда появляется новое всплывающее окно, в его простейшей форме вы сможете фильтровать соединения или приложения по одному свойству соединения (исполняемый файл, порт, IP-адрес и т. д.).</p><p>С помощью этих вариантов, вы можете выбрать несколько полей для фильтрации подключений.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation>Также фильтровать соединения по:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="362"/>
+ <source>If checked, this field will be checked when a pop-up is displayed</source>
+ <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation>ID пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation>Порт назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation>IP назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation><html><head/><body><p>Этот тайм-аут представляет собой обратный отсчет, который вы видите, когда отображается всплывающее диалоговое окно.</p><p>Если всплывающее окно не отвечает, будут применены параметры по умолчанию.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation>Расширенный вид позволяет легко выбирать несколько полей для фильтрации подключений.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation>Если флажок установлен, это поле будет выбрано при отображении всплывающего окна.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation><html><head/><body><p>Действие всплывающего окна по умолчанию.</p><p>Когда будет установлено новое исходящее соединение, это действие будет выбрано по умолчанию, поэтому, если тайм-аут срабатывает , будет применен этот параметр.</p><p><br/></p><p>Когда всплывающее окно просит пользователя разрешить или запретить соединение:</p><p >1. новые исходящие соединения запрещены.</p><p>2. известные соединения разрешаются или запрещаются на основе правил, определенных пользователем.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="816"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>Обычное действие, когда интерфейс отключен</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="912"/>
+ <source>Debug invalid connections</source>
+ <translation>Отладка недействительных соединений</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation>Всплывающие окна</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation>Параметры по умолчанию</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation>Положение по умолчанию на экране</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="713"/>
+ <source>any temporary rules</source>
+ <translation>любые временные правила</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="478"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation><html><head/><body><p>Если выбран этот параметр, правила выбранной продолжительности не будут добавляться в список временных правил в графическом интерфейсе.</p><p><br/></p><p>Временные правила останутся в силе, и вы сможете использовать их, когда будет предложено разрешить/запретить новое подключение.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="481"/>
+ <source>Don't save rules of duration</source>
+ <translation>Не сохранять правила длительности</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="463"/>
+ <source>Show events columns</source>
+ <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="596"/>
+ <source>Time</source>
+ <translation>Время</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="676"/>
+ <source>Destination</source>
+ <translation>Назначение</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="644"/>
+ <source>Protocol</source>
+ <translation>Протокол</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="692"/>
+ <source>Process</source>
+ <translation>Процесс</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="612"/>
+ <source>Rule</source>
+ <translation>Правило</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="628"/>
+ <source>Node</source>
+ <translation>Узел</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="723"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Eсли этот флажок установлен, opensnitch предложит вам разрешить или запретить соединения, не имеющие связанного PID, по нескольким причинам, в основном из-за плохого состояния соединений.</p> <p>Всплывающее диалоговое окно будет содержать только информацию о сетевом подключении.</p><p>Хотя в некоторых сценариях это действительные подключения, например, при установке VPN с помощью wireguard.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="584"/>
+ <source>Events tab columns</source>
+ <translation>Столбцы вкладки "События"</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="494"/>
+ <source>Desktop notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="512"/>
+ <source>Use system notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="528"/>
+ <source>Use Qt notifications</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="557"/>
+ <source>Test</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="570"/>
+ <source>System</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="705"/>
+ <source>Theme</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="909"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1196"/>
+ <source>minutes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1222"/>
+ <source>Minutes between events purges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1245"/>
+ <source>days</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1255"/>
+ <source>Maximum days of events to keep</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation>Детали процесса</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation>загрузка...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation>CWD: загрузка...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation>статистика памяти: загружается...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation>Статус</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation>Открыть файлы</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation>I/O Статистика</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation>Файлы с отображением памяти</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation>Стек</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation>Переменные среды</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation>PIDы приложений</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation>Начать или остановить мониторинг этого процесса</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/>
+ <source>Rule</source>
+ <translation>Правило</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="852"/>
+ <source>Node</source>
+ <translation>Узел</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="875"/>
+ <source>Apply rule to all nodes</source>
+ <translation>Применить правило ко всем узлам</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/>
+ <source>From this command line</source>
+ <translation>Из этой командной строки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="341"/>
+ <source>From this executable</source>
+ <translation>Из этого исполняемого файла</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/>
+ <source>Action</source>
+ <translation>Действие</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/>
+ <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source>
+ <translation type="obsolete">/путь/к/исполняемому/файлу, .*/bin/executable[0-9\.]+$, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="456"/>
+ <source>To this IP / Network</source>
+ <translation>К этому IP / Сети</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/>
+ <source>once</source>
+ <translation>один раз</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="102"/>
+ <source>30s</source>
+ <translation>30 секунд</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/>
+ <source>5m</source>
+ <translation>5 минут</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/>
+ <source>15m</source>
+ <translation>15 минут</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/>
+ <source>30m</source>
+ <translation>30 минут</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/>
+ <source>1h</source>
+ <translation>1 час</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source>until restart</source>
+ <translation type="obsolete">hasta reiniciar (el servicio)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/>
+ <source>always</source>
+ <translation>всегда</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="366"/>
+ <source>To this port</source>
+ <translation>К этому порту</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="247"/>
+ <source>From this user ID</source>
+ <translation>От этого ID пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="492"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation>Запятые или пробелы не могут указывать несколько доменов.
+
+Вместо этого используйте регулярные выражения:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+или один домен:
+www.gnu.org - это будет соответствовать только www.gnu.org, но не ftp.gnu.org, и не www2.gnu.org,...
+gnu.org - это будет соответствовать только gnu.org, но не www.gnu.org, и не ftp.gnu.org, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="503"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation>www.domain.org, .*\.domain.org</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="396"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation><html><head/><body><p>Только TCP, UDP или UDPLITE разрешены</p><p>Вы можете использовать regexp: ^(TCP|UDP)$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="406"/>
+ <source>TCP</source>
+ <translation>TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/>
+ <source>UDP</source>
+ <translation>UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/>
+ <source>UDPLITE</source>
+ <translation>UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/>
+ <source>TCP6</source>
+ <translation>TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/>
+ <source>UDP6</source>
+ <translation>UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/>
+ <source>UDPLITE6</source>
+ <translation>UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="513"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation>Вы можете указать один IP:
+- 192.168.1.1
+
+или регулярное выражение:
+- 192\.168\.1\.[0-9]+
+
+несколько IP-адресов:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+Вы также можете указать подсеть:
+- 192.168.1.0/24
+
+Примечание. Запятые или пробелы не могут использоваться для разделения IP-адресов или сетей.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/>
+ <source>LAN</source>
+ <translation>LAN</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/>
+ <source>127.0.0.0/8</source>
+ <translation>127.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>192.168.0.0/24</source>
+ <translation>192.168.0.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/>
+ <source>192.168.1.0/24</source>
+ <translation>192.168.1.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/>
+ <source>192.168.2.0/24</source>
+ <translation>192.168.2.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/>
+ <source>192.168.0.0/16</source>
+ <translation>192.168.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/>
+ <source>169.254.0.0/16</source>
+ <translation>169.254.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/>
+ <source>172.16.0.0/12</source>
+ <translation>172.16.0.0/12</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/>
+ <source>10.0.0.0/8</source>
+ <translation>10.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/>
+ <source>::1/128</source>
+ <translation>::1/128</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/>
+ <source>fc00::/7</source>
+ <translation>fc00::/7</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/>
+ <source>ff00::/8</source>
+ <translation>ff00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/>
+ <source>fe80::/10</source>
+ <translation>fe80::/10</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/>
+ <source>fd00::/8</source>
+ <translation>fd00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/>
+ <source>Duration</source>
+ <translation>Длительность</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="449"/>
+ <source>Protocol</source>
+ <translation>Протокол</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="373"/>
+ <source>To this host</source>
+ <translation>К этому хосту</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/>
+ <source>Deny</source>
+ <translation>Запретить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/>
+ <source>Allow</source>
+ <translation>Разрешить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="912"/>
+ <source>Name</source>
+ <translation>Имя</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="884"/>
+ <source>Enable</source>
+ <translation>Включить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="928"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation>Правила проверяются в алфавитном порядке, поэтому вы можете назвать их соответствующим образом, чтобы расставить приоритеты.
+
+000-allow-localhost
+001-deny-broadcast
+...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="935"/>
+ <source>leave blank to autocreate</source>
+ <translation>оставьте пустым для автосоздания</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="891"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation>Если этот флажок установлен, это правило будет иметь приоритет над остальными правилами. Никакие другие правила не будут проверяться после этого.
+
+Вы должны назвать правило таким образом, чтобы оно проверялось первым, потому что они проверяются в алфавитном порядке. Например:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Priority rule</source>
+ <translation>Правило приоритета</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="770"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation><html><head/><body><p>По умолчанию поле правил не чувствительно к регистру, т. е. если процесс пытается получить доступ к gOOgle.CoM, а у вас есть правило Запретить .*google.com, соединение будет заблокировано.<br/></p><p>Если вы установите этот флажок, вы должны указать точную строку (домен, исполняемый файл, командную строку), которую вы хотите отфильтровать.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/>
+ <source>Case-sensitive</source>
+ <translation>Чувствительно к регистру</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="442"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation><html><head/><body><p>Вы можете указать несколько портов, используя регулярные выражения:</p><p><br/></p><p>- 53, 80 или 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 или 5551, 5552, 5553, итд:</p><p>^(53|443|555[0-9])$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/>
+ <source>until reboot</source>
+ <translation>до перезагрузки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="658"/>
+ <source>To this list of domains</source>
+ <translation>К этому списку доменов</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Выберите каталог со списками доменов, которые нужно заблокировать или разрешить.</p><p>Поместите в этот каталог файлы с любым расширением, содержащие списки доменов.</p><p><br/>Формат каждой записи списка следующий (формат хостов):</p><p>127.0.0.1 www.domain.com</p><p>или </p><p>0.0.0.0 www.domain.com</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/>
+ <source>Deny will just discard the connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/>
+ <source>Reject will drop the connection, and kill the socket that initiated it</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/>
+ <source>Allow will allow the connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="221"/>
+ <source>Applications</source>
+ <translation type="unfinished">Приложения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="240"/>
+ <source>Is regular expression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="274"/>
+ <source>From this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>is regular expression</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="360"/>
+ <source>Network</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/>
+ <source>List of domains/IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="616"/>
+ <source>To this list of network ranges</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="623"/>
+ <source>To this list of IPs</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="649"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="684"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="712"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="727"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="764"/>
+ <source>More</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>Сетевая статистика OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="284"/>
+ <source>Save to CSV.</source>
+ <translation>Сохранить в CSV.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="294"/>
+ <source>Ctrl+S</source>
+ <translation>Ctrl+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="330"/>
+ <source>Create a new rule</source>
+ <translation>Создать новое правило</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="360"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="399"/>
+ <source>Status</source>
+ <translation>Статус</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1627"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="437"/>
+ <source>Start or Stop interception</source>
+ <translation>Начать или остановить перехват</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="482"/>
+ <source>Events</source>
+ <translation>События</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation>Фильтр</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation>Разрешить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation>Запретить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation>Ex.: фаерфокс</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="724"/>
+ <source>Nodes</source>
+ <translation>Узлы</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(дважды щелкните столбец Адреса, чтобы просмотреть сведения об узле)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1531"/>
+ <source>Rules</source>
+ <translation>Правила</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="833"/>
+ <source>enable</source>
+ <translation>включить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="684"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation type="obsolete">искать название правила</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="680"/>
+ <source>Application rules</source>
+ <translation>Правила приложений</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="782"/>
+ <source>Permanent</source>
+ <translation>Постоянно</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="791"/>
+ <source>Temporary</source>
+ <translation>Временно</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="895"/>
+ <source>Hosts</source>
+ <translation>Хосты</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(дважды щелкните, чтобы просмотреть сведения об элементе)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="982"/>
+ <source>Applications</source>
+ <translation>Приложения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1089"/>
+ <source>Addresses</source>
+ <translation>Адреса</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1176"/>
+ <source>Ports</source>
+ <translation>Порты</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1260"/>
+ <source>Users</source>
+ <translation>Пользователи</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1366"/>
+ <source>Connections</source>
+ <translation>Соединения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1421"/>
+ <source>Dropped</source>
+ <translation>Сброшено</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1476"/>
+ <source>Uptime</source>
+ <translation>Время работы</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1601"/>
+ <source>Version</source>
+ <translation>Версия</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation>Удалить все перехваченные события</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="840"/>
+ <source>Edit rule</source>
+ <translation>Редактировать правило</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="854"/>
+ <source>Delete rule</source>
+ <translation>Удалить правило</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation type="obsolete">Удалить всю информацию о перехваченных хостах</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation type="obsolete">Удалить всю информацию о перехваченных приложениях</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation type="obsolete">Удалить всю информацию о перехваченных адресах</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation type="obsolete">Удалить всю информацию о перехваченных портах</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation type="obsolete">Удалить всю информацию о перехваченных пользователях</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(дважды щелкните строку, чтобы просмотреть сведения о правиле)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="665"/>
+ <source>Delete connections that matched this rule</source>
+ <translation type="obsolete">Удалить подключения, соответствующие этому правилу</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="773"/>
+ <source>All applications</source>
+ <translation>Все приложения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation>Статистика</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation>Помощь</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation>Включить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation>Выключить</translation>
+ </message>
+</context>
+<context>
+ <name>menu_close</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="131"/>
+ <source>Close</source>
+ <translation type="obsolete">Cerrar</translation>
+ </message>
+</context>
+<context>
+ <name>menu_help</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="126"/>
+ <source>Help</source>
+ <translation type="obsolete">Ayuda</translation>
+ </message>
+</context>
+<context>
+ <name>menu_statistics</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="120"/>
+ <source>Statistics</source>
+ <translation type="obsolete">Eventos</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="547"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation>Разрешить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation>Запретить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation>навсегда</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation>Исходящее соединение</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation>Процесс запущен из:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation>из этой командной строки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation>из этого исполняемого файла</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/>
+ <source>Unknown process</source>
+ <translation type="obsolete">Proceso no encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation>до перезагрузки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation>в порт {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/>
+ <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation>в {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation>от пользователя {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation>в {0}.*</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation>в *.{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation type="obsolete">в *{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation><b>Удаленный</b> процесс %s запущенный на <b>%s</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation>подключается к <b>%s</b> через %s порт %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation>пытается разрешить <b>%s</b> через %s, %s порт %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="108"/>
+ <source>New outgoing connection</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>popups2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/>
+ <source>Exception saving config: %s</source>
+ <translation type="obsolete">Error al guarda la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/>
+ <source>Applying configuration on %s ...</source>
+ <translation type="obsolete">Aplicando configuración en %s ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="230"/>
+ <source>Server address can not be empty</source>
+ <translation>Адрес сервера не может быть пустым</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/>
+ <source>Error loading %s configuration</source>
+ <translation type="obsolete">Error al cargar la configuración %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="477"/>
+ <source>Configuration applied.</source>
+ <translation>Конфигурация применена.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/>
+ <source>Error applying configuration: %s</source>
+ <translation type="obsolete">Error al aplicar la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/>
+ <source>Exception saving config: {0}</source>
+ <translation>Конфигурация сохранения исключений: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="418"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation>Применение конфигурации к {0}...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="260"/>
+ <source>Error loading {0} configuration</source>
+ <translation>Ошибка при загрузке конфигурации {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="479"/>
+ <source>Error applying configuration: {0}</source>
+ <translation>Ошибка применения конфигурации: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/>
+ <source>Warning</source>
+ <translation>Предупреждение</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation>Вы должны выбрать файл для базы данных<br>или выбрать тип "В памяти".</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="351"/>
+ <source>DB type changed</source>
+ <translation>Тип БД изменен</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="351"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation>Перезапустите графический интерфейс, чтобы эффекты вступили в силу</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="509"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation>Наведите указатель мыши на текст, чтобы отобразить справку<br><br>Не забудьте посетить вики: <a href="{0}">{0}</a></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="127"/>
+ <source>System</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="135"/>
+ <source>Themes not available. Install qt-material: pip3 install qt-material</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/>
+ <source>UI theme changed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/>
+ <source>Restart the GUI in order to apply the new theme</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation><b>Ошибка при загрузке информации о процессе:</b> <br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation><b>Ошибка при остановке мониторинга процесса:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation>загрузка...</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="164"/>
+ <source>There're no nodes connected.</source>
+ <translation>Нет подключенных узлов.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Rule applied.</source>
+ <translation>Правило применено.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/>
+ <source>Error applying rule: %s</source>
+ <translation type="obsolete">Error al aplicar la regla: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="537"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation>протокол не может быть пустым, или снимите галочку</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="551"/>
+ <source>Protocol regexp error</source>
+ <translation>Ошибка регулярного выражения протокола</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="555"/>
+ <source>process path can not be empty</source>
+ <translation>путь процесса не может быть пустым</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="569"/>
+ <source>Process path regexp error</source>
+ <translation>Ошибка регулярного выражения пути процесса</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="573"/>
+ <source>command line can not be empty</source>
+ <translation>командная строка не может быть пустой</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="587"/>
+ <source>Command line regexp error</source>
+ <translation>Ошибка регулярного выражения командной строки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="591"/>
+ <source>Dest port can not be empty</source>
+ <translation>Порт назначения не может быть пустым</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="605"/>
+ <source>Dst port regexp error</source>
+ <translation>Ошибка регулярного выражения порта назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="609"/>
+ <source>Dest host can not be empty</source>
+ <translation>Хост назначения не может быть пустым</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="623"/>
+ <source>Dst host regexp error</source>
+ <translation>Ошибка регулярного выражения хоста назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="627"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>Целевой IP/сеть не могут быть пустыми</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="649"/>
+ <source>Dst IP regexp error</source>
+ <translation>Ошибка регулярного выражения Целевой IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="661"/>
+ <source>User ID can not be empty</source>
+ <translation>Укажите идентификатор пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="675"/>
+ <source>User ID regexp error</source>
+ <translation>Ошибка регулярного выражения ID пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="207"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Ошибка применения правила: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/>
+ <source>Lists field cannot be empty</source>
+ <translation>Поле списков не может быть пустым</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="751"/>
+ <source>Lists field must be a directory</source>
+ <translation>Поле списков должно быть каталогом</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="794"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>Правило не поддерживается</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="439"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Ошибка загрузки правила</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="181"/>
+ <source>There's already a rule with this name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="679"/>
+ <source>PID field can not be empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="693"/>
+ <source>PID field regexp error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="781"/>
+ <source>Select at least one field.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation>Не запущено</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation>Отключено</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation>Запущено</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="412"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="414"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="899"/>
+ <source> Your are about to delete this rule. </source>
+ <translation> Вы собираетесь удалить это правило. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/>
+ <source> Are you sure?</source>
+ <translation> Вы уверены?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="583"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation>OpenSnitch статистика сети {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="585"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation>OpenSnitch статистика сети для {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="24"/>
+ <source>Hits</source>
+ <translation type="unfinished">Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1901"/>
+ <source>Save as CSV</source>
+ <translation>Сохранить как CSV</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="765"/>
+ <source>Delete</source>
+ <translation>Удалить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source>always</source>
+ <translation type="obsolete">siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="580"/>
+ <source><b>Error:</b><br><br>{0}</source>
+ <translation type="obsolete"><b>Error:</b><br><br>{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="758"/>
+ <source>Disable</source>
+ <translation>Отключить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="760"/>
+ <source>Enable</source>
+ <translation>Включить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="763"/>
+ <source>Duplicate</source>
+ <translation>Дублировать</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="764"/>
+ <source>Edit</source>
+ <translation>Редактировать</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="918"/>
+ <source>Rule not found by that name and node</source>
+ <translation>Правило не найдено по этому имени и узлу</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation><b>Ошибка:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="955"/>
+ <source>Warning:</source>
+ <translation>Предупреждение:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="744"/>
+ <source>Allow</source>
+ <translation>Разрешить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="745"/>
+ <source>Deny</source>
+ <translation>Запретить</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="749"/>
+ <source>Always</source>
+ <translation>Всегда</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="750"/>
+ <source>Until reboot</source>
+ <translation>До перезагрузки</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/>
+ <source> You are about to delete this rule. </source>
+ <translation> Вы собираетесь удалить это правило. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>LastConnection</source>
+ <translation type="obsolete">Última Conexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">ÚltimaConexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="285"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Имя</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="286"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Адрес</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="287"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Статус</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Имя хоста</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Версия</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Правила</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="292"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Время</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Действие</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Продолжительность</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Узел</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Включено</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Посещаемость</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Протокол</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Процесс</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Назначение</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Правило</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>ID пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Последнее соединение</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Аргументы</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>IP назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Хост назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Порт назначения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="652"/>
+ <source>New node connected</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="23"/>
+ <source>What</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="289"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">Время работы</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">Соединения</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished">Сброшено</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Apply to</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="746"/>
+ <source>Reject</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="774"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="obsolete"> Estás a punto de borrar esta regla. </translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="776"/>
+ <source> Are you sure?</source>
+ <translation type="obsolete"> ¿Estás seguro?</translation>
+ </message>
+</context>
+<context>
+ <name>stats_disabled</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="74"/>
+ <source>Disabled</source>
+ <translation type="obsolete">Deshabilitado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_notrunning</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="73"/>
+ <source>Not running</source>
+ <translation type="obsolete">Parado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_running</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="75"/>
+ <source>Running</source>
+ <translation type="obsolete">Interceptando</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="409"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de red OpenSnitch</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="411"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="tr">
+<context>
+ <name>Dialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="34"/>
+ <source>opensnitch-qt</source>
+ <translation>opensnitch-qt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="299"/>
+ <source>User ID</source>
+ <translation>Kullanıcı Kimliği</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="333"/>
+ <source><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-weight:600;">Şuradan çalıştırıldı</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="630"/>
+ <source>TextLabel</source>
+ <translation>TextLabel</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="426"/>
+ <source>Source IP</source>
+ <translation>Kaynak IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="449"/>
+ <source>Process ID</source>
+ <translation>İşlem Kimliği</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="582"/>
+ <source>Destination IP</source>
+ <translation>Hedef IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="605"/>
+ <source>Dst Port</source>
+ <translation>Hedef Bağlantı Noktası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="679"/>
+ <source>from this executable</source>
+ <translation>bu programdan</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="684"/>
+ <source>from this command line</source>
+ <translation>bu komut satırından</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="689"/>
+ <source>this destination port</source>
+ <translation>bu hedef bağlantı noktası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="694"/>
+ <source>this user</source>
+ <translation>bu kullanıcı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="699"/>
+ <source>this destination ip</source>
+ <translation>bu hedef IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="728"/>
+ <source>once</source>
+ <translation>bir kere</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="733"/>
+ <source>30s</source>
+ <translation>30sn</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="738"/>
+ <source>5m</source>
+ <translation>5dak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="743"/>
+ <source>15m</source>
+ <translation>15dak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="748"/>
+ <source>30m</source>
+ <translation>30dak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="753"/>
+ <source>1h</source>
+ <translation>1sa</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="706"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="763"/>
+ <source>forever</source>
+ <translation>sonsuza kadar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="789"/>
+ <source>Deny</source>
+ <translation>Reddet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="818"/>
+ <source>Allow</source>
+ <translation>İzin ver</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="847"/>
+ <source>+</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="758"/>
+ <source>until reboot</source>
+ <translation>yeniden başlatılana kadar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/prompt.ui" line="704"/>
+ <source>from this PID</source>
+ <translation>bu işlem kimliğinden</translation>
+ </message>
+</context>
+<context>
+ <name>PreferencesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="14"/>
+ <source>Preferences</source>
+ <translation>Tercihler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="472"/>
+ <source>UI</source>
+ <translation>Kullanıcı arayüzü</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="54"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p></body></html></source>
+ <translation type="obsolete">Este timeout es la cuenta atrás que aparece cuando se muestra una ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="454"/>
+ <source>Default timeout</source>
+ <translation>Öntanımlı zaman aşımı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="331"/>
+ <source>Pop-up default duration</source>
+ <translation>Öntanımlı açılır pencere süresi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="777"/>
+ <source>Default duration</source>
+ <translation>Öntanımlı süre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="162"/>
+ <source>Pop-up default action</source>
+ <translation type="obsolete">Acción por defecto de la ventana emergente</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="483"/>
+ <source>Default action</source>
+ <translation type="obsolete">Acción por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="314"/>
+ <source>Default target</source>
+ <translation>Öntanımlı hedef</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="170"/>
+ <source>center</source>
+ <translation>merkez</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="175"/>
+ <source>top right</source>
+ <translation>sağ üst</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="180"/>
+ <source>bottom right</source>
+ <translation>sağ alt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="185"/>
+ <source>top left</source>
+ <translation>sol üst</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="190"/>
+ <source>bottom left</source>
+ <translation>sol alt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="167"/>
+ <source>Prompt dialog default position on screen</source>
+ <translation type="obsolete">Posición por defecto</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="274"/>
+ <source>by executable</source>
+ <translation>programa göre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="279"/>
+ <source>by command line</source>
+ <translation>komut satırına göre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="284"/>
+ <source>by destination port</source>
+ <translation>hedef bağlantı noktasına göre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="289"/>
+ <source>by destination ip</source>
+ <translation>hedef IP'ye göre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="294"/>
+ <source>by user id</source>
+ <translation>kullanıcı kimliğine göre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="881"/>
+ <source>once</source>
+ <translation>bir kere</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="210"/>
+ <source>30s</source>
+ <translation>30sn</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="215"/>
+ <source>5m</source>
+ <translation>5dak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="220"/>
+ <source>15m</source>
+ <translation>15dak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="225"/>
+ <source>30m</source>
+ <translation>30dak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="230"/>
+ <source>1h</source>
+ <translation>1sa</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>for this session</source>
+ <translation type="obsolete">durante esta sesión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="240"/>
+ <source>forever</source>
+ <translation>sonsuza kadar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="923"/>
+ <source>deny</source>
+ <translation>reddet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="932"/>
+ <source>allow</source>
+ <translation>izin ver</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="406"/>
+ <source>Disable pop-ups, only display an alert</source>
+ <translation type="obsolete">Açılır pencereleri devre dışı bırak, yalnızca bir uyarı görüntüle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="734"/>
+ <source>Nodes</source>
+ <translation>Düğümler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="740"/>
+ <source>Process monitor method</source>
+ <translation>İşlem izleme yöntemi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="774"/>
+ <source><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Öntanımlı süre, bağlı bir kullanıcı arayüzü olmadığında gerçekleşecektir.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="899"/>
+ <source><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></source>
+ <translation><html><head/><body><p>Düğümün adresi.</p><p>Öntanımlı: unix:///tmp/osui.sock (Unix soketi ise unix:// zorunludur)</p><p>Bağlantı noktası ile birlikte bir IP adresi de olabilir: 127.0.0.1:50051</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="902"/>
+ <source>Address</source>
+ <translation>Adres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1042"/>
+ <source>Default log level</source>
+ <translation>Öntanımlı günlük kaydı düzeyi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="950"/>
+ <source>Version</source>
+ <translation>Sürüm</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="813"/>
+ <source><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></source>
+ <translation><html><head/><body><p>Öntanımlı eylem, bağlı bir kullanıcı arayüzü olmadığında gerçekleşecektir.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="757"/>
+ <source><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></source>
+ <translation><html><head/><body><p>Günlük kayıtlarının yazılacağı günlük dosyası.<br/></p><p>/dev/stdout standart çıktıya yazdıracaktır.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="760"/>
+ <source>Log file</source>
+ <translation>Günlük kaydı dosyası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="578"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons.</p><p>The pop-up dialog will only contain information about the network connection.</p></body></html></source>
+ <translation type="obsolete">Si marcas esta opción, OpenSnitch te preguntará para Aceptar o Denegar conexiones que no tengan un PID asociado por diferentes razones.
+
+La ventana emergente sólo contendrá información relativa a la conexión.
+
+Nota: Estas conexiones no tienen por qué indicar que algo sospechoso está sucediendo. Simplemente
+es que no hemos descubierto el PID (por ejemplo conexiones que no se originan en la máquina, o paquetes en mal estado).</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="581"/>
+ <source>Intercept Unknown Connections</source>
+ <translation type="obsolete">Interceptar conexiones desconocidas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="832"/>
+ <source>HostName</source>
+ <translation>Ana makine adı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1001"/>
+ <source>unix:///tmp/osui.sock</source>
+ <translation>unix:///tmp/osui.sock</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="886"/>
+ <source>until restart</source>
+ <translation>yeniden başlatılana kadar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="891"/>
+ <source>always</source>
+ <translation>her zaman</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1013"/>
+ <source>/var/log/opensnitchd.log</source>
+ <translation>/var/log/opensnitchd.log</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1018"/>
+ <source>/dev/stdout</source>
+ <translation>/dev/stdout</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="790"/>
+ <source>Apply configuration to all nodes</source>
+ <translation>Yapılandırmayı tüm düğümlere uygula</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1057"/>
+ <source>Database</source>
+ <translation>Veri tabanı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1092"/>
+ <source>In memory</source>
+ <translation>Bellekte</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1097"/>
+ <source>File</source>
+ <translation>Dosya</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1363"/>
+ <source>Close</source>
+ <translation>Kapat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1374"/>
+ <source>Apply</source>
+ <translation>Uygula</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1385"/>
+ <source>Save</source>
+ <translation>Kaydet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="235"/>
+ <source>until reboot</source>
+ <translation>yeniden başlatılana kadar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1111"/>
+ <source>Database type</source>
+ <translation>Veri tabanı türü</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1118"/>
+ <source>Select</source>
+ <translation>Seç</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="83"/>
+ <source>Pop-ups default options</source>
+ <translation type="obsolete">Opciones por defecto de las ventanas emergentes</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="367"/>
+ <source>Pop-ups default position on screen</source>
+ <translation type="obsolete">Posición en pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="102"/>
+ <source><html><head/><body><p>The advanced view allows you to apply more filters on a connection</p><p>when a pop-up appears.</p></body></html></source>
+ <translation type="obsolete">La vista avanzada permite filtrar conexiones por más parámetros</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="350"/>
+ <source>Show advanced view by default</source>
+ <translation>Öntanımlı olarak gelişmiş görünümü göster</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="660"/>
+ <source>Action</source>
+ <translation>Eylem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="366"/>
+ <source><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></source>
+ <translation><html><head/><body><p>İşaretlenirse, açılır pencereler gelişmiş görünüm etkinken görüntülenecektir.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="334"/>
+ <source>Duration</source>
+ <translation>Süre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="254"/>
+ <source><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></source>
+ <translation><html><head/><body><p>Öntanımlı olarak, yeni bir açılır pencere göründüğünde, en basit haliyle, bağlantıları veya uygulamaları bağlantının bir özelliğine göre (program, bağlantı noktası, IP, vb.) filtreleyebileceksiniz.</p><p>Bu seçeneklerle, bağlantıları filtrelemek için birden fazla alan seçebilirsiniz.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="257"/>
+ <source>Filter connections also by:</source>
+ <translation>Bağlantıları şuna göre de filtrele:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="362"/>
+ <source>If checked, this field will be checked when a pop-up is displayed</source>
+ <translation type="obsolete">Si lo seleccionas, este campo se usará para filtrar las conexiones</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="81"/>
+ <source>User ID</source>
+ <translation>Kullanıcı kimliği</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="97"/>
+ <source>Destination port</source>
+ <translation>Hedef bağlantı noktası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="113"/>
+ <source>Destination IP</source>
+ <translation>Hedef IP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="451"/>
+ <source><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></source>
+ <translation><html><head/><body><p>Bu zaman aşımı, bir açılır iletişim kutusu gösterildiğinde gördüğünüz geri sayımdır.</p><p>Açılır pencereye yanıt verilmezse, öntanımlı seçenekler uygulanacaktır.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="347"/>
+ <source>The advanced view allows you to easily select multiple fields to filter connections</source>
+ <translation>Gelişmiş görünüm, bağlantıları filtrelemek için birden fazla alanı kolayca seçmenize olanak tanır</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="110"/>
+ <source>If checked, this field will be selected when a pop-up is displayed</source>
+ <translation>İşaretlenirse, bir açılır pencere görüntülendiğinde bu alan seçilecektir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="150"/>
+ <source><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></source>
+ <translation><html><head/><body><p>Açılır pencere öntanımlı eylemi.</p><p>Yeni bir giden bağlantı kurulmak üzereyken, bu eylem öntanımlı olarak seçilecektir, bu nedenle zaman aşımı devreye girerse, uygulanacak seçenek budur.</p><p><br/></p><p>Bir açılır pencere kullanıcıdan bir bağlantıya izin vermesini veya reddetmesini isterken:</p><p>1. yeni giden bağlantılar reddedilir.</p><p>2. bilinen bağlantılara kullanıcı tarafından tanımlanan kurallara göre izin verilir veya reddedilir.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="816"/>
+ <source>Default action when the GUI is disconnected</source>
+ <translation>GUI bağlantısı kesildiğinde öntanımlı eylem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="912"/>
+ <source>Debug invalid connections</source>
+ <translation>Geçersiz bağlantılarda hata ayıkla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="39"/>
+ <source>Pop-ups</source>
+ <translation>Açılır pencereler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="64"/>
+ <source>Default options</source>
+ <translation>Öntanımlı seçenekler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="321"/>
+ <source>Default position on screen</source>
+ <translation>Ekranda öntanımlı konum</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="713"/>
+ <source>any temporary rules</source>
+ <translation>herhangi bir geçici kural</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="478"/>
+ <source><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></source>
+ <translation><html><head/><body><p>Bu seçenek seçildiğinde, seçilen sürenin kuralları grafiksel kullanıcı arayüzündeki geçici kurallar listesine eklenmeyecektir.</p><p><br/></p><p>Geçici kurallar hala geçerli olacaktır ve yeni bir bağlantıya izin vermeniz/reddetmeniz istendiğinde bunları kullanabilirsiniz.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="481"/>
+ <source>Don't save rules of duration</source>
+ <translation>Süre kurallarını kaydetme</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="463"/>
+ <source>Show events columns</source>
+ <translation type="obsolete">Mostrar columnas de la pestaña Eventos</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="596"/>
+ <source>Time</source>
+ <translation>Zaman</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="676"/>
+ <source>Destination</source>
+ <translation>Hedef</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="644"/>
+ <source>Protocol</source>
+ <translation>İletişim kuralı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="692"/>
+ <source>Process</source>
+ <translation>İşlem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="612"/>
+ <source>Rule</source>
+ <translation>Kural</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="628"/>
+ <source>Node</source>
+ <translation>Düğüm</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="723"/>
+ <source><html><head/><body><p>If checked, opensnitch will prompt you to allow or deny connections that don't have an asocciated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using wireguard.</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>İşaretlenirse, opensnitch, çoğunlukla kötü durumdaki bağlantılar olmak üzere çeşitli nedenlerden dolayı, atanmış bir işlem kimliğine sahip olmayan bağlantılara izin vermenizi veya reddetmenizi isteyecektir.</p><p>Açılır iletişim kutusu yalnızca ağ bağlantısı hakkında bilgi içerecektir.</p><p>Yine de, örneğin wireguard kullanarak bir VPN kurarken olduğu gibi, bunların geçerli bağlantılar olduğu bazı durumlar vardır.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="584"/>
+ <source>Events tab columns</source>
+ <translation>Olaylar sekmesi sütunları</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="299"/>
+ <source>by PID</source>
+ <translation>işlem kimliğine göre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="461"/>
+ <source>Disable pop-ups, only display an notification</source>
+ <translation>Açılır pencereleri devre dışı bırak, yalnızca bir bildirim görüntüle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="494"/>
+ <source>Desktop notifications</source>
+ <translation>Masaüstü bildirimleri</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="512"/>
+ <source>Use system notifications</source>
+ <translation>Sistem bildirimlerini kullan</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="528"/>
+ <source>Use Qt notifications</source>
+ <translation>Qt bildirimlerini kullan</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="557"/>
+ <source>Test</source>
+ <translation>Test</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="570"/>
+ <source>System</source>
+ <translation>Sistem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="705"/>
+ <source>Theme</source>
+ <translation>Tema</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="909"/>
+ <source><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></source>
+ <translation><html><head/><body><p>İşaretlenirse OpenSnitch, çoğunlukla kötü durumdaki bağlantılar olmak üzere çeşitli nedenlerden dolayı ilişkili bir işlem kimliğine sahip olmayan bağlantılara izin vermenizi veya reddetmenizi isteyecektir.</p><p>Açılır iletişim kutusu yalnızca ağ bağlantısı hakkında bilgi içerecektir.</p><p>WireGuard kullanarak bir VPN kurarken olduğu gibi, bunların geçerli bağlantılar olduğu bazı senaryolar vardır.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1196"/>
+ <source>minutes</source>
+ <translation>dakika</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1222"/>
+ <source>Minutes between events purges</source>
+ <translation>Olay temizlemeleri arasındaki dakikalar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1245"/>
+ <source>days</source>
+ <translation>gün</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/preferences.ui" line="1255"/>
+ <source>Maximum days of events to keep</source>
+ <translation>Saklanacak azami etkinlik günü sayısı</translation>
+ </message>
+</context>
+<context>
+ <name>ProcessDetailsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="14"/>
+ <source>Process details</source>
+ <translation>İşlem ayrıntıları</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="61"/>
+ <source>loading...</source>
+ <translation>yükleniyor...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="81"/>
+ <source>CWD: loading...</source>
+ <translation>CWD: yükleniyor...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="93"/>
+ <source>mem stats: loading...</source>
+ <translation>bellek istatistikleri: yükleniyor...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="121"/>
+ <source>Status</source>
+ <translation>Durum</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="135"/>
+ <source>Open files</source>
+ <translation>Açık dosyalar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="149"/>
+ <source>I/O Statistics</source>
+ <translation>G/Ç İstatistikleri</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="163"/>
+ <source>Memory mapped files</source>
+ <translation>Bellek eşlemeli dosyalar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="177"/>
+ <source>Stack</source>
+ <translation>Yığın</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="191"/>
+ <source>Environment variables</source>
+ <translation>Ortam değişkenleri</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="210"/>
+ <source>Application pids</source>
+ <translation>Uygulama işlem kimlikleri</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="240"/>
+ <source>Start or stop monitoring this process</source>
+ <translation>Bu işlemi izlemeyi başlat veya durdur</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/process_details.ui" line="256"/>
+ <source>Close</source>
+ <translation>Kapat</translation>
+ </message>
+</context>
+<context>
+ <name>RulesDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="20"/>
+ <source>Rule</source>
+ <translation>Kural</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="852"/>
+ <source>Node</source>
+ <translation>Düğüm</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="875"/>
+ <source>Apply rule to all nodes</source>
+ <translation>Kuralı tüm düğümlere uygula</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="254"/>
+ <source>From this command line</source>
+ <translation>Bu komut satırından</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="341"/>
+ <source>From this executable</source>
+ <translation>Bu programdan</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="56"/>
+ <source>Action</source>
+ <translation>Eylem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="138"/>
+ <source>/path/to/executable, .*/bin/executable[0-9\.]+$, ...</source>
+ <translation type="obsolete">/programın/yolu, .*/bin/program[0-9\.]+$, ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="456"/>
+ <source>To this IP / Network</source>
+ <translation>Bu IP'ye / Ağa</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="97"/>
+ <source>once</source>
+ <translation>bir kere</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="102"/>
+ <source>30s</source>
+ <translation>30sn</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="107"/>
+ <source>5m</source>
+ <translation>5dak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="112"/>
+ <source>15m</source>
+ <translation>15dak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="117"/>
+ <source>30m</source>
+ <translation>30dak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="122"/>
+ <source>1h</source>
+ <translation>1sa</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source>until restart</source>
+ <translation type="obsolete">hasta reiniciar (el servicio)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="132"/>
+ <source>always</source>
+ <translation>her zaman</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="366"/>
+ <source>To this port</source>
+ <translation>Bu bağlantı noktasına</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="247"/>
+ <source>From this user ID</source>
+ <translation>Bu kullanıcı kimliğinden</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="492"/>
+ <source>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</source>
+ <translation>Birden fazla etki alanı belirtmek için virgül veya boşluk kullanılmasına izin verilmez.
+
+Bunun yerine düzenli ifadeler kullanın:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+veya tek bir etki alanı:
+www.gnu.org - yalnızca www.gnu.org ile eşleşir, ftp.gnu.org, www2.gnu.org vb. ile eşleşmez.
+gnu.org - yalnızca gnu.org ile eşleşir, www.gnu.org, ftp.gnu.org vb. ile eşleşmez.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="503"/>
+ <source>www.domain.org, .*\.domain.org</source>
+ <translation>www.etkialani.org, .*\.etkialani.org</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="396"/>
+ <source><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></source>
+ <translation><html><head/><body><p>Yalnızca TCP, UDP veya UDPLITE izin verilir</p><p>Düzenli ifade kullanabilirsiniz, örn: ^(TCP|UDP)$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="406"/>
+ <source>TCP</source>
+ <translation>TCP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="411"/>
+ <source>UDP</source>
+ <translation>UDP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="416"/>
+ <source>UDPLITE</source>
+ <translation>UDPLITE</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="421"/>
+ <source>TCP6</source>
+ <translation>TCP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="426"/>
+ <source>UDP6</source>
+ <translation>UDP6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="431"/>
+ <source>UDPLITE6</source>
+ <translation>UDPLITE6</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="513"/>
+ <source>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</source>
+ <translation>Tek bir IP belirtebilirsiniz:
+- 192.168.1.1
+
+veya düzenli bir ifade:
+- 192\.168\.1\.[0-9]+
+
+birden fazla IP:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+Ayrıca bir alt ağ da belirtebilirsiniz:
+- 192.168.1.0/24
+
+Not: IP veya ağları ayırmak için virgüllere veya boşluklara izin verilmez.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="532"/>
+ <source>LAN</source>
+ <translation>LAN</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="537"/>
+ <source>127.0.0.0/8</source>
+ <translation>127.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="542"/>
+ <source>192.168.0.0/24</source>
+ <translation>192.168.0.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="547"/>
+ <source>192.168.1.0/24</source>
+ <translation>192.168.1.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="552"/>
+ <source>192.168.2.0/24</source>
+ <translation>192.168.2.0/24</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="557"/>
+ <source>192.168.0.0/16</source>
+ <translation>192.168.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="562"/>
+ <source>169.254.0.0/16</source>
+ <translation>169.254.0.0/16</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="567"/>
+ <source>172.16.0.0/12</source>
+ <translation>172.16.0.0/12</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="572"/>
+ <source>10.0.0.0/8</source>
+ <translation>10.0.0.0/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="577"/>
+ <source>::1/128</source>
+ <translation>::1/128</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="582"/>
+ <source>fc00::/7</source>
+ <translation>fc00::/7</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="587"/>
+ <source>ff00::/8</source>
+ <translation>ff00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="592"/>
+ <source>fe80::/10</source>
+ <translation>fe80::/10</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="597"/>
+ <source>fd00::/8</source>
+ <translation>fd00::/8</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="89"/>
+ <source>Duration</source>
+ <translation>Süre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="449"/>
+ <source>Protocol</source>
+ <translation>İletişim kuralı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="373"/>
+ <source>To this host</source>
+ <translation>Bu ana makineye</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="151"/>
+ <source>Deny</source>
+ <translation>Reddet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="191"/>
+ <source>Allow</source>
+ <translation>İzin ver</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="912"/>
+ <source>Name</source>
+ <translation>Ad</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="884"/>
+ <source>Enable</source>
+ <translation>Etkinleştir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="928"/>
+ <source>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</source>
+ <translation>Kurallar alfabetik sıraya göre denetlenir, böylece onları önceliklendirmek için uygun şekilde adlandırabilirsiniz.
+
+000-localhost-izinver
+001-genelyayin-reddet
+...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="935"/>
+ <source>leave blank to autocreate</source>
+ <translation>otomatik oluşturmak için boş bırakın</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="891"/>
+ <source>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</source>
+ <translation>İşaretlenirse, bu kural diğer kurallara göre öncelikli olacaktır. Bundan sonra başka hiçbir kural denetlenmeyecektir.
+
+Alfabetik sıraya göre denetlendikleri için kuralı önce denetlenecek şekilde adlandırmalısınız. Örneğin:
+
+[x] Öncelik - 000-oncelik-kurali
+[ ] Öncelik - 001-daha-az-oncelik-kurali</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="899"/>
+ <source>Priority rule</source>
+ <translation>Öncelik kuralı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="770"/>
+ <source><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></source>
+ <translation><html><head/><body><p>Öntanımlı olarak, kuralların alanı büyük/küçük harfe duyarsızdır, yani bir işlem gOOgle.CoM adresine erişmeye çalışırsa ve .*google.com adresini Reddetmek için bir kuralınız varsa, bağlantı engellenecektir.<br/></p><p>Bu kutuyu işaretlerseniz, filtrelemek istediğiniz dizgeyi (etki alanı, program, komut satırı) tam olarak belirtmeniz gerekir.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="773"/>
+ <source>Case-sensitive</source>
+ <translation>Büyük/küçük harfe duyarlı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="442"/>
+ <source><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></source>
+ <translation><html><head/><body><p>Düzenli ifadeler kullanarak birden fazla bağlantı noktası belirtebilirsiniz:</p><p><br/></p> <p> - 53, 80 veya 443:</p><p>^(53|80|443)$</p><p><br/></p> <p> - 53, 443 veya 5551, 5552, 5553, vs:</p><p>^(53|443|555[0-9])$</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="127"/>
+ <source>until reboot</source>
+ <translation>yeniden başlatılana kadar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="658"/>
+ <source>To this list of domains</source>
+ <translation>Bu etki alanı listesine</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="539"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p>Engellenecek veya izin verilecek etki alanlarının listelerini içeren bir dizin seçin.</p><p>Etki alanı listelerini içeren herhangi bir uzantıya sahip dosyaları bu dizinin içine yerleştirin.</p><p><br/>Bir listenin her girdisinin biçimi şu şekildedir (hosts dosyası biçimi):</p><p>127.0.0.1 www.etkialani.com</p><p>veya </p><p>0.0.0.0 www.etkialani.com</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="148"/>
+ <source>Deny will just discard the connection</source>
+ <translation>Reddet seçeneği yalnızca bağlantıyı iptal edecektir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="165"/>
+ <source>Reject will drop the connection, and kill the socket that initiated it</source>
+ <translation>Geri çevir seçeneği bağlantıyı kesecek ve onu başlatan soketi sonlandıracaktır</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="168"/>
+ <source>Reject</source>
+ <translation>Geri çevir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="185"/>
+ <source>Allow will allow the connection</source>
+ <translation>İzin ver seçeneği bağlantıya izin verecektir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="221"/>
+ <source>Applications</source>
+ <translation>Uygulamalar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="230"/>
+ <source><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></source>
+ <translation><html><head/><body><p>Bu alanın değeri her zaman program dosyasının mutlak yoludur: /programın/yolu<br/></p><p>Örnekler:</p><p>- Basit: /programın/yolu</p><p>- Birden çok yol: ^/usr/lib(64|)/firefox/firefox$</p><p>- Birden fazla program: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- /tmp'den çalıştırmaları reddet/izin ver:</p><p>^/(var/|)tmp/.*$<br/></p><p>Daha fazla örnek için <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki sayfasını</a> ziyaret edin veya <a href="https://github.com/evilsocket/opensnitch/discussions">Tartışma forumlarında</a> sorun.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="240"/>
+ <source>Is regular expression</source>
+ <translation>Düzenli ifadedir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="264"/>
+ <source><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></source>
+ <translation><html><head/><body><p>Bu alan kullanıcı tarafından çalıştırılan komut satırını içerecek ve eşleşecektir.<br/></p><p>Kullanıcı komutu yazdıysa, yalnızca komut görünecektir:</p><p>telnet 1.2.3.4<br/></p><p>Kullanıcı komutun mutlak veya göreceli yolunu yazdıysa, görünecek olan budur:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="274"/>
+ <source>From this PID</source>
+ <translation>Bu işlem kimliğinden</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="348"/>
+ <source>is regular expression</source>
+ <translation>düzenli ifadedir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="360"/>
+ <source>Network</source>
+ <translation>Ağ</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="610"/>
+ <source>List of domains/IPs</source>
+ <translation>Etki alanlarının/IP'lerin listesi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="616"/>
+ <source>To this list of network ranges</source>
+ <translation>Bu ağ aralıkları listesine</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="623"/>
+ <source>To this list of IPs</source>
+ <translation>Bu IP listesine</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="649"/>
+ <source><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Engellenecek veya izin verilecek IP'lerin listesini içeren dosyaların bulunduğu bir dizin seçin:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>vb.</p><p>Satır başına bir IP. Boş veya # ile başlayan satırlar dikkate alınmaz.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="684"/>
+ <source><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Engellenecek veya izin verilecek ağ aralıklarının listesini içeren dosyaların bulunduğu bir dizin seçin:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>vb.<br/></p><p>Satır başına bir ağ aralığı. Boş veya # ile başlayan satırlar dikkate alınmaz.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="712"/>
+ <source><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Engellenecek veya izin verilecek etki alanlarının listelerini içeren bir dizin seçin.</p><p>Etki alanı listelerini içeren herhangi bir uzantıya sahip dosyaları bu dizinin içine yerleştirin.</p><p><br/>Bir listenin her girdisinin biçimi şu şekildedir (hosts dosyası biçimi):</p><p>127.0.0.1 www.etkialani.com</p><p>veya </p><p>0.0.0.0 www.etkialani.com</p><p>Boş veya # ile başlayan satırlar dikkate alınmaz.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="727"/>
+ <source>To this list of domains
+(regular expressions)</source>
+ <translation>Bu etki alanları listesine
+(düzenli ifadeler)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="754"/>
+ <source><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></source>
+ <translation><html><head/><body><p>Engellenecek veya izin verilecek etki alanlarının düzenli ifadelerini içeren dosyaları içeren bir dizin seçin:</p><p>.*\.ornek\.com</p><p>Bir etki alanını olduğu gibi de kullanabilirsiniz: &quot;ornek.com&quot;, bu herhangibirsey.ornek.com, herhangibirsey.ornek.com.localdomain, vb. ile eşleşecektir.</p><p>Satır başına bir etki alanı. Boş veya # ile başlayan satırlar dikkate alınmaz.</p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/ruleseditor.ui" line="764"/>
+ <source>More</source>
+ <translation>Daha fazla</translation>
+ </message>
+</context>
+<context>
+ <name>StatsDialog</name>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="34"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation>OpenSnitch Ağ İstatistikleri</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="284"/>
+ <source>Save to CSV.</source>
+ <translation>CSV olarak kaydet.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="294"/>
+ <source>Ctrl+S</source>
+ <translation>Ctrl+S</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="330"/>
+ <source>Create a new rule</source>
+ <translation>Yeni bir kural oluştur</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="360"/>
+ <source><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></source>
+ <translation><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">ana makine adı - 192.168.1.1</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="399"/>
+ <source>Status</source>
+ <translation>Durum</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1627"/>
+ <source>-</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="437"/>
+ <source>Start or Stop interception</source>
+ <translation>Araya girmeyi başlat veya durdur</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="482"/>
+ <source>Events</source>
+ <translation>Olaylar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="94"/>
+ <source>Filter</source>
+ <translation>Filtrele</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="107"/>
+ <source>Allow</source>
+ <translation>İzin ver</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="116"/>
+ <source>Deny</source>
+ <translation>Reddet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="143"/>
+ <source>Ex.: firefox</source>
+ <translation>Örn: firefox</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="199"/>
+ <source>50</source>
+ <translation>50</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="204"/>
+ <source>100</source>
+ <translation>100</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="209"/>
+ <source>200</source>
+ <translation>200</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="214"/>
+ <source>300</source>
+ <translation>300</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="724"/>
+ <source>Nodes</source>
+ <translation>Düğümler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="554"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Addr column to view details of a node)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(bir düğümün ayrıntılarını görüntülemek için Adres sütununa çift tıklayın)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1531"/>
+ <source>Rules</source>
+ <translation>Kurallar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="833"/>
+ <source>enable</source>
+ <translation>etkinleştir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="684"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on the Name column to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete">(doble click en la columna Nombre para ver los detalles)</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="692"/>
+ <source>search rule name</source>
+ <translation type="obsolete">kural adı ara</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="680"/>
+ <source>Application rules</source>
+ <translation>Uygulama kuralları</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="782"/>
+ <source>Permanent</source>
+ <translation>Kalıcı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="791"/>
+ <source>Temporary</source>
+ <translation>Geçici</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="895"/>
+ <source>Hosts</source>
+ <translation>Ana makineler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1364"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click to view details of an item)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(bir ögenin ayrıntılarını görüntülemek için çift tıklayın)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="982"/>
+ <source>Applications</source>
+ <translation>Uygulamalar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1089"/>
+ <source>Addresses</source>
+ <translation>Adresler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1176"/>
+ <source>Ports</source>
+ <translation>Bağlantı noktaları</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1260"/>
+ <source>Users</source>
+ <translation>Kullanıcılar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1366"/>
+ <source>Connections</source>
+ <translation>Bağlantılar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1421"/>
+ <source>Dropped</source>
+ <translation>Bırakıldı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1476"/>
+ <source>Uptime</source>
+ <translation>Çalışma süresi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1601"/>
+ <source>Version</source>
+ <translation>Sürüm</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="227"/>
+ <source>Delete all intercepted events</source>
+ <translation>Araya girilen tüm olayları sil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="840"/>
+ <source>Edit rule</source>
+ <translation>Kuralı düzenle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="854"/>
+ <source>Delete rule</source>
+ <translation>Kuralı sil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="926"/>
+ <source>Delete all intercepted hosts</source>
+ <translation type="obsolete">Araya girilen tüm ana makineleri sil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1051"/>
+ <source>Delete all intercepted applications</source>
+ <translation type="obsolete">Araya girilen tüm uygulamaları sil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1159"/>
+ <source>Delete all intercepted addresses</source>
+ <translation type="obsolete">Araya girilen tüm adresleri sil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1261"/>
+ <source>Delete all intercepted ports</source>
+ <translation type="obsolete">Araya girilen tüm bağlantı noktalarını sil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="1371"/>
+ <source>Delete all intercepted users</source>
+ <translation type="obsolete">Araya girilen tüm kullanıcıları sil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="699"/>
+ <source><html><head/><body><p><span style=" font-size:7pt;">(double click on a row to view details of a rule)</span></p></body></html></source>
+ <translation type="obsolete"><html><head/><body><p><span style=" font-size:7pt;">(bir kuralın ayrıntılarını görüntülemek için bir satıra çift tıklayın)</span></p></body></html></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="665"/>
+ <source>Delete connections that matched this rule</source>
+ <translation type="obsolete">Bu kuralla eşleşen bağlantıları sil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="773"/>
+ <source>All applications</source>
+ <translation>Tüm uygulamalar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="125"/>
+ <source>Reject</source>
+ <translation>Geri çevir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/res/stats.ui" line="177"/>
+ <source>0</source>
+ <translation>0</translation>
+ </message>
+</context>
+<context>
+ <name>contextual_menu</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="43"/>
+ <source>Statistics</source>
+ <translation>İstatistikler</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="46"/>
+ <source>Help</source>
+ <translation>Yardım</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="47"/>
+ <source>Close</source>
+ <translation>Kapat</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="44"/>
+ <source>Enable</source>
+ <translation>Etkinleştir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="45"/>
+ <source>Disable</source>
+ <translation>Devre dışı bırak</translation>
+ </message>
+</context>
+<context>
+ <name>menu_close</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="131"/>
+ <source>Close</source>
+ <translation type="obsolete">Cerrar</translation>
+ </message>
+</context>
+<context>
+ <name>menu_help</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="126"/>
+ <source>Help</source>
+ <translation type="obsolete">Ayuda</translation>
+ </message>
+</context>
+<context>
+ <name>menu_statistics</name>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="120"/>
+ <source>Statistics</source>
+ <translation type="obsolete">Eventos</translation>
+ </message>
+</context>
+<context>
+ <name>notifications</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="547"/>
+ <source>System notifications are not available, you need to install python3-notify2.</source>
+ <translation>Sistem bildirimleri kullanılamıyor, python3-notify2 kurmanız gerekiyor.</translation>
+ </message>
+</context>
+<context>
+ <name>popups</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="91"/>
+ <source>Allow</source>
+ <translation>İzin ver</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="92"/>
+ <source>Deny</source>
+ <translation>Reddet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="54"/>
+ <source>forever</source>
+ <translation>sonsuza kadar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="285"/>
+ <source>Outgoing connection</source>
+ <translation>Giden bağlantı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="290"/>
+ <source>Process launched from:</source>
+ <translation>İşlem şuradan başlatıldı:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="325"/>
+ <source>from this command line</source>
+ <translation>bu komut satırından</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="321"/>
+ <source>from this executable</source>
+ <translation>bu programdan</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="208"/>
+ <source>Unknown process</source>
+ <translation type="obsolete">Proceso no encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="52"/>
+ <source>until reboot</source>
+ <translation>yeniden başlatılana kadar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="327"/>
+ <source>to port {0}</source>
+ <translation>{0} bağlantı noktasına</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="222"/>
+ <source><b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete"><b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="228"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="390"/>
+ <source>to {0}</source>
+ <translation>{0} hedefine</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="330"/>
+ <source>from user {0}</source>
+ <translation>{0} kullanıcısından</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="347"/>
+ <source>to {0}.*</source>
+ <translation>{0}.* hedefine</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="400"/>
+ <source>to *.{0}</source>
+ <translation>*.{0} hedefine</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="378"/>
+ <source>to *{0}</source>
+ <translation type="obsolete">*{0} hedefine</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="434"/>
+ <source><b>Remote</b> process %s running on <b>%s</b></source>
+ <translation>%s <b>uzak</b> işlemi, <b>%s</b> üzerinde çalışıyor</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="438"/>
+ <source>is connecting to <b>%s</b> on %s port %d</source>
+ <translation><b>%s</b> hedefine bağlanıyor, %s bağlantı noktası %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="444"/>
+ <source>is attempting to resolve <b>%s</b> via %s, %s port %d</source>
+ <translation><b>%s</b> çözümlemeye çalışıyor, %s aracılığıyla, %s bağlantı noktası %d</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/notifications.py" line="108"/>
+ <source>New outgoing connection</source>
+ <translation>Yeni giden bağlantı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="334"/>
+ <source>from this PID</source>
+ <translation>bu işlem kimliğinden</translation>
+ </message>
+</context>
+<context>
+ <name>popups2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/prompt.py" line="254"/>
+ <source><b>Remote</b> process <b>%s</b> running on <b>%s</b> is connecting to <b>%s</b> on %s port %d</source>
+ <translation type="obsolete">El proceso <b>remoto %s</b> ejecutándose en <b>%s</b> está conectándose a <b>%s</b> en el puerto %s %d</translation>
+ </message>
+</context>
+<context>
+ <name>preferences</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="171"/>
+ <source>Exception saving config: %s</source>
+ <translation type="obsolete">Error al guarda la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="177"/>
+ <source>Applying configuration on %s ...</source>
+ <translation type="obsolete">Aplicando configuración en %s ...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="230"/>
+ <source>Server address can not be empty</source>
+ <translation>Sunucu adresi boş olamaz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="227"/>
+ <source>Error loading %s configuration</source>
+ <translation type="obsolete">Error al cargar la configuración %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="477"/>
+ <source>Configuration applied.</source>
+ <translation>Yapılandırma uygulandı.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="257"/>
+ <source>Error applying configuration: %s</source>
+ <translation type="obsolete">Error al aplicar la configuración: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="321"/>
+ <source>Exception saving config: {0}</source>
+ <translation>Yapılandırma kaydedilirken istisna oluştu: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="418"/>
+ <source>Applying configuration on {0} ...</source>
+ <translation>{0} üzerinde yapılandırma uygulanıyor...</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="260"/>
+ <source>Error loading {0} configuration</source>
+ <translation>{0} yapılandırması yüklenirken hata oluştu</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="479"/>
+ <source>Error applying configuration: {0}</source>
+ <translation>Yapılandırma uygulanırken hata oluştu: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/>
+ <source>Warning</source>
+ <translation>Uyarı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="345"/>
+ <source>You must select a file for the database<br>or choose "In memory" type.</source>
+ <translation>Veri tabanı için bir dosya seçmelisiniz<br>veya "Bellekte" türünü seçmelisiniz.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="351"/>
+ <source>DB type changed</source>
+ <translation>V.T. türü değişti</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="351"/>
+ <source>Restart the GUI in order effects to take effect</source>
+ <translation>Değişikliklerin etkili olabilmesi için grafiksel arayüzü yeniden başlatın</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="509"/>
+ <source>Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href="{0}">{0}</a></source>
+ <translation>Yardımı görüntülemek için fareyi metinlerin üzerine getirin<br><br>Wiki sayfasını ziyaret etmeyi unutmayın: <a href="{0}">{0}</a></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="127"/>
+ <source>System</source>
+ <translation>Sistem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="135"/>
+ <source>Themes not available. Install qt-material: pip3 install qt-material</source>
+ <translation>Temalar kullanılamıyor. qt-material kurun: pip3 install qt-material</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/>
+ <source>UI theme changed</source>
+ <translation>Kullanıcı arayüzü teması değiştirildi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/preferences.py" line="387"/>
+ <source>Restart the GUI in order to apply the new theme</source>
+ <translation>Yeni temayı uygulamak için grafiksel arayüzü yeniden başlatın</translation>
+ </message>
+</context>
+<context>
+ <name>proc_details</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="97"/>
+ <source><b>Error loading process information:</b> <br><br>
+
+</source>
+ <translation><b>İşlem bilgileri yüklenirken hata oluştu:</b> <br><br>
+
+</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="116"/>
+ <source><b>Error stopping monitoring process:</b><br><br></source>
+ <translation><b>İşlemin izlenmesi durdurulurken hata oluştu:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/processdetails.py" line="156"/>
+ <source>loading...</source>
+ <translation>yükleniyor...</translation>
+ </message>
+</context>
+<context>
+ <name>rules</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="164"/>
+ <source>There're no nodes connected.</source>
+ <translation>Bağlı düğüm yok.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="205"/>
+ <source>Rule applied.</source>
+ <translation>Kural uygulandı.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="123"/>
+ <source>Error applying rule: %s</source>
+ <translation type="obsolete">Error al aplicar la regla: %s</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="537"/>
+ <source>protocol can not be empty, or uncheck it</source>
+ <translation>iletişim kuralı boş olamaz veya işaretini kaldırın</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="551"/>
+ <source>Protocol regexp error</source>
+ <translation>İletişim kuralı düzenli ifade hatası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="555"/>
+ <source>process path can not be empty</source>
+ <translation>işlem yolu boş olamaz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="569"/>
+ <source>Process path regexp error</source>
+ <translation>İşlem yolu düzenli ifade hatası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="573"/>
+ <source>command line can not be empty</source>
+ <translation>komut satırı boş olamaz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="587"/>
+ <source>Command line regexp error</source>
+ <translation>Komut satırı düzenli ifade hatası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="591"/>
+ <source>Dest port can not be empty</source>
+ <translation>Hedef bağlantı noktası boş olamaz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="605"/>
+ <source>Dst port regexp error</source>
+ <translation>Hedef bağlantı noktası düzenli ifade hatası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="609"/>
+ <source>Dest host can not be empty</source>
+ <translation>Hedef ana makine boş olamaz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="623"/>
+ <source>Dst host regexp error</source>
+ <translation>Hedef ana makine düzenli ifade hatası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="627"/>
+ <source>Dest IP/Network can not be empty</source>
+ <translation>Hedef IP/Ağ boş olamaz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="649"/>
+ <source>Dst IP regexp error</source>
+ <translation>Hedef IP düzenli ifade hatası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="661"/>
+ <source>User ID can not be empty</source>
+ <translation>Kullanıcı kimliği boş olamaz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="675"/>
+ <source>User ID regexp error</source>
+ <translation>Kullanıcı kimliği düzenli ifade hatası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="207"/>
+ <source>Error applying rule: {0}</source>
+ <translation>Kural uygulanırken hata oluştu: {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="749"/>
+ <source>Lists field cannot be empty</source>
+ <translation>Listeler alanı boş olamaz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="751"/>
+ <source>Lists field must be a directory</source>
+ <translation>Listeler alanı bir dizin olmalıdır</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="794"/>
+ <source><b>Rule not supported</b></source>
+ <translation><b>Kural desteklenmiyor</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="439"/>
+ <source><b>Error loading rule</b></source>
+ <translation><b>Kural yüklenirken hata oluştu</b></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="181"/>
+ <source>There's already a rule with this name.</source>
+ <translation>Bu ada sahip bir kural zaten var.</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="679"/>
+ <source>PID field can not be empty</source>
+ <translation>İşlem kimliği alanı boş olamaz</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="693"/>
+ <source>PID field regexp error</source>
+ <translation>İşlem kimliği alanı düzenli ifade hatası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/ruleseditor.py" line="781"/>
+ <source>Select at least one field.</source>
+ <translation>En az bir alan seçin.</translation>
+ </message>
+</context>
+<context>
+ <name>stats</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>Not running</source>
+ <translation>Çalışmıyor</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="311"/>
+ <source>Disabled</source>
+ <translation>Devre dışı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="312"/>
+ <source>Running</source>
+ <translation>Çalışıyor</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="412"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de OpenSnitch</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="414"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="899"/>
+ <source> Your are about to delete this rule. </source>
+ <translation> Bu kuralı silmek üzeresiniz. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/>
+ <source> Are you sure?</source>
+ <translation> Emin misiniz?</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="583"/>
+ <source>OpenSnitch Network Statistics {0}</source>
+ <translation>OpenSnitch Ağ İstatistikleri {0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="585"/>
+ <source>OpenSnitch Network Statistics for {0}</source>
+ <translation>{0} için OpenSnitch Ağ İstatistikleri</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="24"/>
+ <source>Hits</source>
+ <translation>Kullanıldı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1901"/>
+ <source>Save as CSV</source>
+ <translation>CSV olarak kaydet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="765"/>
+ <source>Delete</source>
+ <translation>Sil</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source>always</source>
+ <translation type="obsolete">siempre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="580"/>
+ <source><b>Error:</b><br><br>{0}</source>
+ <translation type="obsolete"><b>Error:</b><br><br>{0}</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="758"/>
+ <source>Disable</source>
+ <translation>Devre dışı bırak</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="760"/>
+ <source>Enable</source>
+ <translation>Etkinleştir</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="763"/>
+ <source>Duplicate</source>
+ <translation>Çoğalt</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="764"/>
+ <source>Edit</source>
+ <translation>Düzenle</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="918"/>
+ <source>Rule not found by that name and node</source>
+ <translation>Bu ada ve düğüme göre kural bulunamadı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="948"/>
+ <source><b>Error:</b><br><br></source>
+ <comment>{0}</comment>
+ <translation><b>Hata:</b><br><br></translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="955"/>
+ <source>Warning:</source>
+ <translation>Uyarı:</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="744"/>
+ <source>Allow</source>
+ <translation>İzin ver</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="745"/>
+ <source>Deny</source>
+ <translation>Reddet</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="749"/>
+ <source>Always</source>
+ <translation>Her zaman</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="750"/>
+ <source>Until reboot</source>
+ <translation>Yeniden başlatılana kadar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="1286"/>
+ <source> You are about to delete this rule. </source>
+ <translation> Bu kuralı silmek üzeresiniz. </translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>LastConnection</source>
+ <translation type="obsolete">Última Conexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>xxxxx</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Dirección</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Estado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hostname</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Versión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="298"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Reglas</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Hora</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Acción</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Duración</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Nodo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Habilitado</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Total</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Protocolo</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Aplicación</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Destino</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">Regla</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="309"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">UserID</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="310"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces</comment>
+ <translation type="obsolete">ÚltimaConexión</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="285"/>
+ <source>Name</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Ad</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="286"/>
+ <source>Address</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Adres</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="287"/>
+ <source>Status</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Durum</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="288"/>
+ <source>Hostname</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Ana makine adı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="386"/>
+ <source>Version</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Sürüm</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="383"/>
+ <source>Rules</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Kurallar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="292"/>
+ <source>Time</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Zaman</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="293"/>
+ <source>Action</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Eylem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="294"/>
+ <source>Duration</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Süre</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="295"/>
+ <source>Node</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Düğüm</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="296"/>
+ <source>Enabled</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Etkin</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="405"/>
+ <source>Hits</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Kullanıldı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="299"/>
+ <source>Protocol</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>İletişim kuralı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="300"/>
+ <source>Process</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>İşlem</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="302"/>
+ <source>Destination</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Hedef</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="306"/>
+ <source>Rule</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Kural</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="307"/>
+ <source>UserID</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>KullanıcıKimliği</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="308"/>
+ <source>LastConnection</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>SonBağlantı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="301"/>
+ <source>Args</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Argümanlar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="303"/>
+ <source>DstIP</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>HedefIP</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="304"/>
+ <source>DstHost</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>HedefAnaMakine</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="305"/>
+ <source>DstPort</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>HedefBağlantıNoktası</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/service.py" line="652"/>
+ <source>New node connected</source>
+ <translation>Yeni düğüm bağlandı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="23"/>
+ <source>What</source>
+ <translation>Ne</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/customwidgets/addresstablemodel.py" line="25"/>
+ <source>Network name</source>
+ <translation>Ağ adı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="289"/>
+ <source>Uptime</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Çalışma süresi</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="297"/>
+ <source>Precedence</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Öncelik</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="384"/>
+ <source>Connections</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Bağlantılar</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="385"/>
+ <source>Dropped</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Bırakıldı</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="404"/>
+ <source>What</source>
+ <comment>This is a word, without spaces and symbols.</comment>
+ <translation>Ne</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="736"/>
+ <source>Apply to</source>
+ <translation>Uygula</translation>
+ </message>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="746"/>
+ <source>Reject</source>
+ <translation>Geri çevir</translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="774"/>
+ <source> Your are about to delete this rule. </source>
+ <translation type="obsolete"> Estás a punto de borrar esta regla. </translation>
+ </message>
+</context>
+<context>
+ <name>stats_deleterule2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="776"/>
+ <source> Are you sure?</source>
+ <translation type="obsolete"> ¿Estás seguro?</translation>
+ </message>
+</context>
+<context>
+ <name>stats_disabled</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="74"/>
+ <source>Disabled</source>
+ <translation type="obsolete">Deshabilitado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_notrunning</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="73"/>
+ <source>Not running</source>
+ <translation type="obsolete">Parado</translation>
+ </message>
+</context>
+<context>
+ <name>stats_running</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="75"/>
+ <source>Running</source>
+ <translation type="obsolete">Interceptando</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="409"/>
+ <source>OpenSnitch Network Statistics</source>
+ <translation type="obsolete">Eventos de red OpenSnitch</translation>
+ </message>
+</context>
+<context>
+ <name>stats_wintitle2</name>
+ <message>
+ <location filename="../../../opensnitch/dialogs/stats.py" line="411"/>
+ <source>OpenSnitch Network Statistics for</source>
+ <translation type="obsolete">Eventos de OpenSnitch de</translation>
+ </message>
+</context>
+</TS>
--- /dev/null
+#TEMPLATE = app
+#TARGET = ts
+#INCLUDEPATH += opensnitch
+
+
+# Input
+SOURCES += ../opensnitch/service.py \
+ ../opensnitch/notifications.py \
+ ../opensnitch/customwidgets/addresstablemodel.py \
+ ../opensnitch/customwidgets/main.py \
+ ../opensnitch/dialogs/prompt.py \
+ ../opensnitch/dialogs/preferences.py \
+ ../opensnitch/dialogs/ruleseditor.py \
+ ../opensnitch/dialogs/processdetails.py \
+ ../opensnitch/dialogs/stats.py
+
+FORMS += ../opensnitch/res/prompt.ui \
+ ../opensnitch/res/ruleseditor.ui \
+ ../opensnitch/res/preferences.ui \
+ ../opensnitch/res/process_details.ui \
+ ../opensnitch/res/stats.ui
+TRANSLATIONS += locales/de_DE/opensnitch-de_DE.ts \
+ locales/es_ES/opensnitch-es_ES.ts \
+ locales/eu_ES/opensnitch-eu_ES.ts \
+ locales/hu_HU/opensnitch-hu_HU.ts \
+ locales/ja_JP/opensnitch-ja_JP.ts \
+ locales/pt_BR/opensnitch-pt_BR.ts \
+ locales/ro_RO/opensnitch-ro_RO.ts \
+ locales/fr_FR/opensnitch-fr_FR.ts \
+ locales/lt_LT/opensnitch-lt_LT.ts \
+ locales/tr_TR/opensnitch-tr_TR.ts \
+ locales/ru_RU/opensnitch-ru_RU.ts \
+ locales/nb_NO/opensnitch-nb_NO.ts
--- /dev/null
+%define name opensnitch-ui
+%define version 1.5.8
+%define unmangled_version 1.5.8
+%define release 1
+%define __python python3
+%define desktop_file opensnitch_ui.desktop
+
+Summary: Prompt service and UI for the opensnitch application firewall.
+Name: %{name}
+Version: %{version}
+Release: %{release}
+Source0: %{name}-%{unmangled_version}.tar.gz
+License: GPL-3.0
+Group: Development/Libraries
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
+Prefix: %{_prefix}
+BuildArch: noarch
+Vendor: Simone "evilsocket" Margaritelli <evilsocket@protonmail.com>
+Url: https://github.com/evilsocket/opensnitch
+Requires: python3, python3-pip, (python3-pyinotify or python3-inotify), python3-qt5, python3-notify2
+Recommends: (python3-slugify or python3-python-slugify), python3-protobuf >= 3.0
+
+# avoid to depend on a particular python version
+%global __requires_exclude ^python\\(abi\\) = 3\\..$
+
+%description
+GUI for the opensnitch application firewall
+opensnitch-ui is a GUI for opensnitch written in Python.
+It allows the user to view live outgoing connections, as well as search
+to make connections.
+.
+The user can decide if block the outgoing connection based on properties of
+the connection: by port, by uid, by dst ip, by program or a combination
+of them.
+.
+These rules can last forever, until the app restart or just one time.
+
+%prep
+%setup -n %{name}-%{unmangled_version} -n %{name}-%{unmangled_version}
+
+%post
+
+if [ $1 -ge 1 ]; then
+ for i in $(ls /home)
+ do
+ if grep /home/$i /etc/passwd &>/dev/null; then
+ path=/home/$i/.config/autostart/
+ if [ ! -d $path ]; then
+ mkdir -p $path
+ fi
+ if [ -f /usr/share/applications/%{desktop_file} ];then
+ ln -s /usr/share/applications/%{desktop_file} $path 2>/dev/null || true
+ else
+ echo "No desktop file: %{desktop_file}"
+ fi
+ fi
+ done
+
+ gtk-update-icon-cache /usr/share/icons/hicolor/ || true
+fi
+
+if [ $1 -eq 1 ]; then
+ echo -e "\n You need to install 2 more packages:
+ unicode_slugify and grpcio-tools.
+
+ pip3 install grpcio-tools
+ pip3 install unicode_slugify
+ "
+fi
+
+%postun
+if [ $1 -eq 0 ]; then
+ for i in $(ls /home)
+ do
+ if grep /home/$i /etc/passwd &>/dev/null; then
+ path=/home/$i/.config/autostart/%{desktop_file}
+ if [ -h $path -o -f $path ]; then
+ rm -f $path
+ else
+ echo "No desktop file for this user: $path"
+ fi
+ fi
+ done
+
+ pkill -15 opensnitch-ui 2>/dev/null || true
+
+ echo ""
+ echo " Remember to uninstall grpcio-tools and unicode_slugify if you don't"
+ echo " need them anymore:"
+ echo " pip3 uninstall unicode_slugify"
+ echo " pip3 uninstall grpcio-tools"
+ echo ""
+fi
+
+
+%build
+cd i18n; make; cd ..
+cp -r i18n/locales/ opensnitch/i18n
+pyrcc5 -o opensnitch/resources_rc.py opensnitch/res/resources.qrc
+sed -i 's/^import ui_pb2/from . import ui_pb2/' opensnitch/ui_pb2*
+python3 setup.py build
+
+%install
+python3 setup.py install --install-lib=/usr/lib/python3/dist-packages/ --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --prefix=/usr --record=INSTALLED_FILES
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files -f INSTALLED_FILES
+%defattr(-,root,root)
--- /dev/null
+from PyQt5 import QtCore
+from opensnitch.database import Database
+
+class Config:
+ __instance = None
+
+ HELP_URL = "https://github.com/evilsocket/opensnitch/wiki/"
+ HELP_RULES_URL = "https://github.com/evilsocket/opensnitch/wiki/Rules"
+ HELP_CONFIG_URL = "https://github.com/evilsocket/opensnitch/wiki/Configurations"
+
+ RULE_TYPE_LIST = "list"
+ RULE_TYPE_LISTS = "lists"
+ RULE_TYPE_SIMPLE = "simple"
+ RULE_TYPE_REGEXP = "regexp"
+ RULE_TYPE_NETWORK = "network"
+ RulesTypes = (RULE_TYPE_LIST, RULE_TYPE_LISTS, RULE_TYPE_SIMPLE, RULE_TYPE_REGEXP, RULE_TYPE_NETWORK)
+
+ RULES_DURATION_FILTER = ()
+
+ DEFAULT_DURATION_IDX = 6 # until restart
+ DEFAULT_TARGET_PROCESS = 0
+ ACTION_DENY_IDX = 0
+ ACTION_ALLOW_IDX = 1
+
+ # don't translate
+ ACTION_ALLOW = "allow"
+ ACTION_DENY = "deny"
+ ACTION_REJECT = "reject"
+ DURATION_UNTIL_RESTART = "until restart"
+ DURATION_ALWAYS = "always"
+ DURATION_ONCE = "once"
+ DURATION_1h = "1h"
+ DURATION_30m = "30m"
+ DURATION_15m = "15m"
+ DURATION_5m = "5m"
+ DURATION_30s = "30s"
+
+ POPUP_CENTER = 0
+ POPUP_TOP_RIGHT = 1
+ POPUP_BOTTOM_RIGHT = 2
+ POPUP_TOP_LEFT = 3
+ POPUP_BOTTOM_LEFT = 4
+
+ DEFAULT_THEME = "global/theme"
+ DEFAULT_DISABLE_POPUPS = "global/disable_popups"
+ DEFAULT_TIMEOUT_KEY = "global/default_timeout"
+ DEFAULT_ACTION_KEY = "global/default_action"
+ DEFAULT_DURATION_KEY = "global/default_duration"
+ DEFAULT_TARGET_KEY = "global/default_target"
+ DEFAULT_IGNORE_RULES = "global/default_ignore_rules"
+ DEFAULT_IGNORE_TEMPORARY_RULES = "global/default_ignore_temporary_rules"
+ DEFAULT_POPUP_POSITION = "global/default_popup_position"
+ DEFAULT_POPUP_ADVANCED = "global/default_popup_advanced"
+ DEFAULT_POPUP_ADVANCED_DSTIP = "global/default_popup_advanced_dstip"
+ DEFAULT_POPUP_ADVANCED_DSTPORT = "global/default_popup_advanced_dstport"
+ DEFAULT_POPUP_ADVANCED_UID = "global/default_popup_advanced_uid"
+ DEFAULT_SERVER_ADDR = "global/server_address"
+ DEFAULT_DB_TYPE_KEY = "database/type"
+ DEFAULT_DB_FILE_KEY = "database/file"
+ DEFAULT_DB_PURGE_OLDEST = "database/purge_oldest"
+ DEFAULT_DB_MAX_DAYS = "database/max_days"
+ DEFAULT_DB_PURGE_INTERVAL = "database/purge_interval"
+
+ DEFAULT_TIMEOUT = 30
+
+ NOTIFICATIONS_ENABLED = "notifications/enabled"
+ NOTIFICATIONS_TYPE = "notifications/type"
+ NOTIFICATION_TYPE_SYSTEM = 0
+ NOTIFICATION_TYPE_QT = 1
+
+ STATS_GEOMETRY = "statsDialog/geometry"
+ STATS_LAST_TAB = "statsDialog/last_tab"
+ STATS_FILTER_TEXT = "statsDialog/general_filter_text"
+ STATS_FILTER_ACTION = "statsDialog/general_filter_action"
+ STATS_LIMIT_RESULTS = "statsDialog/general_limit_results"
+ STATS_SHOW_COLUMNS = "statsDialog/show_columns"
+ STATS_NODES_COL_STATE = "statsDialog/nodes_columns_state"
+ STATS_GENERAL_COL_STATE = "statsDialog/general_columns_state"
+ STATS_GENERAL_FILTER_TEXT = "statsDialog/"
+ STATS_GENERAL_FILTER_ACTION = "statsDialog/"
+ STATS_RULES_COL_STATE = "statsDialog/rules_columns_state"
+ STATS_RULES_TREE_EXPANDED_0 = "statsDialog/rules_tree_0_expanded"
+ STATS_RULES_TREE_EXPANDED_1 = "statsDialog/rules_tree_1_expanded"
+ STATS_RULES_SPLITTER_POS = "statsDialog/rules_splitter_pos"
+ STATS_VIEW_COL_STATE = "statsDialog/view_columns_state"
+ STATS_VIEW_DETAILS_COL_STATE = "statsDialog/view_details_columns_state"
+ # don't translate
+
+ @staticmethod
+ def init():
+ Config.__instance = Config()
+ return Config.__instance
+
+ @staticmethod
+ def get():
+ if Config.__instance == None:
+ Config._instance = Config()
+ return Config.__instance
+
+ def __init__(self):
+ self.settings = QtCore.QSettings("opensnitch", "settings")
+
+ if self.settings.value(self.DEFAULT_TIMEOUT_KEY) == None:
+ self.setSettings(self.DEFAULT_TIMEOUT_KEY, self.DEFAULT_TIMEOUT)
+ if self.settings.value(self.DEFAULT_ACTION_KEY) == None:
+ self.setSettings(self.DEFAULT_ACTION_KEY, self.ACTION_ALLOW)
+ if self.settings.value(self.DEFAULT_DURATION_KEY) == None:
+ self.setSettings(self.DEFAULT_DURATION_KEY, self.DEFAULT_DURATION_IDX)
+ if self.settings.value(self.DEFAULT_TARGET_KEY) == None:
+ self.setSettings(self.DEFAULT_TARGET_KEY, self.DEFAULT_TARGET_PROCESS)
+ if self.settings.value(self.DEFAULT_DB_TYPE_KEY) == None:
+ self.setSettings(self.DEFAULT_DB_TYPE_KEY, Database.DB_TYPE_MEMORY)
+ self.setSettings(self.DEFAULT_DB_FILE_KEY, Database.DB_IN_MEMORY)
+
+ self.setRulesDurationFilter(
+ self.getBool(self.DEFAULT_IGNORE_RULES),
+ self.getInt(self.DEFAULT_IGNORE_TEMPORARY_RULES)
+ )
+
+ def reload(self):
+ self.settings = QtCore.QSettings("opensnitch", "settings")
+
+ def hasKey(self, key):
+ return self.settings.contains(key)
+
+ def setSettings(self, path, value):
+ self.settings.setValue(path, value)
+ self.settings.sync()
+
+ def getSettings(self, path):
+ return self.settings.value(path)
+
+ def getBool(self, path, default_value=False):
+ return self.settings.value(path, type=bool, defaultValue=default_value)
+
+ def getInt(self, path, default_value=0):
+ try:
+ return self.settings.value(path, type=int, defaultValue=default_value)
+ except Exception:
+ return default_value
+
+ def getDefaultAction(self):
+ _default_action = self.getInt(self.DEFAULT_ACTION_KEY)
+ if _default_action == self.ACTION_ALLOW_IDX:
+ return self.ACTION_ALLOW
+ else:
+ return self.ACTION_DENY
+
+ def setRulesDurationFilter(self, ignore_temporary_rules=False, temp_rules=1):
+ if ignore_temporary_rules:
+ if temp_rules == 1:
+ Config.RULES_DURATION_FILTER = (Config.DURATION_ONCE)
+ elif temp_rules == 0:
+ Config.RULES_DURATION_FILTER = (
+ Config.DURATION_ONCE, Config.DURATION_30s, Config.DURATION_5m,
+ Config.DURATION_15m, Config.DURATION_30m, Config.DURATION_1h,
+ Config.DURATION_UNTIL_RESTART)
+ else:
+ Config.RULES_DURATION_FILTER = ()
--- /dev/null
+
+from PyQt5 import Qt, QtCore
+from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem
+from PyQt5.QtSql import QSqlQueryModel, QSqlQuery, QSql
+from PyQt5.QtWidgets import QTableView, QAbstractSlider
+from PyQt5.QtCore import QItemSelectionModel, pyqtSignal, QEvent
+import time
+import math
+
+from opensnitch.utils import AsnDB
+from opensnitch.customwidgets.generictableview import GenericTableModel
+from PyQt5.QtCore import QCoreApplication as QC
+
+class AddressTableModel(GenericTableModel):
+
+ def __init__(self, tableName, headerLabels):
+ super().__init__(tableName, headerLabels)
+ self.asndb = AsnDB.instance()
+ self.reconfigureColumns()
+
+ def reconfigureColumns(self):
+ self.headerLabels = []
+ self.setHorizontalHeaderLabels(self.headerLabels)
+ self.headerLabels.append(QC.translate("stats", "What", ""))
+ self.headerLabels.append(QC.translate("stats", "Hits", ""))
+ self.headerLabels.append(QC.translate("stats", "Network name", ""))
+ self.setHorizontalHeaderLabels(self.headerLabels)
+ self.setColumnCount(len(self.headerLabels))
+ self.lastColumnCount = len(self.headerLabels)
+
+ def setQuery(self, q, db):
+ self.origQueryStr = q
+ self.db = db
+
+ if self.prevQueryStr != self.origQueryStr:
+ self.realQuery = QSqlQuery(q, db)
+
+ self.realQuery.exec_()
+ self.realQuery.last()
+
+ queryRows = max(0, self.realQuery.at()+1)
+ self.totalRowCount = queryRows
+ self.setRowCount(self.totalRowCount)
+
+ queryColumns = self.realQuery.record().count()
+ if self.asndb.is_available() and queryColumns < 3:
+ self.reconfigureColumns()
+ else:
+ # update view's columns
+ if queryColumns != self.lastColumnCount:
+ self.setModelColumns(queryColumns)
+
+ self.prevQueryStr = self.origQueryStr
+ self.rowCountChanged.emit()
+
+ def fillRows(self, q, upperBound, force=False):
+ super().fillRows(q, upperBound, force)
+
+ if self.asndb.is_available() == True and self.columnCount() <= 3:
+ for n, col in enumerate(self.items):
+ try:
+ if len(col) < 2:
+ continue
+ col[2] = self.asndb.get_asn(col[0])
+ except Exception as e:
+ col[2] = ""
+ finally:
+ self.items[n] = col
+ self.lastItems = self.items
--- /dev/null
+
+from PyQt5 import Qt, QtCore
+from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem
+from PyQt5.QtSql import QSqlQueryModel, QSqlQuery, QSql
+from PyQt5.QtWidgets import QTableView, QAbstractSlider
+from PyQt5.QtCore import QItemSelectionModel, pyqtSignal, QEvent
+import time
+import math
+
+from PyQt5.QtCore import QCoreApplication as QC
+
+class GenericTableModel(QStandardItemModel):
+ rowCountChanged = pyqtSignal()
+
+ db = None
+ tableName = ""
+ # total row count which must de displayed in the view
+ totalRowCount = 0
+ #
+ lastColumnCount = 0
+
+ # original query string before we modify it
+ origQueryStr = QSqlQuery()
+ # previous original query string; used to check if the query has changed
+ prevQueryStr = ''
+ # modified query object
+ realQuery = QSqlQuery()
+
+ items = []
+ lastItems = []
+
+ def __init__(self, tableName, headerLabels):
+ self.tableName = tableName
+ self.headerLabels = headerLabels
+ self.lastColumnCount = len(self.headerLabels)
+ QStandardItemModel.__init__(self, 0, self.lastColumnCount)
+ self.setHorizontalHeaderLabels(self.headerLabels)
+
+ #Some QSqlQueryModel methods must be mimiced so that this class can serve as a drop-in replacement
+ #mimic QSqlQueryModel.query()
+ def query(self):
+ return self
+
+ #mimic QSqlQueryModel.query().lastQuery()
+ def lastQuery(self):
+ return self.origQueryStr
+
+ #mimic QSqlQueryModel.query().lastError()
+ def lastError(self):
+ return self.realQuery.lastError()
+
+ #mimic QSqlQueryModel.clear()
+ def clear(self):
+ pass
+
+ def data(self, index, role=QtCore.Qt.DisplayRole):
+ if role == QtCore.Qt.DisplayRole:
+ items_count = len(self.items)
+ if index.isValid() and items_count > 0 and index.row() < items_count:
+ return self.items[index.row()][index.column()]
+ return QStandardItemModel.data(self, index, role)
+
+ # set columns based on query's fields
+ def setModelColumns(self, newColumns):
+ self.headerLabels = []
+ self.removeColumns(0, self.lastColumnCount)
+ self.setHorizontalHeaderLabels(self.headerLabels)
+ for col in range(0, newColumns):
+ self.headerLabels.append(self.realQuery.record().fieldName(col))
+ self.lastColumnCount = newColumns
+ self.setHorizontalHeaderLabels(self.headerLabels)
+ self.setColumnCount(len(self.headerLabels))
+
+ def setQuery(self, q, db):
+ self.origQueryStr = q
+ self.db = db
+ #print("q:", q)
+
+ if self.prevQueryStr != self.origQueryStr:
+ self.realQuery = QSqlQuery(q, db)
+
+ self.realQuery.exec_()
+ self.realQuery.last()
+
+ queryRows = max(0, self.realQuery.at()+1)
+ self.totalRowCount = queryRows
+ self.setRowCount(self.totalRowCount)
+
+ # update view's columns
+ queryColumns = self.realQuery.record().count()
+ if queryColumns != self.lastColumnCount:
+ self.setModelColumns(queryColumns)
+
+ self.prevQueryStr = self.origQueryStr
+ self.rowCountChanged.emit()
+
+ def nextRecord(self, offset):
+ cur_pos = self.realQuery.at()
+ q.seek(max(cur_pos, cur_pos+offset))
+
+ def prevRecord(self, offset):
+ cur_pos = self.realQuery.at()
+ q.seek(min(cur_pos, cur_pos-offset))
+
+ # refresh the viewport with data from the db.
+ def refreshViewport(self, scrollValue, maxRowsInViewport, force=False):
+ # set records position to last, in order to get correctly the number of
+ # rows.
+ self.realQuery.last()
+ rowsFound = max(0, self.realQuery.at()+1)
+ if scrollValue == 0 or self.realQuery.at() == QSql.BeforeFirstRow:
+ self.realQuery.seek(QSql.BeforeFirstRow)
+ elif self.realQuery.at() == QSql.AfterLastRow:
+ self.realQuery.seek(rowsFound - maxRowsInViewport)
+ else:
+ self.realQuery.seek(min(scrollValue-1, self.realQuery.at()))
+
+ upperBound = min(maxRowsInViewport, rowsFound)
+ self.setRowCount(self.totalRowCount)
+
+ # only visible rows will be filled with data, and only if we're not
+ # updating the viewport already.
+ if upperBound > 0 or self.realQuery.at() < 0:
+ self.fillRows(self.realQuery, upperBound, force)
+
+ def fillRows(self, q, upperBound, force=False):
+ rowsLabels = []
+ self.setVerticalHeaderLabels(rowsLabels)
+
+ self.items = []
+ cols = []
+ self.blockSignals(True)
+ #don't trigger setItem's signals for each cell, instead emit dataChanged for all cells
+ for x in range(0, upperBound):
+ q.next()
+ if q.at() < 0:
+ # if we don't set query to a valid record here, it gets stucked
+ # forever at -2/-1.
+ q.seek(upperBound)
+ break
+ rowsLabels.append(str(q.at()+1))
+ cols = []
+ for col in range(0, len(self.headerLabels)):
+ cols.append(str(q.value(col)))
+
+ self.items.append(cols)
+ self.blockSignals(False)
+
+ self.setVerticalHeaderLabels(rowsLabels)
+ if self.lastItems != self.items or force == True:
+ self.dataChanged.emit(self.createIndex(0,0), self.createIndex(upperBound, len(self.headerLabels)))
+ self.lastItems = self.items
+ del cols
+
+ def dumpRows(self):
+ rows = []
+ q = QSqlQuery(self.db)
+ q.exec(self.origQueryStr)
+ q.seek(QSql.BeforeFirstRow)
+ while True:
+ q.next()
+ if q.at() == QSql.AfterLastRow:
+ break
+ row = []
+ for col in range(0, len(self.headerLabels)):
+ row.append(q.value(col))
+ rows.append(row)
+ return rows
+
+class GenericTableView(QTableView):
+ # how many rows can potentially be displayed in viewport
+ # the actual number of rows currently displayed may be less than this
+ maxRowsInViewport = 0
+ vScrollBar = None
+
+ def __init__(self, parent):
+ QTableView.__init__(self, parent)
+ #eventFilter to catch key up/down events and wheel events
+ self.installEventFilter(self)
+ self.verticalHeader().setVisible(True)
+ self.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignCenter)
+ self.horizontalHeader().setStretchLastSection(True)
+ #the built-in vertical scrollBar of this view is always off
+ self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+
+ def setVerticalScrollBar(self, vScrollBar):
+ self.vScrollBar = vScrollBar
+ self.vScrollBar.valueChanged.connect(self.onValueChanged)
+ self.vScrollBar.setVisible(False)
+
+ def setModel(self, model):
+ super().setModel(model)
+ model.rowCountChanged.connect(self.onRowCountChanged)
+ model.rowsInserted.connect(self.onRowsInsertedOrRemoved)
+ model.rowsRemoved.connect(self.onRowsInsertedOrRemoved)
+ self.horizontalHeader().sortIndicatorChanged.disconnect()
+ self.setSortingEnabled(False)
+
+ # model().rowCount() is always <= self.maxRowsInViewport
+ # stretch the bottom row; we don't want partial-height rows at the bottom
+ # this will only trigger if rowCount value was changed
+ def onRowsInsertedOrRemoved(self, parent, start, end):
+ if self.model().rowCount() == self.maxRowsInViewport:
+ self.verticalHeader().setStretchLastSection(True)
+ else:
+ self.verticalHeader().setStretchLastSection(False)
+
+ def resizeEvent(self, event):
+ super().resizeEvent(event)
+ #refresh the viewport data based on new geometry
+ self.refresh()
+
+ def refresh(self):
+ self.calculateRowsInViewport()
+ self.model().setRowCount(min(self.maxRowsInViewport, self.model().totalRowCount))
+ self.model().refreshViewport(self.vScrollBar.value(), self.maxRowsInViewport, force=True)
+
+ def calculateRowsInViewport(self):
+ rowHeight = self.verticalHeader().defaultSectionSize()
+ columnSize = self.horizontalHeader().defaultSectionSize()
+ # we don't want partial-height rows in viewport, hence .floor()
+ self.maxRowsInViewport = math.floor(self.viewport().height() / rowHeight)+1
+
+ def onValueChanged(self, vSBNewValue):
+ self.model().refreshViewport(vSBNewValue, self.maxRowsInViewport, force=True)
+
+ def onRowCountChanged(self):
+ scrollBar = self.vScrollBar
+
+ totalCount = self.model().totalRowCount
+ scrollBar.setVisible(True if totalCount > self.maxRowsInViewport else False)
+
+ scrollBar.setMinimum(0)
+ # we need to substract the displayed rows to the total rows, to scroll
+ # down correctly.
+ scrollBar.setMaximum(max(0, totalCount - self.maxRowsInViewport+1))
+
+ self.model().refreshViewport(scrollBar.value(), self.maxRowsInViewport)
+
+ def getCurrentIndex(self):
+ return self.selectionModel().currentIndex().internalId()
+
+ def selectItem(self, _data, _column):
+ """Select a row based on the data displayed on the given column.
+ """
+ items = self.model().findItems(_data, column=_column)
+ if len(items) > 0:
+ self.selectionModel().setCurrentIndex(
+ items[0].index(),
+ QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent
+ )
+
+ def _selectLastRow(self):
+ internalId = self.getCurrentIndex()
+ idx = self.model().createIndex(self.maxRowsInViewport-2, 0, internalId)
+ self.selectionModel().setCurrentIndex(
+ idx, QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent
+ )
+
+ def _selectRow(self, pos):
+ internalId = self.getCurrentIndex()
+ self.selectionModel().setCurrentIndex(
+ self.model().createIndex(pos, 1, internalId), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent
+ )
+
+ def onKeyUp(self):
+ if self.selectionModel().currentIndex().row() == 0:
+ self.vScrollBar.setValue(max(0, self.vScrollBar.value() - 1))
+
+ def onKeyDown(self):
+ if self.vScrollBar.isVisible() == False:
+ return
+
+ if self.selectionModel().currentIndex().row() >= self.maxRowsInViewport-2:
+ self.vScrollBar.setValue(self.vScrollBar.value() + 1)
+ self._selectLastRow()
+
+ def onKeyHome(self):
+ self.vScrollBar.setValue(0)
+ self.selectionModel().setCurrentIndex(self.model().createIndex(0, 0), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent)
+
+ def onKeyEnd(self):
+ self.vScrollBar.setValue(self.vScrollBar.maximum())
+ self._selectLastRow()
+
+ def onKeyPageUp(self):
+ newValue = max(0, self.vScrollBar.value() - self.maxRowsInViewport)
+ self.vScrollBar.setValue(newValue)
+
+ def onKeyPageDown(self):
+ if self.vScrollBar.isVisible() == False:
+ return
+
+ newValue = self.vScrollBar.value() + (self.maxRowsInViewport-2)
+ if newValue >= self.model().rowCount():
+ self._selectLastRow()
+ return
+
+ if newValue < self.model().rowCount():
+ self.vScrollBar.setValue(newValue)
+ self._selectRow(0)
+
+ def eventFilter(self, obj, event):
+ if event.type() == QEvent.KeyPress:
+ # FIXME: setValue() does not update the scrollbars correctly in
+ # some pyqt versions.
+ if event.key() == QtCore.Qt.Key_Up:
+ self.onKeyUp()
+ elif event.key() == QtCore.Qt.Key_Down:
+ self.onKeyDown()
+ elif event.key() == QtCore.Qt.Key_Home:
+ self.onKeyHome()
+ elif event.key() == QtCore.Qt.Key_End:
+ self.onKeyEnd()
+ elif event.key() == QtCore.Qt.Key_PageUp:
+ self.onKeyPageUp()
+ elif event.key() == QtCore.Qt.Key_PageDown:
+ self.onKeyPageDown()
+ elif event.type() == QEvent.Wheel:
+ self.vScrollBar.wheelEvent(event)
+ return True
+ #return False
+ return super(GenericTableView, self).eventFilter(obj, event)
--- /dev/null
+from PyQt5 import Qt, QtCore
+from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem
+from PyQt5.QtSql import QSqlQueryModel, QSqlQuery, QSql
+from PyQt5.QtWidgets import QTableView
+from PyQt5.QtCore import QItemSelectionModel, pyqtSignal, QEvent
+import time
+import math
+
+from PyQt5.QtCore import QCoreApplication as QC
+
+# PyQt5 >= v5.15.8 (#821)
+if hasattr(Qt, "QItemDelegate"):
+ from PyQt5.Qt import QItemDelegate, QStyle
+else:
+ from PyQt5.QtWidgets import QItemDelegate, QStyle
+
+class ColorizedDelegate(QItemDelegate):
+ def __init__(self, parent=None, *args, config=None):
+ QItemDelegate.__init__(self, parent, *args)
+ self._config = config
+ self._alignment = QtCore.Qt.AlignLeft | QtCore.Qt.AlignHCenter
+
+ def paint(self, painter, option, index):
+ if not index.isValid():
+ return super().paint(painter, option, index)
+
+ nocolor=True
+
+ value = index.data(QtCore.Qt.DisplayRole)
+ for _, what in enumerate(self._config):
+ if what == value:
+ nocolor=False
+ painter.save()
+ painter.setPen(self._config[what])
+ if 'alignment' in self._config:
+ self._alignment = self._config['alignment']
+
+ if option.state & QStyle.State_Selected:
+ painter.setBrush(painter.brush())
+ painter.setPen(painter.pen())
+ painter.drawText(option.rect, self._alignment, value)
+ painter.restore()
+
+ if nocolor == True:
+ super().paint(painter, option, index)
+
+class ColorizedQSqlQueryModel(QSqlQueryModel):
+ """
+ model=CustomQSqlQueryModel(
+ modelData=
+ {
+ 'colorize':
+ {'offline': (QColor(QtCore.Qt.red), 2)},
+ 'alignment': { Qt.AlignLeft, 2 }
+ }
+ )
+ """
+ RED = QColor(QtCore.Qt.red)
+ GREEN = QColor(QtCore.Qt.green)
+
+ def __init__(self, modelData={}):
+ QSqlQueryModel.__init__(self)
+ self._model_data = modelData
+
+ def data(self, index, role=QtCore.Qt.DisplayRole):
+ if not index.isValid():
+ return QSqlQueryModel.data(self, index, role)
+
+ column = index.column()
+ row = index.row()
+
+ if role == QtCore.Qt.TextAlignmentRole:
+ return QtCore.Qt.AlignCenter
+ if role == QtCore.Qt.TextColorRole:
+ for _, what in enumerate(self._model_data):
+ d = QSqlQueryModel.data(self, self.index(row, self._model_data[what][1]), QtCore.Qt.DisplayRole)
+ if column == self._model_data[what][1] and what in d:
+ return self._model_data[what][0]
+
+ return QSqlQueryModel.data(self, index, role)
+
+class ConnectionsTableModel(QStandardItemModel):
+ rowCountChanged = pyqtSignal()
+
+ #max rowid in the db; starts with 1, not with 0
+ maxRowId = 0
+ #previous total number of rows in the db when the filter was applied
+ prevFiltRowCount = 0
+ #total number of rows in the db when the filter was not applied
+ prevNormRowCount = 0
+ #total row count which must de displayed in the view
+ totalRowCount = 0
+ #new rows which must be added to the top of the rows displayed in the view
+ prependedRowCount = 0
+
+ db = None
+ #original query string before we modify it
+ origQueryStr = QSqlQuery()
+ #modified query object
+ realQuery = QSqlQuery()
+ #previous original query string; used to check if the query has changed
+ prevQueryStr = ''
+ #whether or not the original query has a filter (a WHERE condition)
+ isQueryFilter = False
+ limit = None
+
+ #a map for fast lookup or rows when filter is enabled
+ #contains ranges of rowids and count of filter hits
+ #range format {'from': <rowid>, 'to': <rowid>, 'hits':<int>}
+ #including the 'from' rowid up to but NOT including the 'to' rowid
+ map = []
+ rangeSize = 1000
+ #all unique/distinct values for each column will be stored here
+ distinct = {'time':[], 'process':[], 'dst_host':[], 'dst_ip':[], 'dst_port':[], 'rule':[], 'node':[], 'protocol':[]}
+ #what was the last rowid\time when the distinct value were updates
+ distinctLastRowId = 0
+ distinctLastUpdateTime = time.time()
+
+ def __init__(self):
+ self.headerLabels = [
+ QC.translate("stats", "Time", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Node", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Action", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Destination", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Protocol", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Process", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Rule", "This is a word, without spaces and symbols.").replace(" ", ""),
+ ]
+ QStandardItemModel.__init__(self, 0, len(self.headerLabels))
+ self.setHorizontalHeaderLabels(self.headerLabels)
+
+ #Some QSqlQueryModel methods must be mimiced so that this class can serve as a drop-in replacement
+ #mimic QSqlQueryModel.query()
+ def query(self):
+ return self
+
+ #mimic QSqlQueryModel.query().lastQuery()
+ def lastQuery(self):
+ return self.origQueryStr
+
+ #mimic QSqlQueryModel.query().lastError()
+ def lastError(self):
+ return self.realQuery.lastError()
+
+ #mimic QSqlQueryModel.clear()
+ def clear(self):
+ pass
+
+ def setQuery(self, q, db):
+ self.origQueryStr = q
+ self.db = db
+ maxRowIdQuery = QSqlQuery(db)
+ maxRowIdQuery.setForwardOnly(True)
+ maxRowIdQuery.exec("SELECT MAX(rowid) FROM connections")
+ maxRowIdQuery.first()
+ value = maxRowIdQuery.value(0)
+ self.maxRowId = 0 if value == '' else int(value)
+ self.updateDistinctIfNeeded()
+ self.limit = int(q.split(' ')[-1]) if q.split(' ')[-2] == 'LIMIT' else None
+ self.isQueryFilter = True if ("LIKE '%" in q and "LIKE '% %'" not in q) or 'Action = "' in q else False
+
+ self.realQuery = QSqlQuery(db)
+ isTotalRowCountChanged = False
+ isQueryChanged = False
+ if self.prevQueryStr != q:
+ isQueryChanged = True
+ if self.isQueryFilter:
+ if isQueryChanged:
+ self.buildMap()
+ largestRowIdInMap = self.map[0]['from']
+ newRowsCount = self.maxRowId - largestRowIdInMap
+ self.prependedRowCount = 0
+
+ if newRowsCount > 0:
+ starttime = time.time()
+ self.realQuery.setForwardOnly(True)
+ for offset in range(0, newRowsCount, self.rangeSize):
+ lowerBound = largestRowIdInMap + offset
+ upperBound = min(lowerBound + self.rangeSize, self.maxRowId)
+ part1, part2 = q.split('ORDER')
+ qStr = part1 + 'AND rowid>'+ str(lowerBound) + ' AND rowid<=' + str(upperBound) + ' ORDER' + part2
+ self.realQuery.exec(qStr)
+ self.realQuery.last()
+ rowsInRange = max(0, self.realQuery.at()+1)
+ if self.map[0]['from'] - self.map[0]['to'] < self.rangeSize:
+ #consolidate with the previous range; we don't want many small ranges
+ self.map[0]['from'] = upperBound
+ self.map[0]['hits'] += rowsInRange
+ else:
+ self.map.insert(0, {'from':upperBound, 'to':lowerBound, 'hits':rowsInRange})
+ self.prependedRowCount += rowsInRange
+ if time.time() - starttime > 0.5:
+ #dont freeze the UI when fetching too many recent rows
+ break
+
+ self.totalRowCount = 0
+ for i in self.map:
+ self.totalRowCount += i['hits']
+ if self.totalRowCount != self.prevFiltRowCount:
+ isTotalRowCountChanged = True
+ self.prevFiltRowCount = self.totalRowCount
+ else: #self.isQueryFilter == False
+ self.prependedRowCount = self.maxRowId - self.prevNormRowCount
+ self.totalRowCount = self.maxRowId
+ if self.totalRowCount != self.prevNormRowCount:
+ isTotalRowCountChanged = True
+ self.prevNormRowCount = self.totalRowCount
+
+ self.prevQueryStr = self.origQueryStr
+ if isTotalRowCountChanged or self.prependedRowCount > 0 or isQueryChanged:
+ self.rowCountChanged.emit()
+
+ #fill self.map with data
+ def buildMap(self):
+ self.map = []
+ q = QSqlQuery(self.db)
+ q.setForwardOnly(True)
+ self.updateDistinctIfNeeded(True)
+ filterStr = self.getFilterStr()
+ actionStr = self.getActionStr()
+ #we only want to know the count of matching rows
+ qStr = "SELECT COUNT(*) from connections WHERE (rowid> :lowerBound AND rowid<= :upperBound)"
+ if actionStr:
+ qStr += ' AND ' + actionStr
+ matchStr = self.getMatch(filterStr) if filterStr else None
+ if matchStr:
+ qStr += ' AND ' + matchStr
+ qStr += ' LIMIT ' + str(self.limit) if self.limit else ''
+ q.prepare(qStr)
+
+ totalRows = 0
+ for offset in range(self.maxRowId, -1, -self.rangeSize):
+ upperBound = offset
+ lowerBound = max(0, upperBound - self.rangeSize)
+ if (not filterStr and actionStr) or (filterStr and matchStr):
+ #either 1) only action was present or 2) filter which has a match (with or without action)
+ q.bindValue(":lowerBound", str(lowerBound))
+ q.bindValue(":upperBound", str(upperBound))
+ q.exec_()
+ q.first()
+ rowsInRange = int(q.value(0))
+ else:
+ rowsInRange = 0
+ totalRows += rowsInRange
+ self.map.append({'from':upperBound, 'to':lowerBound, 'hits':rowsInRange})
+ if self.limit and totalRows >= self.limit:
+ break
+
+ #periodically keep track of all distinct values for each column
+ #this is needed in order to build efficient queries when the filter is applied
+ def updateDistinctIfNeeded(self, force=False):
+ if (not force and (time.time() - self.distinctLastUpdateTime) < 10) or self.maxRowId == self.distinctLastRowId:
+ return
+ if (self.maxRowId < self.distinctLastRowId):
+ #the db has been cleared, re-init the values
+ self.distinctLastRowId = 0
+ self.distinct = {'time':[], 'process':[], 'dst_host':[], 'dst_ip':[], 'dst_port':[], 'rule':[], 'node':[], 'protocol':[]}
+ q = QSqlQuery(self.db)
+ q.setForwardOnly(True)
+ for column in self.distinct.keys():
+ q.exec('SELECT DISTINCT ' + column + ' FROM connections WHERE rowid>'
+ + str(self.distinctLastRowId) + ' AND rowid<=' + str(self.maxRowId))
+ while q.next():
+ if q.value(0) not in self.distinct[column]:
+ self.distinct[column].append(q.value(0))
+ self.distinctLastRowId =self.maxRowId
+ self.distinctLastUpdateTime = time.time()
+
+ #refresh the viewport with data from the db
+ #"value" is vertical scrollbar's value
+ def refreshViewport(self, value, maxRowsInViewport):
+ q = QSqlQuery(self.db)
+ #sequential number of topmost/bottommost rows in viewport (numbering starts from the bottom with 1 not with 0)
+ botRowNo = max(1, self.totalRowCount - (value + maxRowsInViewport-1))
+ topRowNo = min(botRowNo + maxRowsInViewport-1, self.totalRowCount)
+
+ if not self.isQueryFilter:
+ part1, part2 = self.origQueryStr.split('ORDER')
+ qStr = part1 + 'WHERE rowid>='+ str(botRowNo) + ' AND rowid<=' + str(topRowNo) + ' ORDER' + part2
+ else:
+ self.updateDistinctIfNeeded(True)
+ #replace query part between WHERE and ORDER
+ qStr = self.origQueryStr.split('WHERE')[0] + ' WHERE '
+ actionStr = self.getActionStr()
+ if actionStr:
+ qStr += actionStr + " AND "
+ #find inside the map the range(s) in which top and bottom rows are located
+ total, offsetInRange, botRowFound, topRowFound = 0, None, False, False
+ ranges = [{'from':0, 'to':0, 'hits':0}]
+ for i in reversed(self.map):
+ if total + i['hits'] >= botRowNo:
+ botRowFound = True
+ if total + i['hits'] >= topRowNo:
+ topRowFound = True
+ if botRowFound and i['hits'] > 0:
+ if i['to'] == ranges[-1]['from']:
+ #merge two adjacent ranges
+ ranges[-1]['from'] = i['from']
+ ranges[-1]['hits'] += i['hits']
+ else:
+ ranges.append(i.copy())
+ if topRowFound:
+ offsetInRange = i['hits'] - (topRowNo - total)
+ break
+ total += i['hits']
+
+ rangeStr = ''
+ if len(ranges) > 0:
+ rangeStr = '('
+ for r in ranges:
+ rangeStr += '(rowid>' + str(r['to']) + ' AND rowid<=' + str(r['from']) + ') OR '
+ rangeStr = rangeStr[:-3] #remove trailing 'OR '
+ rangeStr += ') AND '
+ qStr += rangeStr
+
+ filterStr = self.getFilterStr()
+ matchStr = self.getMatch(filterStr) if filterStr else None
+ if matchStr:
+ qStr += matchStr + " AND "
+ qStr = qStr[:-4] #remove trailing ' AND'
+ qStr += ' ORDER '+ self.origQueryStr.split('ORDER')[1]
+
+ q.exec(qStr)
+ q.last()
+ rowsFound = max(0, q.at()+1)
+ if not self.isQueryFilter:
+ q.seek(QSql.BeforeFirstRow)
+ else:
+ #position the db cursor on topRowNo
+ q.seek(QSql.BeforeFirstRow if offsetInRange == 0 else offsetInRange-1)
+ upperBound = min(maxRowsInViewport, rowsFound)
+ self.setRowCount(upperBound)
+ #only visible rows will be filled with data
+ if upperBound > 0:
+ #don't trigger setItem's signals for each cell, instead emit dataChanged for all cells
+ self.blockSignals(True)
+ for x in range(0, upperBound):
+ q.next()
+ for col in range(0, len(self.headerLabels)):
+ self.setItem(x, col, QStandardItem(q.value(col)))
+ self.blockSignals(False)
+ self.dataChanged.emit(self.createIndex(0,0), self.createIndex(upperBound, len(self.headerLabels)))
+
+ #form a condition string for the query: if filterStr is (partially) present in any of the columns
+ def getMatch (self, filterStr):
+ match = {}
+ for column in self.distinct.keys():
+ match[column] = []
+ for value in self.distinct[column]:
+ if filterStr in value:
+ match[column].append(value)
+ matchStr = None
+ if any([match[col] for col in match]):
+ matchStr = '( '
+ if match['time']:
+ matchStr += "time IN ('" + "','".join(match['time']) + "') OR"
+ if match['process']:
+ matchStr += "process IN ('" + "','".join(match['process']) + "') OR"
+ if match['dst_host']:
+ matchStr += " (dst_host != '' AND dst_host IN ('" + "','".join(match['dst_host']) + "') ) OR"
+ if match['dst_ip']:
+ matchStr += " (dst_host = '' AND dst_ip IN ('" + "','".join(match['dst_ip']) + "') ) OR"
+ if match['dst_port']:
+ matchStr += " dst_port IN ('" + "','".join(match['dst_port']) + "') OR"
+ if match['rule']:
+ matchStr += " rule IN ('" + "','".join(match['rule']) + "') OR"
+ if match['node']:
+ matchStr += " node IN ('" + "','".join(match['node']) + "') OR"
+ if match['protocol']:
+ matchStr += " protocol IN ('" + "','".join(match['protocol']) + "') OR"
+ matchStr = matchStr[:-2] #remove trailing 'OR'
+ matchStr += ' )'
+ return matchStr
+
+ #extract the filter string if any
+ def getFilterStr(self):
+ filterStr = None
+ if "LIKE '%" in self.origQueryStr:
+ filterStr = self.origQueryStr.split("LIKE '%")[1].split("%")[0]
+ return filterStr
+
+ #extract the action string if any
+ def getActionStr(self):
+ actionStr = None
+ if 'WHERE Action = "' in self.origQueryStr:
+ actionCond = self.origQueryStr.split('WHERE Action = "')[1].split('"')[0]
+ actionStr = "action = '"+actionCond+"'"
+ return actionStr
+
+ def dumpRows(self):
+ rows = []
+ q = QSqlQuery(self.db)
+ q.exec(self.origQueryStr)
+ q.seek(QSql.BeforeFirstRow)
+ while True:
+ q.next()
+ if q.at() == QSql.AfterLastRow:
+ break
+ row = []
+ for col in range(0, len(self.headerLabels)):
+ row.append(q.value(col))
+ rows.append(row)
+ return rows
+
+class ConnectionsTableView(QTableView):
+ # how many rows can potentially be displayed in viewport
+ # the actual number of rows currently displayed may be less than this
+ maxRowsInViewport = 0
+ #vertical scroll bar
+ vScrollBar = None
+
+ def __init__(self, parent):
+ QTableView.__init__(self, parent)
+ #eventFilter to catch key up/down events and wheel events
+ self.installEventFilter(self)
+ self.verticalHeader().setVisible(False)
+ self.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignCenter)
+ self.horizontalHeader().setStretchLastSection(True)
+ #the built-in vertical scrollBar of this view is always off
+ self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+
+ def setVerticalScrollBar(self, vScrollBar):
+ self.vScrollBar = vScrollBar
+ self.vScrollBar.valueChanged.connect(self.onValueChanged)
+ self.vScrollBar.setVisible(False)
+
+ def setModel(self, model):
+ super().setModel(model)
+ model.rowCountChanged.connect(self.onRowCountChanged)
+ model.rowsInserted.connect(self.onRowsInsertedOrRemoved)
+ model.rowsRemoved.connect(self.onRowsInsertedOrRemoved)
+ self.horizontalHeader().sortIndicatorChanged.disconnect()
+ self.setSortingEnabled(False)
+
+ #model().rowCount() is always <= self.maxRowsInViewport
+ #stretch the bottom row; we don't want partial-height rows at the bottom
+ #this will only trigger if rowCount value was changed
+ def onRowsInsertedOrRemoved(self, parent, start, end):
+ if self.model().rowCount() == self.maxRowsInViewport:
+ self.verticalHeader().setStretchLastSection(True)
+ else:
+ self.verticalHeader().setStretchLastSection(False)
+
+ def resizeEvent(self, event):
+ super().resizeEvent(event)
+ #refresh the viewport data based on new geometry
+ self.calculateRowsInViewport()
+ self.model().setRowCount(min(self.maxRowsInViewport, self.model().totalRowCount))
+ self.model().refreshViewport(self.vScrollBar.value(), self.maxRowsInViewport)
+
+ def calculateRowsInViewport(self):
+ rowHeight = self.verticalHeader().defaultSectionSize()
+ #we don't want partial-height rows in viewport, hence .floor()
+ self.maxRowsInViewport = math.floor(self.viewport().height() / rowHeight)
+
+ def onValueChanged(self, vSBNewValue):
+ savedIndex = self.selectionModel().currentIndex()
+ self.model().refreshViewport(vSBNewValue, self.maxRowsInViewport)
+ #restore selection which was removed by model's refreshing the data
+ self.selectionModel().setCurrentIndex(savedIndex, QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent)
+
+ # if ( scrollbar at the top or row limit set):
+ # let new rows "push down" older rows without changing the scrollbar position
+ # else:
+ # don't update data in viewport, only change scrollbar position.
+ def onRowCountChanged(self):
+ totalCount = self.model().totalRowCount
+ scrollBar = self.vScrollBar
+ scrollBar.setVisible(True if totalCount > self.maxRowsInViewport else False)
+ scrollBarValue = scrollBar.value()
+ if self.model().limit:
+ newValue = min(scrollBarValue, self.model().limit - self.maxRowsInViewport)
+ scrollBar.setMinimum(0)
+ scrollBar.setMaximum( min(totalCount, self.model().limit) - self.maxRowsInViewport)
+ if scrollBarValue != newValue:
+ #setValue does not trigger valueChanged if new value is the same as old
+ scrollBar.setValue(newValue)
+ else:
+ scrollBar.valueChanged.emit(newValue)
+ else:
+ scrollBar.setMinimum(0)
+ scrollBar.setMaximum(max(0, totalCount - self.maxRowsInViewport))
+ if scrollBarValue == 0:
+ scrollBar.valueChanged.emit(0)
+ elif scrollBarValue > 0:
+ if self.model().prependedRowCount == 0:
+ scrollBar.valueChanged.emit(scrollBarValue)
+ else:
+ scrollBar.setValue(scrollBarValue + self.model().prependedRowCount)
+
+ def onKeyUp(self):
+ if self.selectionModel().currentIndex().row() == 0:
+ self.vScrollBar.setValue(self.vScrollBar.value() - 1)
+
+ def onKeyDown(self):
+ if self.selectionModel().currentIndex().row() == self.maxRowsInViewport - 1:
+ self.vScrollBar.setValue(self.vScrollBar.value() + 1)
+
+ def onKeyHome(self):
+ self.vScrollBar.setValue(0)
+ self.selectionModel().setCurrentIndex(self.model().createIndex(0, 0), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent)
+
+ def onKeyEnd(self):
+ self.vScrollBar.setValue(self.vScrollBar.maximum())
+ self.selectionModel().setCurrentIndex(self.model().createIndex(min(self.maxRowsInViewport, self.model().totalRowCount) - 1, 0), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent)
+
+ def onKeyPageUp(self):
+ #scroll up only when on the first row
+ if self.selectionModel().currentIndex().row() != 0:
+ return
+ self.vScrollBar.setValue(self.vScrollBar.value() - self.maxRowsInViewport)
+
+ def onKeyPageDown(self):
+ #scroll down only when on the last row
+ if self.selectionModel().currentIndex().row() != self.maxRowsInViewport - 1:
+ return
+ self.vScrollBar.setValue(self.vScrollBar.value() + self.maxRowsInViewport)
+
+ def eventFilter(self, obj, event):
+ if event.type() == QEvent.KeyPress:
+ if event.key() == QtCore.Qt.Key_Up:
+ self.onKeyUp()
+ elif event.key() == QtCore.Qt.Key_Down:
+ self.onKeyDown()
+ elif event.key() == QtCore.Qt.Key_Home:
+ self.onKeyHome()
+ elif event.key() == QtCore.Qt.Key_End:
+ self.onKeyEnd()
+ elif event.key() == QtCore.Qt.Key_PageUp:
+ self.onKeyPageUp()
+ elif event.key() == QtCore.Qt.Key_PageDown:
+ self.onKeyPageDown()
+ elif event.type() == QEvent.Wheel:
+ self.vScrollBar.wheelEvent(event)
+ return False
--- /dev/null
+from PyQt5.QtSql import QSqlDatabase, QSqlQueryModel, QSqlQuery
+import threading
+import sys
+from datetime import datetime, timedelta
+
+class Database:
+ db = None
+ __instance = None
+ DB_IN_MEMORY = ":memory:"
+ DB_TYPE_MEMORY = 0
+ DB_TYPE_FILE = 1
+
+ @staticmethod
+ def instance():
+ if Database.__instance == None:
+ Database.__instance = Database()
+ return Database.__instance
+
+ def __init__(self, dbname="db"):
+ self._lock = threading.RLock()
+ self.db = None
+ self.db_file = Database.DB_IN_MEMORY
+ self.db_name = dbname
+
+ def initialize(self, dbtype=DB_TYPE_MEMORY, dbfile=DB_IN_MEMORY, db_name="db"):
+ if dbtype != Database.DB_TYPE_MEMORY:
+ self.db_file = dbfile
+
+ self.db = QSqlDatabase.addDatabase("QSQLITE", self.db_name)
+ self.db.setDatabaseName(self.db_file)
+ if not self.db.open():
+ print("\n ** Error opening DB: SQLite driver not loaded. DB name: %s\n" % self.db_file)
+ print("\n Available drivers: ", QSqlDatabase.drivers())
+ sys.exit(-1)
+
+ db_status, db_error = self.is_db_ok()
+ if db_status is False:
+ print("db.initialize() error:", db_error)
+ return False, db_error
+
+ self._create_tables()
+ return True, None
+
+ def close(self):
+ try:
+ if self.db.isOpen():
+ self.db.removeDatabase(self.db_name)
+ self.db.close()
+ except Exception as e:
+ print("db.close() exception:", e)
+
+ def is_db_ok(self):
+ # XXX: quick_check may not be fast enough with some DBs on slow
+ # hardware.
+ q = QSqlQuery("PRAGMA quick_check;", self.db)
+ if q.exec_() is not True:
+ print(q.lastError().driverText())
+ return False, q.lastError().driverText()
+
+ if q.next() and q.value(0) != "ok":
+ return False, "Database is corrupted (1)"
+
+ return True, None
+
+ def get_db(self):
+ return self.db
+
+ def get_db_file(self):
+ return self.db_file
+
+ def get_new_qsql_model(self):
+ return QSqlQueryModel()
+
+ def get_db_name(self):
+ return self.db_name
+
+ def _create_tables(self):
+ # https://www.sqlite.org/wal.html
+ if self.db_file == Database.DB_IN_MEMORY:
+ q = QSqlQuery("PRAGMA journal_mode = OFF", self.db)
+ q.exec_()
+ q = QSqlQuery("PRAGMA synchronous = OFF", self.db)
+ q.exec_()
+ q = QSqlQuery("PRAGMA cache_size=10000", self.db)
+ q.exec_()
+ else:
+ q = QSqlQuery("PRAGMA synchronous = NORMAL", self.db)
+ q.exec_()
+
+ q = QSqlQuery("create table if not exists connections (" \
+ "time text, " \
+ "node text, " \
+ "action text, " \
+ "protocol text, " \
+ "src_ip text, " \
+ "src_port text, " \
+ "dst_ip text, " \
+ "dst_host text, " \
+ "dst_port text, " \
+ "uid text, " \
+ "pid text, " \
+ "process text, " \
+ "process_args text, " \
+ "process_cwd text, " \
+ "rule text, " \
+ "UNIQUE(node, action, protocol, src_ip, src_port, dst_ip, dst_port, uid, pid, process, process_args))",
+ self.db)
+ q = QSqlQuery("create index time_index on connections (time)", self.db)
+ q.exec_()
+ q = QSqlQuery("create index action_index on connections (action)", self.db)
+ q.exec_()
+ q = QSqlQuery("create index protocol_index on connections (protocol)", self.db)
+ q.exec_()
+ q = QSqlQuery("create index dst_host_index on connections (dst_host)", self.db)
+ q.exec_()
+ q = QSqlQuery("create index process_index on connections (process)", self.db)
+ q.exec_()
+ q = QSqlQuery("create index dst_ip_index on connections (dst_ip)", self.db)
+ q.exec_()
+ q = QSqlQuery("create index dst_port_index on connections (dst_port)", self.db)
+ q.exec_()
+ q = QSqlQuery("create index rule_index on connections (rule)", self.db)
+ q.exec_()
+ q = QSqlQuery("create index node_index on connections (node)", self.db)
+ q.exec_()
+ q = QSqlQuery("CREATE INDEX details_query_index on connections (process, process_args, uid, pid, dst_ip, dst_host, dst_port, action, node, protocol)", self.db)
+ q.exec_()
+ q = QSqlQuery("create table if not exists rules (" \
+ "time text, " \
+ "node text, " \
+ "name text, " \
+ "enabled text, " \
+ "precedence text, " \
+ "action text, " \
+ "duration text, " \
+ "operator_type text, " \
+ "operator_sensitive text, " \
+ "operator_operand text, " \
+ "operator_data text, " \
+ "UNIQUE(node, name)"
+ ")", self.db)
+ q.exec_()
+ q = QSqlQuery("create index rules_index on rules (time)", self.db)
+ q.exec_()
+
+ q = QSqlQuery("create table if not exists hosts (what text primary key, hits integer)", self.db)
+ q.exec_()
+ q = QSqlQuery("create table if not exists procs (what text primary key, hits integer)", self.db)
+ q.exec_()
+ q = QSqlQuery("create table if not exists addrs (what text primary key, hits integer)", self.db)
+ q.exec_()
+ q = QSqlQuery("create table if not exists ports (what text primary key, hits integer)", self.db)
+ q.exec_()
+ q = QSqlQuery("create table if not exists users (what text primary key, hits integer)", self.db)
+ q.exec_()
+
+ q = QSqlQuery("create table if not exists nodes (" \
+ "addr text primary key," \
+ "hostname text," \
+ "daemon_version text," \
+ "daemon_uptime text," \
+ "daemon_rules text," \
+ "cons text," \
+ "cons_dropped text," \
+ "version text," \
+ "status text, " \
+ "last_connection text)"
+ , self.db)
+ q.exec_()
+
+ def optimize(self):
+ """https://www.sqlite.org/pragma.html#pragma_optimize
+ """
+ q = QSqlQuery("PRAGMA optimize;", self.db)
+ q.exec_()
+
+ def clean(self, table):
+ with self._lock:
+ q = QSqlQuery("delete from " + table, self.db)
+ q.exec_()
+
+ def vacuum(self):
+ q = QSqlQuery("VACUUM;", self.db)
+ q.exec_()
+
+ def clone_db(self, name):
+ return QSqlDatabase.cloneDatabase(self.db, name)
+
+ def clone(self):
+ q = QSqlQuery(".dump", self.db)
+ q.exec_()
+
+ def transaction(self):
+ self.db.transaction()
+
+ def commit(self):
+ self.db.commit()
+
+ def rollback(self):
+ self.db.rollback()
+
+ def get_total_records(self):
+ try:
+ q = QSqlQuery("SELECT count(*) FROM connections", self.db)
+ if q.exec_() and q.first():
+ r = q.value(0)
+ except Exception as e:
+ print("db, get_total_records() error:", e)
+
+ def get_newest_record(self):
+ try:
+ q = QSqlQuery("SELECT time FROM connections ORDER BY 1 DESC LIMIT 1", self.db)
+ if q.exec_() and q.first():
+ return q.value(0)
+ except Exception as e:
+ print("db, get_newest_record() error:", e)
+ return 0
+
+ def get_oldest_record(self):
+ try:
+ q = QSqlQuery("SELECT time FROM connections ORDER BY 1 ASC LIMIT 1", self.db)
+ if q.exec_() and q.first():
+ return q.value(0)
+ except Exception as e:
+ print("db, get_oldest_record() error:", e)
+ return 0
+
+ def purge_oldest(self, max_days_to_keep):
+ try:
+ oldt = self.get_oldest_record()
+ newt = self.get_newest_record()
+ if oldt == None or newt == None or oldt == 0 or newt == 0:
+ return -1
+
+ oldest = datetime.fromisoformat(oldt)
+ newest = datetime.fromisoformat(newt)
+ diff = newest - oldest
+ date_to_purge = datetime.now() - timedelta(days=max_days_to_keep)
+
+ if diff.days >= max_days_to_keep:
+ q = QSqlQuery(self.db)
+ q.prepare("DELETE FROM connections WHERE time < ?")
+ q.bindValue(0, str(date_to_purge))
+ if q.exec_():
+ print("purge_oldest() {0} records deleted".format(q.numRowsAffected()))
+ return q.numRowsAffected()
+ except Exception as e:
+ print("db, purge_oldest() error:", e)
+
+ return -1
+
+ def select(self, qstr):
+ try:
+ return QSqlQuery(qstr, self.db)
+ except Exception as e:
+ print("db, select() exception: ", e)
+
+ return None
+
+ def remove(self, qstr):
+ try:
+ q = QSqlQuery(qstr, self.db)
+ if q.exec_():
+ return True
+ else:
+ print("db, remove() ERROR: ", qstr)
+ print(q.lastError().driverText())
+ except Exception as e:
+ print("db, remove exception: ", e)
+
+ return False
+
+ def _insert(self, query_str, columns):
+ with self._lock:
+ try:
+
+ q = QSqlQuery(self.db)
+ q.prepare(query_str)
+ for idx, v in enumerate(columns):
+ q.bindValue(idx, v)
+ if q.exec_():
+ return True
+ else:
+ print("_insert() ERROR", query_str)
+ print(q.lastError().driverText())
+
+ except Exception as e:
+ print("_insert exception", e)
+ finally:
+ q.finish()
+
+ return False
+
+ def insert(self, table, fields, columns, update_field=None, update_values=None, action_on_conflict="REPLACE"):
+ if update_field != None:
+ action_on_conflict = ""
+ else:
+ action_on_conflict = "OR " + action_on_conflict
+
+ qstr = "INSERT " + action_on_conflict + " INTO " + table + " " + fields + " VALUES("
+ update_fields=""
+ for col in columns:
+ qstr += "?,"
+ qstr = qstr[0:len(qstr)-1] + ")"
+
+ if update_field != None:
+ # NOTE: UPSERTS on sqlite are only supported from v3.24 on.
+ # On Ubuntu16.04/18 for example (v3.11/3.22) updating a record on conflict
+ # fails with "Parameter count error"
+ qstr += " ON CONFLICT (" + update_field + ") DO UPDATE SET "
+ for idx, field in enumerate(update_values):
+ qstr += str(field) + "=excluded." + str(field) + ","
+
+ qstr = qstr[0:len(qstr)-1]
+
+ return self._insert(qstr, columns)
+
+ def update(self, table, fields, values, condition, action_on_conflict="OR IGNORE"):
+ qstr = "UPDATE " + action_on_conflict + " " + table + " SET " + fields + " WHERE " + condition
+ try:
+ with self._lock:
+ q = QSqlQuery(qstr, self.db)
+ q.prepare(qstr)
+ for idx, v in enumerate(values):
+ q.bindValue(idx, v)
+ if not q.exec_():
+ print("update ERROR", qstr)
+ print(q.lastError().driverText())
+
+ except Exception as e:
+ print("update() exception:", e)
+ finally:
+ q.finish()
+
+ def _insert_batch(self, query_str, fields, values):
+ result=True
+ with self._lock:
+ try:
+ q = QSqlQuery(self.db)
+ q.prepare(query_str)
+ q.addBindValue(fields)
+ q.addBindValue(values)
+ if not q.execBatch():
+ print("_insert_batch() error", query_str)
+ print(q.lastError().driverText())
+
+ result=False
+ except Exception as e:
+ print("_insert_batch() exception:", e)
+ finally:
+ q.finish()
+
+ return result
+
+ def insert_batch(self, table, db_fields, db_columns, fields, values, update_field=None, update_value=None, action_on_conflict="REPLACE"):
+ action = "OR " + action_on_conflict
+ if update_field != None:
+ action = ""
+
+ qstr = "INSERT " + action + " INTO " + table + " (" + db_fields[0] + "," + db_fields[1] + ") VALUES("
+ for idx in db_columns:
+ qstr += "?,"
+ qstr = qstr[0:len(qstr)-1] + ")"
+
+ if self._insert_batch(qstr, fields, values) == False:
+ self.update_batch(table, db_fields, db_columns, fields, values, update_field, update_value, action_on_conflict)
+
+ def update_batch(self, table, db_fields, db_columns, fields, values, update_field=None, update_value=None, action_on_conflict="REPLACE"):
+ for idx, i in enumerate(values):
+ s = "UPDATE " + table + " SET " + "%s=(select hits from %s)+%s" % (db_fields[1], table, values[idx])
+ s += " WHERE %s=\"%s\"," % (db_fields[0], fields[idx])
+ s = s[0:len(s)-1]
+ with self._lock:
+ q = QSqlQuery(s, self.db)
+ if not q.exec_():
+ print("update batch ERROR", s)
+ print(q.lastError().driverText())
+
+ def dump(self):
+ q = QSqlQuery(".dump", db=self.db)
+ q.exec_()
+
+ def get_query(self, table, fields):
+ return "SELECT " + fields + " FROM " + table
+
+ def empty_rule(self, name=""):
+ if name == "":
+ return
+ qstr = "DELETE FROM connections WHERE rule = ?"
+
+ with self._lock:
+ q = QSqlQuery(qstr, self.db)
+ q.prepare(qstr)
+ q.addBindValue(name)
+ if not q.exec_():
+ print("db, empty_rule() ERROR: ", qstr)
+ print(q.lastError().driverText())
+
+ def delete_rule(self, name, node_addr):
+ qstr = "DELETE FROM rules WHERE name=?"
+ if node_addr != None:
+ qstr = qstr + " AND node=?"
+
+ with self._lock:
+ q = QSqlQuery(qstr, self.db)
+ q.prepare(qstr)
+ q.addBindValue(name)
+ if node_addr != None:
+ q.addBindValue(node_addr)
+ if not q.exec_():
+ print("db, delete_rule() ERROR: ", qstr)
+ print(q.lastError().driverText())
+
+ def get_rule(self, rule_name, node_addr=None):
+ """
+ get rule records, given the name of the rule and the node
+ """
+ qstr = "SELECT * from rules WHERE name=?"
+ if node_addr != None:
+ qstr = qstr + " AND node=?"
+
+ q = QSqlQuery(qstr, self.db)
+ q.prepare(qstr)
+ q.addBindValue(rule_name)
+ if node_addr != None:
+ q.addBindValue(node_addr)
+ q.exec_()
+
+ return q
+
+ def insert_rule(self, rule, node_addr):
+ self.insert("rules",
+ "(time, node, name, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)",
+ (datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ node_addr, rule.name,
+ str(rule.enabled), str(rule.precedence),
+ rule.action, rule.duration, rule.operator.type,
+ str(rule.operator.sensitive), rule.operator.operand, rule.operator.data),
+ action_on_conflict="IGNORE")
--- /dev/null
+from threading import Lock
+import configparser
+import pyinotify
+import threading
+import glob
+import os
+import re
+import shutil
+import locale
+
+DESKTOP_PATHS = tuple([
+ os.path.join(d, 'applications')
+ for d in os.getenv('XDG_DATA_DIRS', '/usr/share/').split(':')
+])
+
+class LinuxDesktopParser(threading.Thread):
+ def __init__(self):
+ threading.Thread.__init__(self)
+ self.lock = Lock()
+ self.daemon = True
+ self.running = False
+ self.apps = {}
+ self.apps_by_name = {}
+ self.get_locale()
+ # some things are just weird
+ # (not really, i don't want to keep track of parent pids
+ # just because of icons though, this hack is way easier)
+ self.fixes = {
+ '/opt/google/chrome/chrome': '/opt/google/chrome/google-chrome',
+ '/usr/lib/firefox/firefox': '/usr/lib/firefox/firefox.sh',
+ '/usr/bin/pidgin.orig': '/usr/bin/pidgin'
+ }
+
+ for desktop_path in DESKTOP_PATHS:
+ if not os.path.exists(desktop_path):
+ continue
+ for desktop_file in glob.glob(os.path.join(desktop_path, '*.desktop')):
+ self._parse_desktop_file(desktop_file)
+
+ self.start()
+
+ def get_locale(self):
+ try:
+ self.locale = locale.getlocale()[0]
+ self.locale_country = self.locale.split("_")[0]
+ except Exception:
+ self.locale = ""
+ self.locale_country = ""
+
+ def _parse_exec(self, cmd):
+ # remove stuff like %U
+ cmd = re.sub( r'%[a-zA-Z]+', '', cmd)
+ # remove 'env .... command'
+ cmd = re.sub( r'^env\s+[^\s]+\s', '', cmd)
+ # split && trim
+ cmd = cmd.split(' ')[0].strip()
+ # remove quotes
+ cmd = re.sub( r'["\']+', '', cmd)
+ # check if we need to resolve the path
+ if len(cmd) > 0 and cmd[0] != '/':
+ for path in os.environ["PATH"].split(os.pathsep):
+ filename = os.path.join(path, cmd)
+ if os.path.exists(filename):
+ cmd = filename
+ break
+
+ return cmd
+
+ def get_app_description(self, parser):
+ try:
+ desc = parser.get('Desktop Entry', 'Comment[%s]' % self.locale_country, raw=True, fallback=None)
+ if desc == None:
+ desc = parser.get('Desktop Entry', 'Comment[%s]' % self.locale, raw=True, fallback=None)
+
+ if desc == None:
+ desc = parser.get('Desktop Entry', 'Comment', raw=True, fallback=None)
+
+ return desc
+ except:
+ return None
+
+ def discover_app_icon(self, app_name):
+ # more hacks
+ # normally qt will find icons if the system if configured properly.
+ # if it's not, qt won't be able to find the icon by using QIcon().fromTheme(""),
+ # so we fallback to try to determine if the icon exist in some well known system paths.
+ icon_dirs = ("/usr/share/icons/gnome/48x48/apps/", "/usr/share/pixmaps/", "/usr/share/icons/hicolor/48x48/apps/")
+ icon_exts = (".png", ".xpm", ".svg")
+ for idir in icon_dirs:
+ for iext in icon_exts:
+ if iext in app_name:
+ iconPath = idir + app_name
+ if os.path.exists(iconPath):
+ return iconPath
+ else:
+ iconPath = idir + app_name + iext
+ if os.path.exists(iconPath):
+ return iconPath
+
+ def _parse_desktop_file(self, desktop_path):
+ parser = configparser.ConfigParser(strict=False) # Allow duplicate config entries
+ try:
+ basename = os.path.basename(desktop_path)[:-8]
+ parser.read(desktop_path, 'utf8')
+
+ cmd = parser.get('Desktop Entry', 'exec', raw=True, fallback=None)
+ if cmd == None:
+ cmd = parser.get('Desktop Entry', 'Exec', raw=True, fallback=None)
+ if cmd is not None:
+ cmd = self._parse_exec(cmd)
+ icon = parser.get('Desktop Entry', 'Icon', raw=True, fallback=None)
+ name = parser.get('Desktop Entry', 'Name', raw=True, fallback=None)
+ desc = self.get_app_description(parser)
+
+ if icon == None:
+ # Some .desktop files doesn't have the Icon entry
+ # FIXME: even if we return an icon, if the DE is not properly configured,
+ # it won't be loaded/displayed.
+ icon = self.discover_app_icon(basename)
+
+ with self.lock:
+ # The Exec entry may have an absolute path to a binary or just the binary with parameters.
+ # /path/binary or binary, so save both
+ self.apps[cmd] = (name, icon, desc, desktop_path)
+ self.apps[basename] = (name, icon, desc, desktop_path)
+ # if the command is a symlink, add the real binary too
+ if os.path.islink(cmd):
+ link_to = os.path.realpath(cmd)
+ self.apps[link_to] = (name, icon, desc, desktop_path)
+ except:
+ print("Exception parsing .desktop file ", desktop_path)
+
+ def get_info_by_path(self, path, default_icon):
+ def_name = os.path.basename(path)
+ # apply fixes
+ for orig, to in self.fixes.items():
+ if path == orig:
+ path = to
+ break
+
+ app_name = self.apps.get(path)
+ if app_name == None:
+ # last try to get a default terminal icon
+ for def_icon in ("utilities-terminal", "gnome-terminal", "xfce-terminal"):
+ test = self.apps.get(def_name, (def_name, def_icon, "", None))
+ if test != None:
+ return test
+
+ return self.apps.get(def_name, (def_name, default_icon, "", None))
+
+ return self.apps.get(path, (def_name, default_icon, "", None))
+
+ def get_info_by_binname(self, name, default_icon):
+ def_name = os.path.basename(name)
+ return self.apps.get(def_name, (def_name, default_icon, None))
+
+ def run(self):
+ self.running = True
+ wm = pyinotify.WatchManager()
+ notifier = pyinotify.Notifier(wm)
+
+ def inotify_callback(event):
+ if event.mask == pyinotify.IN_CLOSE_WRITE:
+ self._parse_desktop_file(event.pathname)
+
+ elif event.mask == pyinotify.IN_DELETE:
+ with self.lock:
+ for cmd, data in self.apps.items():
+ if data[2] == event.pathname:
+ del self.apps[cmd]
+ break
+
+ for p in DESKTOP_PATHS:
+ if os.path.exists(p):
+ wm.add_watch(p,
+ pyinotify.IN_CLOSE_WRITE | pyinotify.IN_DELETE,
+ inotify_callback)
+ notifier.loop()
--- /dev/null
+import sys
+import time
+import os
+import json
+
+from PyQt5 import QtCore, QtGui, uic, QtWidgets
+from PyQt5.QtCore import QCoreApplication as QC
+
+from opensnitch.config import Config
+from opensnitch.nodes import Nodes
+from opensnitch.database import Database
+from opensnitch.utils import Message, QuickHelp, Themes
+from opensnitch.notifications import DesktopNotifications
+
+from opensnitch import ui_pb2
+
+DIALOG_UI_PATH = "%s/../res/preferences.ui" % os.path.dirname(sys.modules[__name__].__file__)
+class PreferencesDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
+
+ LOG_TAG = "[Preferences] "
+ _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
+ saved = QtCore.pyqtSignal()
+
+ TAB_POPUPS = 0
+ TAB_UI = 1
+ TAB_NODES = 2
+ TAB_DB = 3
+
+ SUM = 1
+ REST = 0
+
+ def __init__(self, parent=None, appicon=None):
+ QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)
+
+ self._themes = Themes.instance()
+ self._saved_theme = ""
+
+ self._cfg = Config.get()
+ self._nodes = Nodes.instance()
+ self._db = Database.instance()
+
+ self._notification_callback.connect(self._cb_notification_callback)
+ self._notifications_sent = {}
+ self._desktop_notifications = DesktopNotifications()
+
+ self.setupUi(self)
+ self.setWindowIcon(appicon)
+
+ self.dbFileButton.setVisible(False)
+ self.dbLabel.setVisible(False)
+ self.dbType = None
+
+ self.acceptButton.clicked.connect(self._cb_accept_button_clicked)
+ self.applyButton.clicked.connect(self._cb_apply_button_clicked)
+ self.cancelButton.clicked.connect(self._cb_cancel_button_clicked)
+ self.helpButton.clicked.connect(self._cb_help_button_clicked)
+ self.popupsCheck.clicked.connect(self._cb_popups_check_toggled)
+ self.dbFileButton.clicked.connect(self._cb_file_db_clicked)
+ self.checkUIRules.toggled.connect(self._cb_check_ui_rules_toggled)
+ self.cmdTimeoutUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUITimeout, self.SUM))
+ self.cmdTimeoutDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinUITimeout, self.REST))
+ self.cmdDBMaxDaysUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBMaxDays, self.SUM))
+ self.cmdDBMaxDaysDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBMaxDays, self.REST))
+ self.cmdDBPurgesUp.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBPurgeInterval, self.SUM))
+ self.cmdDBPurgesDown.clicked.connect(lambda: self._cb_cmd_spin_clicked(self.spinDBPurgeInterval, self.REST))
+ self.cmdTestNotifs.clicked.connect(self._cb_test_notifs_clicked)
+ self.radioSysNotifs.clicked.connect(self._cb_radio_system_notifications)
+ self.helpButton.setToolTipDuration(30 * 1000)
+
+ if QtGui.QIcon.hasThemeIcon("emblem-default") == False:
+ self.applyButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogApplyButton")))
+ self.cancelButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogCloseButton")))
+ self.acceptButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogSaveButton")))
+ self.dbFileButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DirOpenIcon")))
+
+ if QtGui.QIcon.hasThemeIcon("list-add") == False:
+ self.cmdTimeoutUp.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowUp")))
+ self.cmdTimeoutDown.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowDown")))
+ self.cmdDBMaxDaysUp.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowUp")))
+ self.cmdDBMaxDaysDown.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowDown")))
+ self.cmdDBPurgesUp.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowUp")))
+ self.cmdDBPurgesDown.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowDown")))
+
+ def showEvent(self, event):
+ super(PreferencesDialog, self).showEvent(event)
+
+ try:
+ self._settingsSaved = False
+ self._reset_status_message()
+ self._hide_status_label()
+ self.comboNodes.clear()
+
+ self._node_list = self._nodes.get()
+ for addr in self._node_list:
+ self.comboNodes.addItem(addr)
+
+ if len(self._node_list) == 0:
+ self._reset_node_settings()
+ except Exception as e:
+ print(self.LOG_TAG + "exception loading nodes", e)
+
+ self._load_settings()
+
+ # connect the signals after loading settings, to avoid firing
+ # the signals
+ self.comboNodes.currentIndexChanged.connect(self._cb_node_combo_changed)
+ self.comboNodeAction.currentIndexChanged.connect(self._cb_node_needs_update)
+ self.comboNodeDuration.currentIndexChanged.connect(self._cb_node_needs_update)
+ self.comboNodeMonitorMethod.currentIndexChanged.connect(self._cb_node_needs_update)
+ self.comboNodeLogLevel.currentIndexChanged.connect(self._cb_node_needs_update)
+ self.comboNodeLogFile.currentIndexChanged.connect(self._cb_node_needs_update)
+ self.comboNodeAddress.currentTextChanged.connect(self._cb_node_needs_update)
+ self.checkInterceptUnknown.clicked.connect(self._cb_node_needs_update)
+ self.checkApplyToNodes.clicked.connect(self._cb_node_needs_update)
+ self.comboDBType.currentIndexChanged.connect(self._cb_db_type_changed)
+ self.checkDBMaxDays.toggled.connect(self._cb_db_max_days_toggled)
+
+ # True when any node option changes
+ self._node_needs_update = False
+
+ def _load_themes(self):
+ theme_idx, self._saved_theme = self._themes.get_saved_theme()
+
+ self.labelThemeError.setVisible(False)
+ self.labelThemeError.setText("")
+ self.comboUITheme.clear()
+ self.comboUITheme.addItem(QC.translate("preferences", "System"))
+ if self._themes.available():
+ themes = self._themes.list_themes()
+ self.comboUITheme.addItems(themes)
+ else:
+ self._saved_theme = ""
+ self.labelThemeError.setStyleSheet('color: red')
+ self.labelThemeError.setVisible(True)
+ self.labelThemeError.setText(QC.translate("preferences", "Themes not available. Install qt-material: pip3 install qt-material"))
+
+ self.comboUITheme.setCurrentIndex(theme_idx)
+
+ def _load_settings(self):
+ self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY)
+ self._default_target = self._cfg.getInt(self._cfg.DEFAULT_TARGET_KEY, 0)
+ self._default_timeout = self._cfg.getInt(self._cfg.DEFAULT_TIMEOUT_KEY, Config.DEFAULT_TIMEOUT)
+ self._disable_popups = self._cfg.getBool(self._cfg.DEFAULT_DISABLE_POPUPS)
+
+ if self._cfg.hasKey(self._cfg.DEFAULT_DURATION_KEY):
+ self._default_duration = self._cfg.getInt(self._cfg.DEFAULT_DURATION_KEY)
+ else:
+ self._default_duration = self._cfg.DEFAULT_DURATION_IDX
+
+ self.comboUIDuration.setCurrentIndex(self._default_duration)
+ self.comboUIDialogPos.setCurrentIndex(self._cfg.getInt(self._cfg.DEFAULT_POPUP_POSITION))
+
+ self.comboUIRules.setCurrentIndex(self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES))
+ self.checkUIRules.setChecked(self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES))
+ self.comboUIRules.setEnabled(self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES))
+ #self._set_rules_duration_filter()
+
+ self._cfg.setRulesDurationFilter(
+ self._cfg.getBool(self._cfg.DEFAULT_IGNORE_RULES),
+ self._cfg.getInt(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES)
+ )
+
+ self.comboUIAction.setCurrentIndex(self._default_action)
+ self.comboUITarget.setCurrentIndex(self._default_target)
+ self.spinUITimeout.setValue(self._default_timeout)
+ self.spinUITimeout.setEnabled(not self._disable_popups)
+ self.popupsCheck.setChecked(self._disable_popups)
+
+ self.showAdvancedCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED))
+ self.dstIPCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP))
+ self.dstPortCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT))
+ self.uidCheck.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_UID))
+
+ # by default, if no configuration exists, enable notifications.
+ self.groupNotifs.setChecked(self._cfg.getBool(Config.NOTIFICATIONS_ENABLED, True))
+ self.radioSysNotifs.setChecked(
+ True if self._cfg.getInt(Config.NOTIFICATIONS_TYPE) == Config.NOTIFICATION_TYPE_SYSTEM and self._desktop_notifications.is_available() == True else False
+ )
+ self.radioQtNotifs.setChecked(
+ True if self._cfg.getInt(Config.NOTIFICATIONS_TYPE) == Config.NOTIFICATION_TYPE_QT or self._desktop_notifications.is_available() == False else False
+ )
+
+ self.dbType = self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY)
+ self.comboDBType.setCurrentIndex(self.dbType)
+ if self.comboDBType.currentIndex() != Database.DB_TYPE_MEMORY:
+ self.dbFileButton.setVisible(True)
+ self.dbLabel.setVisible(True)
+ self.dbLabel.setText(self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY))
+ dbMaxDays = self._cfg.getInt(self._cfg.DEFAULT_DB_MAX_DAYS, 1)
+ dbPurgeInterval = self._cfg.getInt(self._cfg.DEFAULT_DB_PURGE_INTERVAL, 5)
+ self._enable_db_cleaner_options(self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST), dbMaxDays)
+ self.spinDBMaxDays.setValue(dbMaxDays)
+ self.spinDBPurgeInterval.setValue(dbPurgeInterval)
+
+ self._load_themes()
+ self._load_node_settings()
+ self._load_ui_columns_config()
+
+ def _load_node_settings(self):
+ addr = self.comboNodes.currentText()
+ if addr != "":
+ try:
+ node_data = self._node_list[addr]['data']
+ self.labelNodeVersion.setText(node_data.version)
+ self.labelNodeName.setText(node_data.name)
+ self.comboNodeLogLevel.setCurrentIndex(node_data.logLevel)
+
+ node_config = json.loads(node_data.config)
+ self.comboNodeAction.setCurrentText(node_config['DefaultAction'])
+ self.comboNodeDuration.setCurrentText(node_config['DefaultDuration'])
+ self.comboNodeMonitorMethod.setCurrentText(node_config['ProcMonitorMethod'])
+ self.checkInterceptUnknown.setChecked(node_config['InterceptUnknown'])
+ self.comboNodeLogLevel.setCurrentIndex(int(node_config['LogLevel']))
+
+ if node_config.get('Server') != None:
+ self.comboNodeAddress.setEnabled(True)
+ self.comboNodeLogFile.setEnabled(True)
+
+ self.comboNodeAddress.setCurrentText(node_config['Server']['Address'])
+ self.comboNodeLogFile.setCurrentText(node_config['Server']['LogFile'])
+ else:
+ self.comboNodeAddress.setEnabled(False)
+ self.comboNodeLogFile.setEnabled(False)
+ except Exception as e:
+ print(self.LOG_TAG + "exception loading config: ", e)
+
+ def _load_node_config(self, addr):
+ try:
+ if self.comboNodeAddress.currentText() == "":
+ return None, QC.translate("preferences", "Server address can not be empty")
+
+ node_action = Config.ACTION_DENY
+ if self.comboNodeAction.currentIndex() == 1:
+ node_action = Config.ACTION_ALLOW
+
+ node_duration = Config.DURATION_ONCE
+ if self.comboNodeDuration.currentIndex() == 1:
+ node_duration = Config.DURATION_UNTIL_RESTART
+ elif self.comboNodeDuration.currentIndex() == 2:
+ node_duration = Config.DURATION_ALWAYS
+
+ node_config = json.loads(self._nodes.get_node_config(addr))
+ node_config['DefaultAction'] = node_action
+ node_config['DefaultDuration'] = node_duration
+ node_config['ProcMonitorMethod'] = self.comboNodeMonitorMethod.currentText()
+ node_config['LogLevel'] = self.comboNodeLogLevel.currentIndex()
+ node_config['InterceptUnknown'] = self.checkInterceptUnknown.isChecked()
+
+ if node_config.get('Server') != None:
+ # skip setting Server Address if we're applying the config to all nodes
+ if self.checkApplyToNodes.isChecked():
+ node_config['Server']['Address'] = self.comboNodeAddress.currentText()
+ node_config['Server']['LogFile'] = self.comboNodeLogFile.currentText()
+ else:
+ print(addr, " doesn't have Server item")
+ return json.dumps(node_config, indent=" "), None
+ except Exception as e:
+ print(self.LOG_TAG + "exception loading node config on %s: " % addr, e)
+
+ return None, QC.translate("preferences", "Error loading {0} configuration").format(addr)
+
+ def _load_ui_columns_config(self):
+ cols = self._cfg.getSettings(Config.STATS_SHOW_COLUMNS)
+ if cols == None:
+ return
+
+ for c in range(7):
+ checked = str(c) in cols
+ if c == 0:
+ self.checkHideTime.setChecked(checked)
+ elif c == 1:
+ self.checkHideNode.setChecked(checked)
+ elif c == 2:
+ self.checkHideAction.setChecked(checked)
+ elif c == 3:
+ self.checkHideDst.setChecked(checked)
+ elif c == 4:
+ self.checkHideProto.setChecked(checked)
+ elif c == 5:
+ self.checkHideProc.setChecked(checked)
+ elif c == 6:
+ self.checkHideRule.setChecked(checked)
+
+ def _reset_node_settings(self):
+ self.comboNodeAction.setCurrentIndex(0)
+ self.comboNodeDuration.setCurrentIndex(0)
+ self.comboNodeMonitorMethod.setCurrentIndex(0)
+ self.checkInterceptUnknown.setChecked(False)
+ self.comboNodeLogLevel.setCurrentIndex(0)
+ self.labelNodeName.setText("")
+ self.labelNodeVersion.setText("")
+
+ def _save_settings(self):
+ self._save_ui_config()
+ self._save_db_config()
+
+ if self.tabWidget.currentIndex() == self.TAB_NODES:
+ self._show_status_label()
+
+ addr = self.comboNodes.currentText()
+ if (self._node_needs_update or self.checkApplyToNodes.isChecked()) and addr != "":
+ try:
+ notif = ui_pb2.Notification(
+ id=int(str(time.time()).replace(".", "")),
+ type=ui_pb2.CHANGE_CONFIG,
+ data="",
+ rules=[])
+ if self.checkApplyToNodes.isChecked():
+ for addr in self._nodes.get_nodes():
+ error = self._save_node_config(notif, addr)
+ if error != None:
+ self._set_status_error(error)
+ return
+ else:
+ error = self._save_node_config(notif, addr)
+ if error != None:
+ self._set_status_error(error)
+ return
+ except Exception as e:
+ print(self.LOG_TAG + "exception saving config: ", e)
+ self._set_status_error(QC.translate("preferences", "Exception saving config: {0}").format(str(e)))
+
+ self._node_needs_update = False
+
+ self.saved.emit()
+ self._settingsSaved = True
+
+ def _save_db_config(self):
+ dbtype = self.comboDBType.currentIndex()
+ self._cfg.setSettings(Config.DEFAULT_DB_TYPE_KEY, dbtype)
+ self._cfg.setSettings(Config.DEFAULT_DB_PURGE_OLDEST, bool(self.checkDBMaxDays.isChecked()))
+ self._cfg.setSettings(Config.DEFAULT_DB_MAX_DAYS, int(self.spinDBMaxDays.value()))
+ self._cfg.setSettings(Config.DEFAULT_DB_PURGE_INTERVAL, int(self.spinDBPurgeInterval.value()))
+
+ if self.comboDBType.currentIndex() == self.dbType:
+ return
+
+ if dbtype == self._db.get_db_file():
+ return
+
+ if self.comboDBType.currentIndex() != Database.DB_TYPE_MEMORY:
+ if self.dbLabel.text() != "":
+ self._cfg.setSettings(Config.DEFAULT_DB_FILE_KEY, self.dbLabel.text())
+ else:
+ Message.ok(
+ QC.translate("preferences", "Warning"),
+ QC.translate("preferences", "You must select a file for the database<br>or choose \"In memory\" type."),
+ QtWidgets.QMessageBox.Warning)
+ return
+
+ Message.ok(
+ QC.translate("preferences", "DB type changed"),
+ QC.translate("preferences", "Restart the GUI in order effects to take effect"),
+ QtWidgets.QMessageBox.Warning)
+
+ self.dbType = self.comboDBType.currentIndex()
+
+ def _save_ui_config(self):
+ self._save_ui_columns_config()
+
+ self._cfg.setSettings(self._cfg.DEFAULT_IGNORE_TEMPORARY_RULES, int(self.comboUIRules.currentIndex()))
+ self._cfg.setSettings(self._cfg.DEFAULT_IGNORE_RULES, bool(self.checkUIRules.isChecked()))
+ #self._set_rules_duration_filter()
+ self._cfg.setRulesDurationFilter(
+ bool(self.checkUIRules.isChecked()),
+ int(self.comboUIRules.currentIndex())
+ )
+
+ self._cfg.setSettings(self._cfg.DEFAULT_ACTION_KEY, self.comboUIAction.currentIndex())
+ self._cfg.setSettings(self._cfg.DEFAULT_DURATION_KEY, int(self.comboUIDuration.currentIndex()))
+ self._cfg.setSettings(self._cfg.DEFAULT_TARGET_KEY, self.comboUITarget.currentIndex())
+ self._cfg.setSettings(self._cfg.DEFAULT_TIMEOUT_KEY, self.spinUITimeout.value())
+ self._cfg.setSettings(self._cfg.DEFAULT_DISABLE_POPUPS, bool(self.popupsCheck.isChecked()))
+ self._cfg.setSettings(self._cfg.DEFAULT_POPUP_POSITION, int(self.comboUIDialogPos.currentIndex()))
+
+ self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED, bool(self.showAdvancedCheck.isChecked()))
+ self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP, bool(self.dstIPCheck.isChecked()))
+ self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT, bool(self.dstPortCheck.isChecked()))
+ self._cfg.setSettings(self._cfg.DEFAULT_POPUP_ADVANCED_UID, bool(self.uidCheck.isChecked()))
+
+ self._cfg.setSettings(self._cfg.NOTIFICATIONS_ENABLED, bool(self.groupNotifs.isChecked()))
+ self._cfg.setSettings(self._cfg.NOTIFICATIONS_TYPE,
+ int(Config.NOTIFICATION_TYPE_SYSTEM if self.radioSysNotifs.isChecked() else Config.NOTIFICATION_TYPE_QT))
+
+ self._themes.save_theme(self.comboUITheme.currentIndex(), self.comboUITheme.currentText())
+ if self._themes.available() and self._saved_theme != self.comboUITheme.currentText():
+ Message.ok(
+ QC.translate("preferences", "UI theme changed"),
+ QC.translate("preferences", "Restart the GUI in order to apply the new theme"),
+ QtWidgets.QMessageBox.Warning)
+
+ # this is a workaround for not display pop-ups.
+ # see #79 for more information.
+ if self.popupsCheck.isChecked():
+ self._cfg.setSettings(self._cfg.DEFAULT_TIMEOUT_KEY, 0)
+
+ def _save_ui_columns_config(self):
+ cols=list()
+ if self.checkHideTime.isChecked():
+ cols.append("0")
+ if self.checkHideNode.isChecked():
+ cols.append("1")
+ if self.checkHideAction.isChecked():
+ cols.append("2")
+ if self.checkHideDst.isChecked():
+ cols.append("3")
+ if self.checkHideProto.isChecked():
+ cols.append("4")
+ if self.checkHideProc.isChecked():
+ cols.append("5")
+ if self.checkHideRule.isChecked():
+ cols.append("6")
+
+ self._cfg.setSettings(Config.STATS_SHOW_COLUMNS, cols)
+
+ def _save_node_config(self, notifObject, addr):
+ try:
+ self._set_status_message(QC.translate("preferences", "Applying configuration on {0} ...").format(addr))
+ notifObject.data, error = self._load_node_config(addr)
+ if error != None:
+ return error
+
+ if addr.startswith("unix://"):
+ self._cfg.setSettings(self._cfg.DEFAULT_DEFAULT_SERVER_ADDR, self.comboNodeAddress.currentText())
+ else:
+ self._nodes.save_node_config(addr, notifObject.data)
+ nid = self._nodes.send_notification(addr, notifObject, self._notification_callback)
+
+ self._notifications_sent[nid] = notifObject
+ except Exception as e:
+ print(self.LOG_TAG + "exception saving node config on %s: " % addr, e)
+ self._set_status_error(QC.translate("Exception saving node config {0}: {1}").format((addr, str(e))))
+ return addr + ": " + str(e)
+
+ return None
+
+ def _hide_status_label(self):
+ self.statusLabel.hide()
+
+ def _show_status_label(self):
+ self.statusLabel.show()
+
+ def _set_status_error(self, msg):
+ self._show_status_label()
+ self.statusLabel.setStyleSheet('color: red')
+ self.statusLabel.setText(msg)
+
+ def _set_status_successful(self, msg):
+ self._show_status_label()
+ self.statusLabel.setStyleSheet('color: green')
+ self.statusLabel.setText(msg)
+
+ def _set_status_message(self, msg):
+ self._show_status_label()
+ self.statusLabel.setStyleSheet('color: darkorange')
+ self.statusLabel.setText(msg)
+
+ def _reset_status_message(self):
+ self.statusLabel.setText("")
+ self._hide_status_label()
+
+ def _enable_db_cleaner_options(self, enable, db_max_days):
+ self.checkDBMaxDays.setChecked(enable)
+ self.spinDBMaxDays.setEnabled(enable)
+ self.spinDBPurgeInterval.setEnabled(enable)
+ self.labelDBPurgeInterval.setEnabled(enable)
+ self.cmdDBMaxDaysUp.setEnabled(enable)
+ self.cmdDBMaxDaysDown.setEnabled(enable)
+ self.cmdDBPurgesUp.setEnabled(enable)
+ self.cmdDBPurgesDown.setEnabled(enable)
+
+ @QtCore.pyqtSlot(ui_pb2.NotificationReply)
+ def _cb_notification_callback(self, reply):
+ #print(self.LOG_TAG, "Config notification received: ", reply.id, reply.code)
+ if reply.id in self._notifications_sent:
+ if reply.code == ui_pb2.OK:
+ self._set_status_successful(QC.translate("preferences", "Configuration applied."))
+ else:
+ self._set_status_error(QC.translate("preferences", "Error applying configuration: {0}").format(reply.data))
+
+ del self._notifications_sent[reply.id]
+
+ def _cb_file_db_clicked(self):
+ options = QtWidgets.QFileDialog.Options()
+ fileName, _ = QtWidgets.QFileDialog.getSaveFileName(self, "", "","All Files (*)", options=options)
+ if fileName:
+ self.dbLabel.setText(fileName)
+
+ def _cb_db_type_changed(self):
+ if self.comboDBType.currentIndex() == Database.DB_TYPE_MEMORY:
+ self.dbFileButton.setVisible(False)
+ self.dbLabel.setVisible(False)
+ else:
+ self.dbFileButton.setVisible(True)
+ self.dbLabel.setVisible(True)
+
+ def _cb_accept_button_clicked(self):
+ self.accept()
+ if not self._settingsSaved:
+ self._save_settings()
+
+ def _cb_apply_button_clicked(self):
+ self._save_settings()
+
+ def _cb_cancel_button_clicked(self):
+ self.reject()
+
+ def _cb_help_button_clicked(self):
+ QuickHelp.show(
+ QC.translate("preferences",
+ "Hover the mouse over the texts to display the help<br><br>Don't forget to visit the wiki: <a href=\"{0}\">{0}</a>"
+ ).format(Config.HELP_URL)
+ )
+
+ def _cb_popups_check_toggled(self, checked):
+ self.spinUITimeout.setEnabled(not checked)
+ if not checked:
+ self.spinUITimeout.setValue(15)
+
+ def _cb_node_combo_changed(self, index):
+ self._load_node_settings()
+
+ def _cb_node_needs_update(self):
+ self._node_needs_update = True
+
+ def _cb_check_ui_rules_toggled(self, state):
+ self.comboUIRules.setEnabled(state)
+
+ def _cb_db_max_days_toggled(self, state):
+ self._enable_db_cleaner_options(state, 1)
+
+ def _cb_cmd_spin_clicked(self, spinWidget, operation):
+ if operation == self.SUM:
+ spinWidget.setValue(spinWidget.value() + 1)
+ else:
+ spinWidget.setValue(spinWidget.value() - 1)
+
+ def _cb_radio_system_notifications(self):
+ if self._desktop_notifications.is_available() == False:
+ self.radioSysNotifs.setChecked(False)
+ self.radioQtNotifs.setChecked(True)
+ self._set_status_error(QC.translate("notifications", "System notifications are not available, you need to install python3-notify2."))
+ return
+
+ def _cb_test_notifs_clicked(self):
+ if self._desktop_notifications.is_available() == False:
+ self._set_status_error(QC.translate("notifications", "System notifications are not available, you need to install python3-notify2."))
+ return
+
+ if self.radioSysNotifs.isChecked():
+ self._desktop_notifications.show("title", "body")
+ else:
+ pass
--- /dev/null
+import os
+import sys
+import json
+
+from PyQt5 import QtCore, QtGui, uic, QtWidgets
+
+from opensnitch import ui_pb2
+from opensnitch.nodes import Nodes
+from opensnitch.desktop_parser import LinuxDesktopParser
+from opensnitch.utils import Message
+
+DIALOG_UI_PATH = "%s/../res/process_details.ui" % os.path.dirname(sys.modules[__name__].__file__)
+class ProcessDetailsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
+
+ LOG_TAG = "[ProcessDetails]: "
+
+ _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
+
+ TAB_STATUS = 0
+ TAB_DESCRIPTORS = 1
+ TAB_IOSTATS = 2
+ TAB_MAPS = 3
+ TAB_STACK = 4
+ TAB_ENVS = 5
+
+ TABS = {
+ TAB_STATUS: {
+ "text": None,
+ "scrollPos": 0
+ },
+ TAB_DESCRIPTORS: {
+ "text": None,
+ "scrollPos": 0
+ },
+ TAB_IOSTATS: {
+ "text": None,
+ "scrollPos": 0
+ },
+ TAB_MAPS: {
+ "text": None,
+ "scrollPos": 0
+ },
+ TAB_STACK: {
+ "text": None,
+ "scrollPos": 0
+ },
+ TAB_ENVS: {
+ "text": None,
+ "scrollPos": 0
+ }
+ }
+
+ def __init__(self, parent=None, appicon=None):
+ super(ProcessDetailsDialog, self).__init__(parent)
+ QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)
+ self.setWindowFlags(QtCore.Qt.Window)
+ self.setupUi(self)
+ self.setWindowIcon(appicon)
+
+ self._app_name = None
+ self._app_icon = None
+ self._apps_parser = LinuxDesktopParser()
+ self._nodes = Nodes.instance()
+ self._notification_callback.connect(self._cb_notification_callback)
+
+ self._nid = None
+ self._pid = ""
+ self._notifications_sent = {}
+
+ self.cmdClose.clicked.connect(self._cb_close_clicked)
+ self.cmdAction.clicked.connect(self._cb_action_clicked)
+ self.comboPids.currentIndexChanged.connect(self._cb_combo_pids_changed)
+
+ self.TABS[self.TAB_STATUS]['text'] = self.textStatus
+ self.TABS[self.TAB_DESCRIPTORS]['text'] = self.textOpenedFiles
+ self.TABS[self.TAB_IOSTATS]['text'] = self.textIOStats
+ self.TABS[self.TAB_MAPS]['text'] = self.textMappedFiles
+ self.TABS[self.TAB_STACK]['text'] = self.textStack
+ self.TABS[self.TAB_ENVS]['text'] = self.textEnv
+
+ self.TABS[self.TAB_DESCRIPTORS]['text'].setFont(QtGui.QFont("monospace"))
+
+ self.iconStart = QtGui.QIcon.fromTheme("media-playback-start")
+ self.iconPause = QtGui.QIcon.fromTheme("media-playback-pause")
+
+ if QtGui.QIcon.hasThemeIcon("window-close") == False:
+ self.cmdClose.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogCloseButton")))
+ self.iconStart = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPlay"))
+ self.iconPause = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPause"))
+
+ @QtCore.pyqtSlot(ui_pb2.NotificationReply)
+ def _cb_notification_callback(self, reply):
+ if reply.id in self._notifications_sent:
+ noti = self._notifications_sent[reply.id]
+
+ if reply.code == ui_pb2.ERROR:
+ self._show_message(QtCore.QCoreApplication.translate("proc_details", "<b>Error loading process information:</b> <br><br>\n\n") + reply.data)
+ self._pid = ""
+ self._set_button_running(False)
+
+ # if we haven't loaded any data yet, just close the window
+ if self._data_loaded == False:
+ # but if there're more than 1 pid keep the window open.
+ # we may have one pid already closed and one alive.
+ if self.comboPids.count() <= 1:
+ self._close()
+
+ self._delete_notification(reply.id)
+ return
+
+ if noti.type == ui_pb2.MONITOR_PROCESS and reply.data != "":
+ self._load_data(reply.data)
+
+ elif noti.type == ui_pb2.STOP_MONITOR_PROCESS:
+ if reply.data != "":
+ self._show_message(QtCore.QCoreApplication.translate("proc_details", "<b>Error stopping monitoring process:</b><br><br>") + reply.data)
+ self._set_button_running(False)
+
+ self._delete_notification(reply.id)
+ else:
+ print("[stats] unknown notification received: ", reply.id)
+
+ def closeEvent(self, e):
+ self._close()
+
+ def _cb_close_clicked(self):
+ self._close()
+
+ def _cb_combo_pids_changed(self, idx):
+ if idx == -1:
+ return
+ # TODO: this event causes to send to 2 Start notifications
+ #if self._pid != "" and self._pid != self.comboPids.currentText():
+ # self._stop_monitoring()
+ # self._pid = self.comboPids.currentText()
+ # self._start_monitoring()
+
+ def _cb_action_clicked(self):
+ if not self.cmdAction.isChecked():
+ self._stop_monitoring()
+ else:
+ self._start_monitoring()
+
+ def _show_message(self, text):
+ Message.ok(text, "", QtWidgets.QMessageBox.Warning)
+
+ def _delete_notification(self, nid):
+ if nid in self._notifications_sent:
+ del self._notifications_sent[nid]
+
+ def _reset(self):
+ self._app_name = None
+ self._app_icon = None
+ self.comboPids.clear()
+ self.labelProcName.setText(QtCore.QCoreApplication.translate("proc_details", "loading..."))
+ self.labelProcArgs.setText(QtCore.QCoreApplication.translate("proc_details", "loading..."))
+ self.labelProcIcon.clear()
+ self.labelStatm.setText("")
+ self.labelCwd.setText("")
+ for tidx in range(0, len(self.TABS)):
+ self.TABS[tidx]['text'].setPlainText("")
+
+ def _set_button_running(self, yes):
+ if yes:
+ self.cmdAction.setChecked(True)
+ self.cmdAction.setIcon(self.iconPause)
+ else:
+ self.cmdAction.setChecked(False)
+ self.cmdAction.setIcon(self.iconStart)
+
+ def _close(self):
+ self._stop_monitoring()
+ self.comboPids.clear()
+ self._pid = ""
+ self.hide()
+
+ def monitor(self, pids):
+ if self._pid != "":
+ self._stop_monitoring()
+
+ self._data_loaded = False
+ self._pids = pids
+ self._reset()
+ for pid in pids:
+ if pid != None:
+ self.comboPids.addItem(pid)
+
+ self.show()
+ self._start_monitoring()
+
+ def _set_tab_text(self, tab_idx, text):
+ self.TABS[tab_idx]['scrollPos'] = self.TABS[tab_idx]['text'].verticalScrollBar().value()
+ self.TABS[tab_idx]['text'].setPlainText(text)
+ self.TABS[tab_idx]['text'].verticalScrollBar().setValue(self.TABS[tab_idx]['scrollPos'])
+
+ def _start_monitoring(self):
+ try:
+ # avoid to send notifications without a pid
+ if self._pid != "":
+ return
+
+ self._pid = self.comboPids.currentText()
+ if self._pid == "":
+ return
+
+ self._set_button_running(True)
+ noti = ui_pb2.Notification(clientName="", serverName="", type=ui_pb2.MONITOR_PROCESS, data=self._pid, rules=[])
+ self._nid = self._nodes.send_notification(self._pids[self._pid], noti, self._notification_callback)
+ self._notifications_sent[self._nid] = noti
+ except Exception as e:
+ print(self.LOG_TAG + "exception starting monitoring: ", e)
+
+ def _stop_monitoring(self):
+ if self._pid == "":
+ return
+
+ self._set_button_running(False)
+ noti = ui_pb2.Notification(clientName="", serverName="", type=ui_pb2.STOP_MONITOR_PROCESS, data=str(self._pid), rules=[])
+ self._nid = self._nodes.send_notification(self._pids[self._pid], noti, self._notification_callback)
+ self._notifications_sent[self._nid] = noti
+ self._pid = ""
+ self._app_icon = None
+
+ def _load_data(self, data):
+ tab_idx = self.tabWidget.currentIndex()
+
+ try:
+ proc = json.loads(data)
+ self._load_app_icon(proc['Path'])
+ if self._app_name != None:
+ self.labelProcName.setText("<b>" + self._app_name + "</b>")
+ self.labelProcName.setToolTip("<b>" + self._app_name + "</b>")
+
+ #if proc['Path'] not in proc['Args']:
+ # proc['Args'].insert(0, proc['Path'])
+
+ self.labelProcArgs.setFixedHeight(30)
+ self.labelProcArgs.setText(" ".join(proc['Args']))
+ self.labelProcArgs.setToolTip(" ".join(proc['Args']))
+ self.labelCwd.setText("<b>CWD: </b>" + proc['CWD'])
+ self.labelCwd.setToolTip("<b>CWD: </b>" + proc['CWD'])
+ self._load_mem_data(proc['Statm'])
+
+ if tab_idx == self.TAB_STATUS:
+ self._set_tab_text(tab_idx, proc['Status'])
+
+ elif tab_idx == self.TAB_DESCRIPTORS:
+ self._load_descriptors(proc['Descriptors'])
+
+ elif tab_idx == self.TAB_IOSTATS:
+ self._load_iostats(proc['IOStats'])
+
+ elif tab_idx == self.TAB_MAPS:
+ self._set_tab_text(tab_idx, proc['Maps'])
+
+ elif tab_idx == self.TAB_STACK:
+ self._set_tab_text(tab_idx, proc['Stack'])
+
+ elif tab_idx == self.TAB_ENVS:
+ self._load_env_vars(proc['Env'])
+
+ self._data_loaded = True
+
+ except Exception as e:
+ print(self.LOG_TAG + "exception loading data: ", e)
+
+ def _load_app_icon(self, proc_path):
+ if self._app_icon != None:
+ return
+
+ self._app_name, self._app_icon, _, _ = self._apps_parser.get_info_by_path(proc_path, "terminal")
+
+ icon = QtGui.QIcon().fromTheme(self._app_icon)
+ pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48)))
+ self.labelProcIcon.setPixmap(pixmap)
+
+ if self._app_name == None:
+ self._app_name = proc_path
+
+ def _load_iostats(self, iostats):
+ ioText = "%-16s %dMB<br>%-16s %dMB<br>%-16s %d<br>%-16s %d<br>%-16s %dMB<br>%-16s %dMB<br>" % (
+ "<b>Chars read:</b>",
+ ((iostats['RChar'] / 1024) / 1024),
+ "<b>Chars written:</b>",
+ ((iostats['WChar'] / 1024) / 1024),
+ "<b>Syscalls read:</b>",
+ (iostats['SyscallRead']),
+ "<b>Syscalls write:</b>",
+ (iostats['SyscallWrite']),
+ "<b>KB read:</b>",
+ ((iostats['ReadBytes'] / 1024) / 1024),
+ "<b>KB written: </b>",
+ ((iostats['WriteBytes'] / 1024) / 1024)
+ )
+
+ self.textIOStats.setPlainText("")
+ self.textIOStats.appendHtml(ioText)
+
+ def _load_mem_data(self, mem):
+ # assuming page size == 4096
+ pagesize = 4096
+ memText = "<b>VIRT:</b> %dMB, <b>RSS:</b> %dMB, <b>Libs:</b> %dMB, <b>Data:</b> %dMB, <b>Text:</b> %dMB" % (
+ ((mem['Size'] * pagesize) / 1024) / 1024,
+ ((mem['Resident'] * pagesize) / 1024) / 1024,
+ ((mem['Lib'] * pagesize) / 1024) / 1024,
+ ((mem['Data'] * pagesize) / 1024) / 1024,
+ ((mem['Text'] * pagesize) / 1024) / 1024
+ )
+ self.labelStatm.setText(memText)
+
+ def _load_descriptors(self, descriptors):
+ text = "%-12s%-40s%-8s -> %s\n\n" % ("Size", "Time", "Name", "Symlink")
+ for d in descriptors:
+ text += "{:<12}{:<40}{:<8} -> {}\n".format(str(d['Size']), d['ModTime'], d['Name'], d['SymLink'])
+
+ self._set_tab_text(self.TAB_DESCRIPTORS, text)
+
+ def _load_env_vars(self, envs):
+ if envs == {}:
+ self._set_tab_text(self.TAB_ENVS, "<no environment variables>")
+ return
+
+ text = "%-15s\t%s\n\n" % ("Name", "Value")
+ for env_name in envs:
+ text += "%-15s:\t%s\n" % (env_name, envs[env_name])
+
+ self._set_tab_text(self.TAB_ENVS, text)
+
+
--- /dev/null
+import threading
+import sys
+import time
+import os
+import os.path
+import pwd
+import json
+import ipaddress
+
+from PyQt5 import QtCore, QtGui, uic, QtWidgets
+from PyQt5.QtCore import QCoreApplication as QC
+
+from slugify import slugify
+
+from opensnitch.desktop_parser import LinuxDesktopParser
+from opensnitch.config import Config
+from opensnitch.version import version
+
+from opensnitch import ui_pb2
+
+DIALOG_UI_PATH = "%s/../res/prompt.ui" % os.path.dirname(sys.modules[__name__].__file__)
+class PromptDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
+ _prompt_trigger = QtCore.pyqtSignal()
+ _tick_trigger = QtCore.pyqtSignal()
+ _timeout_trigger = QtCore.pyqtSignal()
+
+ DEFAULT_TIMEOUT = 15
+
+ ACTION_IDX_DENY = 0
+ ACTION_IDX_ALLOW = 1
+
+ FIELD_REGEX_HOST = "regex_host"
+ FIELD_REGEX_IP = "regex_ip"
+ FIELD_PROC_PATH = "process_path"
+ FIELD_PROC_ARGS = "process_args"
+ FIELD_PROC_ID = "process_id"
+ FIELD_USER_ID = "user_id"
+ FIELD_DST_IP = "dst_ip"
+ FIELD_DST_PORT = "dst_port"
+ FIELD_DST_NETWORK = "dst_network"
+ FIELD_DST_HOST = "simple_host"
+
+ # don't translate
+ DURATION_30s = "30s"
+ DURATION_5m = "5m"
+ DURATION_15m = "15m"
+ DURATION_30m = "30m"
+ DURATION_1h = "1h"
+ # don't translate
+
+ # label displayed in the pop-up combo
+ DURATION_session = QC.translate("popups", "until reboot")
+ # label displayed in the pop-up combo
+ DURATION_forever = QC.translate("popups", "forever")
+
+ def __init__(self, parent=None, appicon=None):
+ QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)
+ # Other interesting flags: QtCore.Qt.Tool | QtCore.Qt.BypassWindowManagerHint
+ self._cfg = Config.get()
+ self.setupUi(self)
+ self.setWindowIcon(appicon)
+
+ self._width = None
+ self._height = None
+
+ dialog_geometry = self._cfg.getSettings("promptDialog/geometry")
+ if dialog_geometry == QtCore.QByteArray:
+ self.restoreGeometry(dialog_geometry)
+
+ self.setWindowTitle("OpenSnitch v%s" % version)
+
+ self._lock = threading.Lock()
+ self._con = None
+ self._rule = None
+ self._local = True
+ self._peer = None
+ self._prompt_trigger.connect(self.on_connection_prompt_triggered)
+ self._timeout_trigger.connect(self.on_timeout_triggered)
+ self._tick_trigger.connect(self.on_tick_triggered)
+ self._tick = int(self._cfg.getSettings(self._cfg.DEFAULT_TIMEOUT_KEY)) if self._cfg.hasKey(self._cfg.DEFAULT_TIMEOUT_KEY) else self.DEFAULT_TIMEOUT
+ self._tick_thread = None
+ self._done = threading.Event()
+ self._timeout_text = ""
+ self._timeout_triggered = False
+
+ self._apps_parser = LinuxDesktopParser()
+
+ self.denyButton.clicked.connect(self._on_deny_clicked)
+ # also accept button
+ self.applyButton.clicked.connect(self._on_apply_clicked)
+ self._apply_text = QC.translate("popups", "Allow")
+ self._deny_text = QC.translate("popups", "Deny")
+ self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY)
+
+ self.whatIPCombo.setVisible(False)
+ self.checkDstIP.setVisible(False)
+ self.checkDstPort.setVisible(False)
+ self.checkUserID.setVisible(False)
+ self.appDescriptionLabel.setVisible(False)
+
+ self._ischeckAdvanceded = False
+ self.checkAdvanced.toggled.connect(self._check_advanced_toggled)
+
+ self.checkAdvanced.clicked.connect(self._button_clicked)
+ self.durationCombo.activated.connect(self._button_clicked)
+ self.whatCombo.activated.connect(self._button_clicked)
+ self.whatIPCombo.activated.connect(self._button_clicked)
+ self.checkDstIP.clicked.connect(self._button_clicked)
+ self.checkDstPort.clicked.connect(self._button_clicked)
+ self.checkUserID.clicked.connect(self._button_clicked)
+
+ if QtGui.QIcon.hasThemeIcon("emblem-default") == False:
+ self.applyButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogApplyButton")))
+ self.denyButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogCancelButton")))
+
+ def showEvent(self, event):
+ super(PromptDialog, self).showEvent(event)
+ self.activateWindow()
+
+ if self._width is None or self._height is None:
+ self._width = self.width()
+ self._height = self.height()
+
+ self.setMinimumSize(self._width, self._height)
+ self.setMaximumSize(self._width, self._height)
+ self.move_popup()
+
+ def move_popup(self):
+ popup_pos = self._cfg.getInt(self._cfg.DEFAULT_POPUP_POSITION)
+ point = QtWidgets.QDesktopWidget().availableGeometry()
+ if popup_pos == self._cfg.POPUP_TOP_RIGHT:
+ self.move(point.topRight())
+ elif popup_pos == self._cfg.POPUP_TOP_LEFT:
+ self.move(point.topLeft())
+ elif popup_pos == self._cfg.POPUP_BOTTOM_RIGHT:
+ self.move(point.bottomRight())
+ elif popup_pos == self._cfg.POPUP_BOTTOM_LEFT:
+ self.move(point.bottomLeft())
+
+ def _stop_countdown(self):
+ self.applyButton.setText("%s" % self._apply_text)
+ self.denyButton.setText("%s" % self._deny_text)
+ self._tick_thread.stop = True
+
+ def _check_advanced_toggled(self, state):
+ self.checkDstIP.setVisible(state)
+ self.whatIPCombo.setVisible(state)
+ self.destIPLabel.setVisible(not state)
+ self.checkDstPort.setVisible(state)
+ self.checkUserID.setVisible(state)
+ self._ischeckAdvanceded = state
+
+ def _button_clicked(self):
+ self._stop_countdown()
+
+ def _set_elide_text(self, widget, text, max_size=132):
+ if len(text) > max_size:
+ text = text[:max_size] + "..."
+
+ widget.setText(text)
+
+ def promptUser(self, connection, is_local, peer):
+ # one at a time
+ with self._lock:
+ # reset state
+ if self._tick_thread != None and self._tick_thread.is_alive():
+ self._tick_thread.join()
+ self._cfg.reload()
+ self._tick = int(self._cfg.getSettings(self._cfg.DEFAULT_TIMEOUT_KEY)) if self._cfg.hasKey(self._cfg.DEFAULT_TIMEOUT_KEY) else self.DEFAULT_TIMEOUT
+ self._tick_thread = threading.Thread(target=self._timeout_worker)
+ self._tick_thread.stop = self._ischeckAdvanceded
+ self._timeout_triggered = False
+ self._rule = None
+ self._local = is_local
+ self._peer = peer
+ self._con = connection
+ self._done.clear()
+ # trigger and show dialog
+ self._prompt_trigger.emit()
+ # start timeout thread
+ self._tick_thread.start()
+ # wait for user choice or timeout
+ self._done.wait()
+
+ return self._rule, self._timeout_triggered
+
+ def _timeout_worker(self):
+ if self._tick == 0:
+ self._timeout_trigger.emit()
+ return
+
+ while self._tick > 0 and self._done.is_set() is False:
+ t = threading.currentThread()
+ # stop only stops the coundtdown, not the thread itself.
+ if getattr(t, "stop", True):
+ self._tick = int(self._cfg.getSettings(self._cfg.DEFAULT_TIMEOUT_KEY))
+ time.sleep(1)
+ continue
+
+ self._tick -= 1
+ self._tick_trigger.emit()
+ time.sleep(1)
+
+ if not self._done.is_set():
+ self._timeout_trigger.emit()
+
+ @QtCore.pyqtSlot()
+ def on_connection_prompt_triggered(self):
+ self._render_connection(self._con)
+ if self._tick > 0:
+ self.show()
+
+ @QtCore.pyqtSlot()
+ def on_tick_triggered(self):
+ self._set_cmd_action_text()
+
+ @QtCore.pyqtSlot()
+ def on_timeout_triggered(self):
+ self._timeout_triggered = True
+ self._send_rule()
+
+ def _configure_default_duration(self):
+ if self._cfg.hasKey(self._cfg.DEFAULT_DURATION_KEY):
+ cur_idx = self._cfg.getInt(self._cfg.DEFAULT_DURATION_KEY)
+ self.durationCombo.setCurrentIndex(cur_idx)
+ else:
+ self.durationCombo.setCurrentIndex(self._cfg.DEFAULT_DURATION_IDX)
+
+ def _set_cmd_action_text(self):
+ if self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY) == self.ACTION_IDX_ALLOW:
+ self.applyButton.setText("%s (%d)" % (self._apply_text, self._tick))
+ self.denyButton.setText(self._deny_text)
+ else:
+ self.denyButton.setText("%s (%d)" % (self._deny_text, self._tick))
+ self.applyButton.setText(self._apply_text)
+
+ def _set_app_description(self, description):
+ if description != None and description != "":
+ self.appDescriptionLabel.setVisible(True)
+ self.appDescriptionLabel.setFixedHeight(50)
+ self.appDescriptionLabel.setToolTip(description)
+ self._set_elide_text(self.appDescriptionLabel, "%s" % description)
+ else:
+ self.appDescriptionLabel.setVisible(False)
+ self.appDescriptionLabel.setFixedHeight(0)
+ self.appDescriptionLabel.setText("")
+
+ def _set_app_path(self, app_name, app_args, con):
+ # show the binary path if it's not part of the cmdline args:
+ # cmdline: telnet 1.1.1.1 (path: /usr/bin/telnet.netkit)
+ # cmdline: /usr/bin/telnet.netkit 1.1.1.1 (the binary path is part of the cmdline args, no need to display it)
+ if con.process_path != "" and len(con.process_args) >= 1 and con.process_path not in con.process_args:
+ self.appPathLabel.setToolTip("Process path: %s" % con.process_path)
+ if app_name.lower() == app_args:
+ self._set_elide_text(self.appPathLabel, "%s" % con.process_path)
+ else:
+ self._set_elide_text(self.appPathLabel, "(%s)" % con.process_path)
+ self.appPathLabel.setVisible(True)
+ else:
+ self.appPathLabel.setVisible(False)
+ self.appPathLabel.setText("")
+
+ def _set_app_args(self, app_name, app_args):
+ # if the app name and the args are the same, there's no need to display
+ # the args label (amule for example)
+ if app_name.lower() != app_args:
+ self.argsLabel.setVisible(True)
+ self._set_elide_text(self.argsLabel, app_args)
+ self.argsLabel.setToolTip(app_args)
+ else:
+ self.argsLabel.setVisible(False)
+ self.argsLabel.setText("")
+
+ def _render_connection(self, con):
+ app_name, app_icon, description, _ = self._apps_parser.get_info_by_path(con.process_path, "terminal")
+ app_args = " ".join(con.process_args)
+ self._set_app_description(description)
+ self._set_app_path(app_name, app_args, con)
+ self._set_app_args(app_name, app_args)
+
+ if app_name == "":
+ self.appPathLabel.setVisible(False)
+ self.argsLabel.setVisible(False)
+ app_name = QC.translate("popups", "Unknown process %s" % con.process_path)
+ self.appNameLabel.setText(QC.translate("popups", "Outgoing connection"))
+ else:
+ self._set_elide_text(self.appNameLabel, "%s" % app_name, max_size=42)
+ self.appNameLabel.setToolTip(app_name)
+
+ self.cwdLabel.setToolTip("%s %s" % (QC.translate("popups", "Process launched from:"), con.process_cwd))
+ self._set_elide_text(self.cwdLabel, con.process_cwd, max_size=32)
+
+ pixmap = self._get_app_icon(app_icon)
+ self.iconLabel.setPixmap(pixmap)
+
+ message = self._get_popup_message(app_name, con)
+
+ self.messageLabel.setText(message)
+ self.messageLabel.setToolTip(message)
+
+ self.sourceIPLabel.setText(con.src_ip)
+ self.destIPLabel.setText(con.dst_ip)
+ self.destPortLabel.setText(str(con.dst_port))
+
+ if self._local:
+ try:
+ uid = "%d (%s)" % (con.user_id, pwd.getpwuid(con.user_id).pw_name)
+ except:
+ uid = ""
+ else:
+ uid = "%d" % con.user_id
+
+ self.uidLabel.setText(uid)
+ self.pidLabel.setText("%s" % con.process_id)
+
+ self.whatCombo.clear()
+ self.whatIPCombo.clear()
+
+ # the order of these combobox entries must match those in the preferences dialog
+ # prefs -> UI -> Default target
+ self.whatCombo.addItem(QC.translate("popups", "from this executable"), self.FIELD_PROC_PATH)
+ if int(con.process_id) < 0:
+ self.whatCombo.model().item(0).setEnabled(False)
+
+ self.whatCombo.addItem(QC.translate("popups", "from this command line"), self.FIELD_PROC_ARGS)
+
+ self.whatCombo.addItem(QC.translate("popups", "to port {0}").format(con.dst_port), self.FIELD_DST_PORT)
+ self.whatCombo.addItem(QC.translate("popups", "to {0}").format(con.dst_ip), self.FIELD_DST_IP)
+
+ self.whatCombo.addItem(QC.translate("popups", "from user {0}").format(uid), self.FIELD_USER_ID)
+ if int(con.user_id) < 0:
+ self.whatCombo.model().item(4).setEnabled(False)
+
+ self.whatCombo.addItem(QC.translate("popups", "from this PID"), self.FIELD_PROC_ID)
+
+ self._add_dst_networks_to_combo(self.whatCombo, con.dst_ip)
+
+ if con.dst_host != "" and con.dst_host != con.dst_ip:
+ self._add_dsthost_to_combo(con.dst_host)
+
+ self.whatIPCombo.addItem(QC.translate("popups", "to {0}").format(con.dst_ip), self.FIELD_DST_IP)
+
+ parts = con.dst_ip.split('.')
+ nparts = len(parts)
+ for i in range(1, nparts):
+ self.whatCombo.addItem(QC.translate("popups", "to {0}.*").format('.'.join(parts[:i])), self.FIELD_REGEX_IP)
+ self.whatIPCombo.addItem(QC.translate("popups", "to {0}.*").format( '.'.join(parts[:i])), self.FIELD_REGEX_IP)
+
+ self._add_dst_networks_to_combo(self.whatIPCombo, con.dst_ip)
+
+ self._default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY)
+
+ self._configure_default_duration()
+
+ if int(con.process_id) > 0:
+ self.whatCombo.setCurrentIndex(int(self._cfg.getSettings(self._cfg.DEFAULT_TARGET_KEY)))
+ else:
+ self.whatCombo.setCurrentIndex(2)
+
+
+ self.checkDstIP.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTIP))
+ self.checkDstPort.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_DSTPORT))
+ self.checkUserID.setChecked(self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED_UID))
+ if self._cfg.getBool(self._cfg.DEFAULT_POPUP_ADVANCED):
+ self.checkAdvanced.toggle()
+
+ self._set_cmd_action_text()
+ self.checkAdvanced.setFocus()
+
+ self.setFixedSize(self.size())
+
+ # https://gis.stackexchange.com/questions/86398/how-to-disable-the-escape-key-for-a-dialog
+ def keyPressEvent(self, event):
+ if not event.key() == QtCore.Qt.Key_Escape:
+ super(PromptDialog, self).keyPressEvent(event)
+
+ # prevent a click on the window's x
+ # from quitting the whole application
+ def closeEvent(self, e):
+ self._send_rule()
+ e.ignore()
+
+ def _add_dst_networks_to_combo(self, combo, dst_ip):
+ if type(ipaddress.ip_address(dst_ip)) == ipaddress.IPv4Address:
+ combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/24", strict=False)), self.FIELD_DST_NETWORK)
+ combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/16", strict=False)), self.FIELD_DST_NETWORK)
+ combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/8", strict=False)), self.FIELD_DST_NETWORK)
+ else:
+ combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/64", strict=False)), self.FIELD_DST_NETWORK)
+ combo.addItem(QC.translate("popups", "to {0}").format(ipaddress.ip_network(dst_ip + "/128", strict=False)), self.FIELD_DST_NETWORK)
+
+ def _add_dsthost_to_combo(self, dst_host):
+ self.whatCombo.addItem("%s" % dst_host, self.FIELD_DST_HOST)
+ self.whatIPCombo.addItem("%s" % dst_host, self.FIELD_DST_HOST)
+
+ parts = dst_host.split('.')[1:]
+ nparts = len(parts)
+ for i in range(0, nparts - 1):
+ self.whatCombo.addItem(QC.translate("popups", "to *.{0}").format('.'.join(parts[i:])), self.FIELD_REGEX_HOST)
+ self.whatIPCombo.addItem(QC.translate("popups", "to *.{0}").format('.'.join(parts[i:])), self.FIELD_REGEX_HOST)
+
+
+ def _get_app_icon(self, app_icon):
+ """we try to get the icon of an app from the system.
+ If it's not found, then we'll try to search for it in common directories
+ of the system.
+ """
+ try:
+ icon = QtGui.QIcon().fromTheme(app_icon)
+ pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48)))
+ if QtGui.QIcon().hasThemeIcon(app_icon) == False or pixmap.height() == 0:
+ # sometimes the icon is an absolute path, sometimes it's not
+ if os.path.isabs(app_icon):
+ icon = QtGui.QIcon(app_icon)
+ pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48)))
+ else:
+ icon_path = self._apps_parser.discover_app_icon(app_icon)
+ if icon_path != None:
+ icon = QtGui.QIcon(icon_path)
+ pixmap = icon.pixmap(icon.actualSize(QtCore.QSize(48, 48)))
+ except Exception as e:
+ print("Exception _get_app_icon():", e)
+
+ return pixmap
+
+ def _get_popup_message(self, app_name, con):
+ """
+ _get_popup_message helps constructing the message that is displayed on
+ the pop-up dialog. Example:
+ curl is connecting to www.opensnitch.io on TCP port 443
+ """
+ message = "<b>%s</b>" % app_name
+ if not self._local:
+ message = QC.translate("popups", "<b>Remote</b> process %s running on <b>%s</b>") % ( \
+ message,
+ self._peer.split(':')[1])
+
+ msg_action = QC.translate("popups", "is connecting to <b>%s</b> on %s port %d") % ( \
+ con.dst_host or con.dst_ip,
+ con.protocol.upper(),
+ con.dst_port )
+
+ if con.dst_port == 53 and con.dst_ip != con.dst_host and con.dst_host != "":
+ msg_action = QC.translate("popups", "is attempting to resolve <b>%s</b> via %s, %s port %d") % ( \
+ con.dst_host,
+ con.dst_ip,
+ con.protocol.upper(),
+ con.dst_port)
+
+ return "%s %s" % (message, msg_action)
+
+ def _get_duration(self, duration_idx):
+ if duration_idx == 0:
+ return Config.DURATION_ONCE
+ elif duration_idx == 1:
+ return self.DURATION_30s
+ elif duration_idx == 2:
+ return self.DURATION_5m
+ elif duration_idx == 3:
+ return self.DURATION_15m
+ elif duration_idx == 4:
+ return self.DURATION_30m
+ elif duration_idx == 5:
+ return self.DURATION_1h
+ elif duration_idx == 6:
+ return Config.DURATION_UNTIL_RESTART
+ else:
+ return Config.DURATION_ALWAYS
+
+ def _get_combo_operator(self, combo, what_idx):
+ if combo.itemData(what_idx) == self.FIELD_PROC_PATH:
+ return Config.RULE_TYPE_SIMPLE, "process.path", self._con.process_path
+
+ elif combo.itemData(what_idx) == self.FIELD_PROC_ARGS:
+ # this should not happen
+ if len(self._con.process_args) == 0 or self._con.process_args[0] == "":
+ return Config.RULE_TYPE_SIMPLE, "process.path", self._con.process_path
+ return Config.RULE_TYPE_SIMPLE, "process.command", ' '.join(self._con.process_args)
+
+ elif combo.itemData(what_idx) == self.FIELD_PROC_ID:
+ return Config.RULE_TYPE_SIMPLE, "process.id", "{0}".format(self._con.process_id)
+
+ elif combo.itemData(what_idx) == self.FIELD_USER_ID:
+ return Config.RULE_TYPE_SIMPLE, "user.id", "%s" % self._con.user_id
+
+ elif combo.itemData(what_idx) == self.FIELD_DST_PORT:
+ return Config.RULE_TYPE_SIMPLE, "dest.port", "%s" % self._con.dst_port
+
+ elif combo.itemData(what_idx) == self.FIELD_DST_IP:
+ return Config.RULE_TYPE_SIMPLE, "dest.ip", self._con.dst_ip
+
+ elif combo.itemData(what_idx) == self.FIELD_DST_HOST:
+ return Config.RULE_TYPE_SIMPLE, "dest.host", combo.currentText()
+
+ elif combo.itemData(what_idx) == self.FIELD_DST_NETWORK:
+ # strip "to ": "to x.x.x/20" -> "x.x.x/20"
+ # we assume that to is one word in all languages
+ parts = combo.currentText().split(' ')
+ text = parts[len(parts)-1]
+ return Config.RULE_TYPE_NETWORK, "dest.network", text
+
+ elif combo.itemData(what_idx) == self.FIELD_REGEX_HOST:
+ parts = combo.currentText().split(' ')
+ text = parts[len(parts)-1]
+ # ^(|.*\.)yahoo\.com
+ dsthost = '\.'.join(text.split('.')).replace("*", "")
+ dsthost = "^(|.*\.)%s" % dsthost[2:]
+ return Config.RULE_TYPE_REGEXP, "dest.host", dsthost
+
+ elif combo.itemData(what_idx) == self.FIELD_REGEX_IP:
+ parts = combo.currentText().split(' ')
+ text = parts[len(parts)-1]
+ return Config.RULE_TYPE_REGEXP, "dest.ip", "%s" % '\.'.join(text.split('.')).replace("*", ".*")
+
+ def _on_deny_clicked(self):
+ self._default_action = self.ACTION_IDX_DENY
+ self._send_rule()
+
+ def _on_apply_clicked(self):
+ self._default_action = self.ACTION_IDX_ALLOW
+ self._send_rule()
+
+ def _is_list_rule(self):
+ return self.checkUserID.isChecked() or self.checkDstPort.isChecked() or self.checkDstIP.isChecked()
+
+ def _get_rule_name(self, rule):
+ rule_temp_name = slugify("%s %s" % (rule.action, rule.duration))
+ if self._is_list_rule():
+ rule_temp_name = "%s-list" % rule_temp_name
+ else:
+ rule_temp_name = "%s-simple" % rule_temp_name
+ rule_temp_name = slugify("%s %s" % (rule_temp_name, rule.operator.data))
+
+ return rule_temp_name[:128]
+
+ def _send_rule(self):
+ try:
+ self._cfg.setSettings("promptDialog/geometry", self.saveGeometry())
+ self._rule = ui_pb2.Rule(name="user.choice")
+ self._rule.enabled = True
+ self._rule.action = Config.ACTION_DENY if self._default_action == self.ACTION_IDX_DENY else Config.ACTION_ALLOW
+ self._rule.duration = self._get_duration(self.durationCombo.currentIndex())
+
+ what_idx = self.whatCombo.currentIndex()
+ self._rule.operator.type, self._rule.operator.operand, self._rule.operator.data = self._get_combo_operator(self.whatCombo, what_idx)
+ if self._rule.operator.data == "":
+ print("Invalid rule, discarding: ", self._rule)
+ self._rule = None
+ return
+
+ rule_temp_name = self._get_rule_name(self._rule)
+ self._rule.name = rule_temp_name
+
+ # TODO: move to a method
+ data=[]
+ if self.checkDstIP.isChecked() and self.whatCombo.itemData(what_idx) != self.FIELD_DST_IP:
+ _type, _operand, _data = self._get_combo_operator(self.whatIPCombo, self.whatIPCombo.currentIndex())
+ data.append({"type": _type, "operand": _operand, "data": _data})
+ rule_temp_name = slugify("%s %s" % (rule_temp_name, _data))
+
+ if self.checkDstPort.isChecked() and self.whatCombo.itemData(what_idx) != self.FIELD_DST_PORT:
+ data.append({"type": Config.RULE_TYPE_SIMPLE, "operand": "dest.port", "data": str(self._con.dst_port)})
+ rule_temp_name = slugify("%s %s" % (rule_temp_name, str(self._con.dst_port)))
+
+ if self.checkUserID.isChecked() and self.whatCombo.itemData(what_idx) != self.FIELD_USER_ID:
+ data.append({"type": Config.RULE_TYPE_SIMPLE, "operand": "user.id", "data": str(self._con.user_id)})
+ rule_temp_name = slugify("%s %s" % (rule_temp_name, str(self._con.user_id)))
+
+ if self._is_list_rule():
+ data.append({"type": self._rule.operator.type, "operand": self._rule.operator.operand, "data": self._rule.operator.data})
+ self._rule.operator.data = json.dumps(data)
+ self._rule.operator.type = Config.RULE_TYPE_LIST
+ self._rule.operator.operand = Config.RULE_TYPE_LIST
+
+ self._rule.name = rule_temp_name
+
+ self.hide()
+ if self._ischeckAdvanceded:
+ self.checkAdvanced.toggle()
+ self._ischeckAdvanceded = False
+
+ except Exception as e:
+ print("[pop-up] exception creating a rule:", e)
+ finally:
+ # signal that the user took a decision and
+ # a new rule is available
+ self._done.set()
+ self.hide()
--- /dev/null
+
+from PyQt5 import QtCore, QtGui, uic, QtWidgets
+from PyQt5.QtCore import QCoreApplication as QC
+from slugify import slugify
+from datetime import datetime
+import re
+import json
+import sys
+import os
+from opensnitch import ui_pb2
+import time
+import ipaddress
+
+from opensnitch.config import Config
+from opensnitch.nodes import Nodes
+from opensnitch.database import Database
+from opensnitch.version import version
+from opensnitch.utils import Message, FileDialog
+
+DIALOG_UI_PATH = "%s/../res/ruleseditor.ui" % os.path.dirname(sys.modules[__name__].__file__)
+class RulesEditorDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
+
+ LOG_TAG = "[rules editor]"
+ classA_net = "10\.\d{1,3}\.\d{1,3}\.\d{1,3}"
+ classB_net = "172\.1[6-9]\.\d+\.\d+|172\.2[0-9]\.\d+\.\d+|172\.3[0-1]+\.\d{1,3}\.\d{1,3}"
+ classC_net = "192\.168\.\d{1,3}\.\d{1,3}"
+ others_net = "127\.\d{1,3}\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3}"
+ LAN_RANGES = "^(" + others_net + "|" + classC_net + "|" + classB_net + "|" + classA_net + "|::1|f[cde].*::.*)$"
+ LAN_LABEL = "LAN"
+
+ ADD_RULE = 0
+ EDIT_RULE = 1
+ WORK_MODE = ADD_RULE
+
+ _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
+
+ def __init__(self, parent=None, _rule=None, appicon=None):
+ super(RulesEditorDialog, self).__init__(parent)
+ QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)
+
+ self._notifications_sent = {}
+ self._nodes = Nodes.instance()
+ self._db = Database.instance()
+ self._notification_callback.connect(self._cb_notification_callback)
+ self._old_rule_name = None
+
+ self.setupUi(self)
+ self.setWindowIcon(appicon)
+
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.Reset).clicked.connect(self._cb_reset_clicked)
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.Close).clicked.connect(self._cb_close_clicked)
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).clicked.connect(self._cb_apply_clicked)
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.Help).clicked.connect(self._cb_help_clicked)
+ self.selectListButton.clicked.connect(self._cb_select_list_button_clicked)
+ self.selectListRegexpButton.clicked.connect(self._cb_select_regexp_list_button_clicked)
+ self.selectIPsListButton.clicked.connect(self._cb_select_ips_list_button_clicked)
+ self.selectNetsListButton.clicked.connect(self._cb_select_nets_list_button_clicked)
+ self.protoCheck.toggled.connect(self._cb_proto_check_toggled)
+ self.procCheck.toggled.connect(self._cb_proc_check_toggled)
+ self.cmdlineCheck.toggled.connect(self._cb_cmdline_check_toggled)
+ self.dstPortCheck.toggled.connect(self._cb_dstport_check_toggled)
+ self.uidCheck.toggled.connect(self._cb_uid_check_toggled)
+ self.pidCheck.toggled.connect(self._cb_pid_check_toggled)
+ self.dstIPCheck.toggled.connect(self._cb_dstip_check_toggled)
+ self.dstHostCheck.toggled.connect(self._cb_dsthost_check_toggled)
+ self.dstListsCheck.toggled.connect(self._cb_dstlists_check_toggled)
+ self.dstListRegexpCheck.toggled.connect(self._cb_dstregexplists_check_toggled)
+ self.dstListIPsCheck.toggled.connect(self._cb_dstiplists_check_toggled)
+ self.dstListNetsCheck.toggled.connect(self._cb_dstnetlists_check_toggled)
+
+ if QtGui.QIcon.hasThemeIcon("emblem-default") == False:
+ self.actionAllowRadio.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogApplyButton")))
+ self.actionDenyRadio.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogCancelButton")))
+
+ if _rule != None:
+ self._load_rule(rule=_rule)
+
+ def _bool(self, s):
+ return s == 'True'
+
+ def _cb_accept_clicked(self):
+ pass
+
+ def _cb_close_clicked(self):
+ self.hide()
+
+ def _cb_reset_clicked(self):
+ self._reset_state()
+
+ def _cb_help_clicked(self):
+ QtGui.QDesktopServices.openUrl(QtCore.QUrl(Config.HELP_URL))
+
+ def _cb_select_list_button_clicked(self):
+ dirName = FileDialog.select_dir(self, self.dstListsLine.text())
+ if dirName != None and dirName != "":
+ self.dstListsLine.setText(dirName)
+
+ def _cb_select_nets_list_button_clicked(self):
+ dirName = FileDialog.select_dir(self, self.dstListNetsLine.text())
+ if dirName != None and dirName != "":
+ self.dstListNetsLine.setText(dirName)
+
+ def _cb_select_ips_list_button_clicked(self):
+ dirName = FileDialog.select_dir(self, self.dstListIPsLine.text())
+ if dirName != None and dirName != "":
+ self.dstListIPsLine.setText(dirName)
+
+ def _cb_select_regexp_list_button_clicked(self):
+ dirName = FileDialog.select_dir(self, self.dstRegexpListsLine.text())
+ if dirName != None and dirName != "":
+ self.dstRegexpListsLine.setText(dirName)
+
+ def _cb_proto_check_toggled(self, state):
+ self.protoCombo.setEnabled(state)
+
+ def _cb_proc_check_toggled(self, state):
+ self.procLine.setEnabled(state)
+ self.checkProcRegexp.setEnabled(state)
+
+ def _cb_cmdline_check_toggled(self, state):
+ self.cmdlineLine.setEnabled(state)
+ self.checkCmdlineRegexp.setEnabled(state)
+
+ def _cb_dstport_check_toggled(self, state):
+ self.dstPortLine.setEnabled(state)
+
+ def _cb_uid_check_toggled(self, state):
+ self.uidLine.setEnabled(state)
+
+ def _cb_pid_check_toggled(self, state):
+ self.pidLine.setEnabled(state)
+
+ def _cb_dstip_check_toggled(self, state):
+ self.dstIPCombo.setEnabled(state)
+
+ def _cb_dsthost_check_toggled(self, state):
+ self.dstHostLine.setEnabled(state)
+
+ def _cb_dstlists_check_toggled(self, state):
+ self.dstListsLine.setEnabled(state)
+ self.selectListButton.setEnabled(state)
+
+ def _cb_dstregexplists_check_toggled(self, state):
+ self.dstRegexpListsLine.setEnabled(state)
+ self.selectListRegexpButton.setEnabled(state)
+
+ def _cb_dstiplists_check_toggled(self, state):
+ self.dstListIPsLine.setEnabled(state)
+ self.selectIPsListButton.setEnabled(state)
+
+ def _cb_dstnetlists_check_toggled(self, state):
+ self.dstListNetsLine.setEnabled(state)
+ self.selectNetsListButton.setEnabled(state)
+
+ def _set_status_error(self, msg):
+ self.statusLabel.setStyleSheet('color: red')
+ self.statusLabel.setText(msg)
+
+ def _set_status_message(self, msg):
+ self.statusLabel.setStyleSheet('color: green')
+ self.statusLabel.setText(msg)
+
+ def _cb_apply_clicked(self):
+ if self.nodesCombo.count() == 0:
+ self._set_status_error(QC.translate("rules", "There're no nodes connected."))
+ return
+
+ rule_name = self.ruleNameEdit.text()
+ if rule_name == "":
+ return
+
+ node = self.nodesCombo.currentText()
+ # avoid to overwrite rules when:
+ # - adding a new rule.
+ # - when a rule is renamed, i.e., the rule is edited or added and the
+ # user changes the name.
+ if self.WORK_MODE == self.ADD_RULE and self._db.get_rule(rule_name, node).next() == True:
+ self._set_status_error(QC.translate("rules", "There's already a rule with this name."))
+ return
+ elif self.WORK_MODE == self.EDIT_RULE and rule_name != self._old_rule_name and \
+ self._db.get_rule(rule_name, node).next() == True:
+ self._set_status_error(QC.translate("rules", "There's already a rule with this name."))
+ return
+
+ result, error = self._save_rule()
+ if result == False:
+ self._set_status_error(error)
+ return
+
+ self._add_rule()
+ if self._old_rule_name != None and self._old_rule_name != self.rule.name:
+ self._delete_rule()
+
+ self._old_rule_name = rule_name
+
+ # after adding a new rule, we enter into EDIT mode, to allow further
+ # changes without closing the dialog.
+ if self.WORK_MODE == self.ADD_RULE:
+ self.WORK_MODE = self.EDIT_RULE
+
+ @QtCore.pyqtSlot(ui_pb2.NotificationReply)
+ def _cb_notification_callback(self, reply):
+ #print(self.LOG_TAG, "Rule notification received: ", reply.id, reply.code)
+ if reply.id in self._notifications_sent:
+ if reply.code == ui_pb2.OK:
+ self._set_status_message(QC.translate("rules", "Rule applied."))
+ else:
+ self._set_status_error(QC.translate("rules", "Error applying rule: {0}").format(reply.data))
+
+ del self._notifications_sent[reply.id]
+
+ def _get_duration(self, duration_idx):
+ if duration_idx == 0:
+ return Config.DURATION_ONCE
+ elif duration_idx == 1:
+ return Config.DURATION_30s
+ elif duration_idx == 2:
+ return Config.DURATION_5m
+ elif duration_idx == 3:
+ return Config.DURATION_15m
+ elif duration_idx == 4:
+ return Config.DURATION_30m
+ elif duration_idx == 5:
+ return Config.DURATION_1h
+ elif duration_idx == 6:
+ return Config.DURATION_UNTIL_RESTART
+ else:
+ return Config.DURATION_ALWAYS
+
+ def _load_duration(self, duration):
+ if duration == Config.DURATION_ONCE:
+ return 0
+ elif duration == Config.DURATION_30s:
+ return 1
+ elif duration == Config.DURATION_5m:
+ return 2
+ elif duration == Config.DURATION_15m:
+ return 3
+ elif duration == Config.DURATION_30m:
+ return 4
+ elif duration == Config.DURATION_1h:
+ return 5
+ elif duration == Config.DURATION_UNTIL_RESTART:
+ return 6
+ else:
+ # always
+ return 7
+
+ def _is_regex(self, text):
+ charset="\\*{[|^?$"
+ for c in charset:
+ if c in text:
+ return True
+ return False
+
+ def _is_valid_regex(self, regex):
+ try:
+ re.compile(regex)
+ return True
+ except re.error as e:
+ self.statusLabel.setText(str(e))
+ return False
+
+ def get_rule_from_records(self, records):
+ rule = ui_pb2.Rule(name=records.value(2))
+ rule.enabled = self._bool(records.value(3))
+ rule.precedence = self._bool(records.value(4))
+ rule.action = records.value(5)
+ rule.duration = records.value(6)
+ rule.operator.type = records.value(7)
+ rule.operator.sensitive = self._bool(records.value(8))
+ rule.operator.operand = records.value(9)
+ rule.operator.data = "" if records.value(10) == None else str(records.value(10))
+
+ return rule
+
+ def _reset_state(self):
+ self._old_rule_name = None
+ self.rule = None
+
+ self.ruleNameEdit.setText("")
+ self.statusLabel.setText("")
+
+ self.actionDenyRadio.setChecked(True)
+ self.durationCombo.setCurrentIndex(0)
+
+ self.protoCheck.setChecked(False)
+ self.protoCombo.setCurrentText("")
+
+ self.procCheck.setChecked(False)
+ self.checkProcRegexp.setEnabled(False)
+ self.checkProcRegexp.setChecked(False)
+ self.procLine.setText("")
+
+ self.cmdlineCheck.setChecked(False)
+ self.checkCmdlineRegexp.setEnabled(False)
+ self.checkCmdlineRegexp.setChecked(False)
+ self.cmdlineLine.setText("")
+
+ self.uidCheck.setChecked(False)
+ self.uidLine.setText("")
+
+ self.pidCheck.setChecked(False)
+ self.pidLine.setText("")
+
+ self.dstPortCheck.setChecked(False)
+ self.dstPortLine.setText("")
+
+ self.dstIPCheck.setChecked(False)
+ self.dstIPCombo.setCurrentText("")
+
+ self.dstHostCheck.setChecked(False)
+ self.dstHostLine.setText("")
+
+ self.selectListButton.setEnabled(False)
+ self.dstListsCheck.setChecked(False)
+ self.dstListsLine.setText("")
+
+ self.selectListRegexpButton.setEnabled(False)
+ self.dstListRegexpCheck.setChecked(False)
+ self.dstRegexpListsLine.setText("")
+
+ self.selectIPsListButton.setEnabled(False)
+ self.dstListIPsCheck.setChecked(False)
+ self.dstListIPsLine.setText("")
+
+ self.selectNetsListButton.setEnabled(False)
+ self.dstListNetsCheck.setChecked(False)
+ self.dstListNetsLine.setText("")
+
+ def _load_rule(self, addr=None, rule=None):
+ if self._load_nodes(addr) == False:
+ return False
+
+ self.ruleNameEdit.setText(rule.name)
+ self.enableCheck.setChecked(rule.enabled)
+ self.precedenceCheck.setChecked(rule.precedence)
+ if rule.action == Config.ACTION_DENY:
+ self.actionDenyRadio.setChecked(True)
+ elif rule.action == Config.ACTION_ALLOW:
+ self.actionAllowRadio.setChecked(True)
+ elif rule.action == Config.ACTION_REJECT:
+ self.actionRejectRadio.setChecked(True)
+
+ self.durationCombo.setCurrentIndex(self._load_duration(self.rule.duration))
+
+ if self.rule.operator.type != Config.RULE_TYPE_LIST:
+ self._load_rule_operator(self.rule.operator)
+ else:
+ rule_options = json.loads(self.rule.operator.data)
+ for r in rule_options:
+ _sensitive = False
+ if 'sensitive' in r:
+ _sensitive = r['sensitive']
+
+ op = ui_pb2.Operator(type=r['type'], operand=r['operand'], data=r['data'], sensitive=_sensitive)
+ self._load_rule_operator(op)
+
+ return True
+
+ def _load_rule_operator(self, operator):
+ self.sensitiveCheck.setChecked(operator.sensitive)
+ if operator.operand == "protocol":
+ self.protoCheck.setChecked(True)
+ self.protoCombo.setEnabled(True)
+ self.protoCombo.setCurrentText(operator.data.upper())
+
+ if operator.operand == "process.path":
+ self.procCheck.setChecked(True)
+ self.procLine.setEnabled(True)
+ self.procLine.setText(operator.data)
+ self.checkProcRegexp.setEnabled(True)
+ self.checkProcRegexp.setChecked(operator.type == Config.RULE_TYPE_REGEXP)
+
+ if operator.operand == "process.command":
+ self.cmdlineCheck.setChecked(True)
+ self.cmdlineLine.setEnabled(True)
+ self.cmdlineLine.setText(operator.data)
+ self.checkCmdlineRegexp.setEnabled(True)
+ self.checkCmdlineRegexp.setChecked(operator.type == Config.RULE_TYPE_REGEXP)
+
+ if operator.operand == "user.id":
+ self.uidCheck.setChecked(True)
+ self.uidLine.setEnabled(True)
+ self.uidLine.setText(operator.data)
+
+ if operator.operand == "process.id":
+ self.pidCheck.setChecked(True)
+ self.pidLine.setEnabled(True)
+ self.pidLine.setText(operator.data)
+
+ if operator.operand == "dest.port":
+ self.dstPortCheck.setChecked(True)
+ self.dstPortLine.setEnabled(True)
+ self.dstPortLine.setText(operator.data)
+
+ if operator.operand == "dest.ip" or operator.operand == "dest.network":
+ self.dstIPCheck.setChecked(True)
+ self.dstIPCombo.setEnabled(True)
+ if operator.data == self.LAN_RANGES:
+ self.dstIPCombo.setCurrentText(self.LAN_LABEL)
+ else:
+ self.dstIPCombo.setCurrentText(operator.data)
+
+ if operator.operand == "dest.host":
+ self.dstHostCheck.setChecked(True)
+ self.dstHostLine.setEnabled(True)
+ self.dstHostLine.setText(operator.data)
+
+ if operator.operand == "lists.domains":
+ self.dstListsCheck.setChecked(True)
+ self.dstListsCheck.setEnabled(True)
+ self.dstListsLine.setText(operator.data)
+ self.selectListButton.setEnabled(True)
+
+ if operator.operand == "lists.domains_regexp":
+ self.dstListRegexpCheck.setChecked(True)
+ self.dstListRegexpCheck.setEnabled(True)
+ self.dstRegexpListsLine.setText(operator.data)
+ self.selectListRegexpButton.setEnabled(True)
+
+ if operator.operand == "lists.ips":
+ self.dstListIPsCheck.setChecked(True)
+ self.dstListIPsCheck.setEnabled(True)
+ self.dstListIPsLine.setText(operator.data)
+ self.selectIPsListButton.setEnabled(True)
+
+ if operator.operand == "lists.nets":
+ self.dstListNetsCheck.setChecked(True)
+ self.dstListNetsCheck.setEnabled(True)
+ self.dstListNetsLine.setText(operator.data)
+ self.selectNetsListButton.setEnabled(True)
+
+ def _load_nodes(self, addr=None):
+ try:
+ self.nodesCombo.clear()
+ self._node_list = self._nodes.get()
+
+ if addr != None and addr not in self._node_list:
+ Message.ok(QC.translate("rules", "<b>Error loading rule</b>"),
+ QC.translate("rules", "node {0} not connected".format(addr)),
+ QtWidgets.QMessageBox.Warning)
+ return False
+
+ if len(self._node_list) < 2:
+ self.nodeApplyAllCheck.setVisible(False)
+
+ for node in self._node_list:
+ self.nodesCombo.addItem(node)
+
+ if addr != None:
+ self.nodesCombo.setCurrentText(addr)
+
+ except Exception as e:
+ print(self.LOG_TAG, "exception loading nodes: ", e, addr)
+ return False
+
+ return True
+
+ def _insert_rule_to_db(self, node_addr):
+ self._db.insert("rules",
+ "(time, node, name, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)",
+ (datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ node_addr, self.rule.name,
+ str(self.rule.enabled), str(self.rule.precedence),
+ self.rule.action, self.rule.duration, self.rule.operator.type,
+ str(self.rule.operator.sensitive), self.rule.operator.operand, self.rule.operator.data),
+ action_on_conflict="REPLACE")
+
+ def _add_rule(self):
+ try:
+ if self.nodeApplyAllCheck.isChecked():
+ for pos in range(self.nodesCombo.count()):
+ self._insert_rule_to_db(self.nodesCombo.itemText(pos))
+ else:
+ self._insert_rule_to_db(self.nodesCombo.currentText())
+
+ notif = ui_pb2.Notification(
+ id=int(str(time.time()).replace(".", "")),
+ type=ui_pb2.CHANGE_RULE,
+ data="",
+ rules=[self.rule])
+ if self.nodeApplyAllCheck.isChecked():
+ nid = self._nodes.send_notifications(notif, self._notification_callback)
+ else:
+ nid = self._nodes.send_notification(self.nodesCombo.currentText(), notif, self._notification_callback)
+
+ self._notifications_sent[nid] = notif
+ except Exception as e:
+ print(self.LOG_TAG, "add_rule() exception: ", e)
+
+ def _delete_rule(self):
+ try:
+ # if the rule name has changed, we need to remove the old one
+ if self._old_rule_name != self.rule.name:
+ node = self.nodesCombo.currentText()
+ old_rule = self.rule
+ old_rule.name = self._old_rule_name
+ if self.nodeApplyAllCheck.isChecked():
+ nid, noti = self._nodes.delete_rule(rule_name=self._old_rule_name, addr=None, callback=self._notification_callback)
+ self._notifications_sent[nid] = noti
+ else:
+ nid, noti = self._nodes.delete_rule(self._old_rule_name, node, self._notification_callback)
+ self._notifications_sent[nid] = noti
+
+ except Exception as e:
+ print(self.LOG_TAG, "delete_rule() exception: ", e)
+
+
+ def _save_rule(self):
+ """
+ Create a new rule based on the fields selected.
+
+ Ensure that some constraints are met:
+ - Determine if a field can be a regexp.
+ - Validate regexp.
+ - Fields cannot be empty.
+ - If the user has not provided a rule name, auto assign one.
+ """
+ self.rule = ui_pb2.Rule()
+ self.rule.name = self.ruleNameEdit.text()
+ self.rule.enabled = self.enableCheck.isChecked()
+ self.rule.precedence = self.precedenceCheck.isChecked()
+ self.rule.operator.type = Config.RULE_TYPE_SIMPLE
+ self.rule.action = Config.ACTION_DENY
+ if self.actionAllowRadio.isChecked():
+ self.rule.action = Config.ACTION_ALLOW
+ elif self.actionRejectRadio.isChecked():
+ self.rule.action = Config.ACTION_REJECT
+
+ self.rule.duration = self._get_duration(self.durationCombo.currentIndex())
+
+ # FIXME: there should be a sensitive checkbox per operand
+ self.rule.operator.sensitive = self.sensitiveCheck.isChecked()
+ rule_data = []
+ if self.protoCheck.isChecked():
+ if self.protoCombo.currentText() == "":
+ return False, QC.translate("rules", "protocol can not be empty, or uncheck it")
+
+ self.rule.operator.operand = "protocol"
+ self.rule.operator.data = self.protoCombo.currentText()
+ rule_data.append(
+ {
+ "type": Config.RULE_TYPE_SIMPLE,
+ "operand": "protocol",
+ "data": self.protoCombo.currentText().lower(),
+ "sensitive": self.sensitiveCheck.isChecked()
+ })
+ if self._is_regex(self.protoCombo.currentText()):
+ rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP
+ if self._is_valid_regex(self.protoCombo.currentText()) == False:
+ return False, QC.translate("rules", "Protocol regexp error")
+
+ if self.procCheck.isChecked():
+ if self.procLine.text() == "":
+ return False, QC.translate("rules", "process path can not be empty")
+
+ self.rule.operator.operand = "process.path"
+ self.rule.operator.data = self.procLine.text()
+ rule_data.append(
+ {
+ "type": Config.RULE_TYPE_SIMPLE,
+ "operand": "process.path",
+ "data": self.procLine.text(),
+ "sensitive": self.sensitiveCheck.isChecked()
+ })
+ if self.checkProcRegexp.isChecked():
+ rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP
+ if self._is_valid_regex(self.procLine.text()) == False:
+ return False, QC.translate("rules", "Process path regexp error")
+
+ if self.cmdlineCheck.isChecked():
+ if self.cmdlineLine.text() == "":
+ return False, QC.translate("rules", "command line can not be empty")
+
+ self.rule.operator.operand = "process.command"
+ self.rule.operator.data = self.cmdlineLine.text()
+ rule_data.append(
+ {
+ 'type': Config.RULE_TYPE_SIMPLE,
+ 'operand': 'process.command',
+ 'data': self.cmdlineLine.text(),
+ "sensitive": self.sensitiveCheck.isChecked()
+ })
+ if self.checkCmdlineRegexp.isChecked():
+ rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP
+ if self._is_valid_regex(self.cmdlineLine.text()) == False:
+ return False, QC.translate("rules", "Command line regexp error")
+
+ if self.dstPortCheck.isChecked():
+ if self.dstPortLine.text() == "":
+ return False, QC.translate("rules", "Dest port can not be empty")
+
+ self.rule.operator.operand = "dest.port"
+ self.rule.operator.data = self.dstPortLine.text()
+ rule_data.append(
+ {
+ 'type': Config.RULE_TYPE_SIMPLE,
+ 'operand': 'dest.port',
+ 'data': self.dstPortLine.text(),
+ "sensitive": self.sensitiveCheck.isChecked()
+ })
+ if self._is_regex(self.dstPortLine.text()):
+ rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP
+ if self._is_valid_regex(self.dstPortLine.text()) == False:
+ return False, QC.translate("rules", "Dst port regexp error")
+
+ if self.dstHostCheck.isChecked():
+ if self.dstHostLine.text() == "":
+ return False, QC.translate("rules", "Dest host can not be empty")
+
+ self.rule.operator.operand = "dest.host"
+ self.rule.operator.data = self.dstHostLine.text()
+ rule_data.append(
+ {
+ 'type': Config.RULE_TYPE_SIMPLE,
+ 'operand': 'dest.host',
+ 'data': self.dstHostLine.text(),
+ "sensitive": self.sensitiveCheck.isChecked()
+ })
+ if self._is_regex(self.dstHostLine.text()):
+ rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP
+ if self._is_valid_regex(self.dstHostLine.text()) == False:
+ return False, QC.translate("rules", "Dst host regexp error")
+
+ if self.dstIPCheck.isChecked():
+ if self.dstIPCombo.currentText() == "":
+ return False, QC.translate("rules", "Dest IP/Network can not be empty")
+
+ dstIPtext = self.dstIPCombo.currentText()
+
+ if dstIPtext == self.LAN_LABEL:
+ self.rule.operator.operand = "dest.ip"
+ self.rule.operator.type = Config.RULE_TYPE_REGEXP
+ dstIPtext = self.LAN_RANGES
+ else:
+ try:
+ if type(ipaddress.ip_address(self.dstIPCombo.currentText())) == ipaddress.IPv4Address \
+ or type(ipaddress.ip_address(self.dstIPCombo.currentText())) == ipaddress.IPv6Address:
+ self.rule.operator.operand = "dest.ip"
+ self.rule.operator.type = Config.RULE_TYPE_SIMPLE
+ except Exception:
+ self.rule.operator.operand = "dest.network"
+ self.rule.operator.type = Config.RULE_TYPE_NETWORK
+
+ if self._is_regex(dstIPtext):
+ self.rule.operator.operand = "dest.ip"
+ self.rule.operator.type = Config.RULE_TYPE_REGEXP
+ if self._is_valid_regex(self.dstIPCombo.currentText()) == False:
+ return False, QC.translate("rules", "Dst IP regexp error")
+
+ rule_data.append(
+ {
+ 'type': self.rule.operator.type,
+ 'operand': self.rule.operator.operand,
+ 'data': dstIPtext,
+ "sensitive": self.sensitiveCheck.isChecked()
+ })
+
+ if self.uidCheck.isChecked():
+ if self.uidLine.text() == "":
+ return False, QC.translate("rules", "User ID can not be empty")
+
+ self.rule.operator.operand = "user.id"
+ self.rule.operator.data = self.uidLine.text()
+ rule_data.append(
+ {
+ 'type': Config.RULE_TYPE_SIMPLE,
+ 'operand': 'user.id',
+ 'data': self.uidLine.text(),
+ "sensitive": self.sensitiveCheck.isChecked()
+ })
+ if self._is_regex(self.uidLine.text()):
+ rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP
+ if self._is_valid_regex(self.uidLine.text()) == False:
+ return False, QC.translate("rules", "User ID regexp error")
+
+ if self.pidCheck.isChecked():
+ if self.pidLine.text() == "":
+ return False, QC.translate("rules", "PID field can not be empty")
+
+ self.rule.operator.operand = "process.id"
+ self.rule.operator.data = self.pidLine.text()
+ rule_data.append(
+ {
+ 'type': Config.RULE_TYPE_SIMPLE,
+ 'operand': 'process.id',
+ 'data': self.pidLine.text(),
+ "sensitive": self.sensitiveCheck.isChecked()
+ })
+ if self._is_regex(self.pidLine.text()):
+ rule_data[len(rule_data)-1]['type'] = Config.RULE_TYPE_REGEXP
+ if self._is_valid_regex(self.pidLine.text()) == False:
+ return False, QC.translate("rules", "PID field regexp error")
+
+ if self.dstListsCheck.isChecked():
+ if self.dstListsLine.text() == "":
+ return False, QC.translate("rules", "Lists field cannot be empty")
+ if os.path.isdir(self.dstListsLine.text()) == False:
+ return False, QC.translate("rules", "Lists field must be a directory")
+
+ self.rule.operator.type = Config.RULE_TYPE_LISTS
+ self.rule.operator.operand = "lists.domains"
+ rule_data.append(
+ {
+ 'type': Config.RULE_TYPE_LISTS,
+ 'operand': 'lists.domains',
+ 'data': self.dstListsLine.text(),
+ 'sensitive': self.sensitiveCheck.isChecked()
+ })
+ self.rule.operator.data = json.dumps(rule_data)
+
+ if self.dstListRegexpCheck.isChecked():
+ if self.dstRegexpListsLine.text() == "":
+ return False, QC.translate("rules", "Lists field cannot be empty")
+ if os.path.isdir(self.dstRegexpListsLine.text()) == False:
+ return False, QC.translate("rules", "Lists field must be a directory")
+
+ self.rule.operator.type = Config.RULE_TYPE_LISTS
+ self.rule.operator.operand = "lists.domains_regexp"
+ rule_data.append(
+ {
+ 'type': Config.RULE_TYPE_LISTS,
+ 'operand': 'lists.domains_regexp',
+ 'data': self.dstRegexpListsLine.text(),
+ 'sensitive': self.sensitiveCheck.isChecked()
+ })
+ self.rule.operator.data = json.dumps(rule_data)
+
+ if self.dstListNetsCheck.isChecked():
+ if self.dstListNetsLine.text() == "":
+ return False, QC.translate("rules", "Lists field cannot be empty")
+ if os.path.isdir(self.dstListNetsLine.text()) == False:
+ return False, QC.translate("rules", "Lists field must be a directory")
+
+ self.rule.operator.type = Config.RULE_TYPE_LISTS
+ self.rule.operator.operand = "lists.nets"
+ rule_data.append(
+ {
+ 'type': Config.RULE_TYPE_LISTS,
+ 'operand': 'lists.nets',
+ 'data': self.dstListNetsLine.text(),
+ 'sensitive': self.sensitiveCheck.isChecked()
+ })
+ self.rule.operator.data = json.dumps(rule_data)
+
+
+ if self.dstListIPsCheck.isChecked():
+ if self.dstListIPsLine.text() == "":
+ return False, QC.translate("rules", "Lists field cannot be empty")
+ if os.path.isdir(self.dstListIPsLine.text()) == False:
+ return False, QC.translate("rules", "Lists field must be a directory")
+
+ self.rule.operator.type = Config.RULE_TYPE_LISTS
+ self.rule.operator.operand = "lists.ips"
+ rule_data.append(
+ {
+ 'type': Config.RULE_TYPE_LISTS,
+ 'operand': 'lists.ips',
+ 'data': self.dstListIPsLine.text(),
+ 'sensitive': self.sensitiveCheck.isChecked()
+ })
+ self.rule.operator.data = json.dumps(rule_data)
+
+ if len(rule_data) >= 2:
+ self.rule.operator.type = Config.RULE_TYPE_LIST
+ self.rule.operator.operand = Config.RULE_TYPE_LIST
+ self.rule.operator.data = json.dumps(rule_data)
+
+ elif len(rule_data) == 1:
+ self.rule.operator.operand = rule_data[0]['operand']
+ self.rule.operator.data = rule_data[0]['data']
+ if self.checkProcRegexp.isChecked():
+ self.rule.operator.type = Config.RULE_TYPE_REGEXP
+ elif self.checkCmdlineRegexp.isChecked():
+ self.rule.operator.type = Config.RULE_TYPE_REGEXP
+ elif (self.procCheck.isChecked() == False and self.cmdlineCheck.isChecked() == False) \
+ and self._is_regex(self.rule.operator.data):
+ self.rule.operator.type = Config.RULE_TYPE_REGEXP
+
+ else:
+ return False, QC.translate("rules", "Select at least one field.")
+
+ if self.ruleNameEdit.text() == "":
+ self.rule.name = slugify("%s %s %s" % (self.rule.action, self.rule.operator.type, self.rule.operator.data))
+
+ return True, ""
+
+ def edit_rule(self, records, _addr=None):
+ self.WORK_MODE = self.EDIT_RULE
+ self._reset_state()
+
+ self.rule = self.get_rule_from_records(records)
+ if self.rule.operator.type not in Config.RulesTypes:
+ Message.ok(QC.translate("rules", "<b>Rule not supported</b>"),
+ QC.translate("rules", "This type of rule ({0}) is not supported by version {1}".format(self.rule.operator.type, version)),
+ QtWidgets.QMessageBox.Warning)
+ self.hide()
+ return
+
+ self._old_rule_name = records.value(2)
+
+ if self._load_rule(addr=_addr, rule=self.rule):
+ self.show()
+
+ def new_rule(self):
+ self.WORK_MODE = self.ADD_RULE
+ self._reset_state()
+ self._load_nodes()
+ self.show()
--- /dev/null
+import threading
+import datetime
+import sys
+import os
+import csv
+import io
+
+from PyQt5 import QtCore, QtGui, uic, QtWidgets
+from PyQt5.QtCore import QCoreApplication as QC
+
+from opensnitch import ui_pb2
+from opensnitch.config import Config
+from opensnitch.version import version
+from opensnitch.nodes import Nodes
+from opensnitch.dialogs.preferences import PreferencesDialog
+from opensnitch.dialogs.ruleseditor import RulesEditorDialog
+from opensnitch.dialogs.processdetails import ProcessDetailsDialog
+from opensnitch.customwidgets.main import ColorizedDelegate, ConnectionsTableModel
+from opensnitch.customwidgets.generictableview import GenericTableModel
+from opensnitch.customwidgets.addresstablemodel import AddressTableModel
+from opensnitch.utils import Message, QuickHelp, AsnDB
+
+DIALOG_UI_PATH = "%s/../res/stats.ui" % os.path.dirname(sys.modules[__name__].__file__)
+class StatsDialog(QtWidgets.QDialog, uic.loadUiType(DIALOG_UI_PATH)[0]):
+ RED = QtGui.QColor(0xff, 0x63, 0x47)
+ GREEN = QtGui.QColor(0x2e, 0x90, 0x59)
+ PURPLE = QtGui.QColor(0x7f, 0x00, 0xff)
+
+ _trigger = QtCore.pyqtSignal(bool, bool)
+ settings_saved = QtCore.pyqtSignal()
+ _status_changed_trigger = QtCore.pyqtSignal(bool)
+ _shown_trigger = QtCore.pyqtSignal()
+ _notification_trigger = QtCore.pyqtSignal(ui_pb2.Notification)
+ _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
+
+ SORT_ORDER = ["ASC", "DESC"]
+ LIMITS = ["LIMIT 50", "LIMIT 100", "LIMIT 200", "LIMIT 300", ""]
+ LAST_GROUP_BY = ""
+
+ # general
+ COL_TIME = 0
+ COL_NODE = 1
+ COL_ACTION = 2
+ COL_DSTIP = 3
+ COL_PROTO = 4
+ COL_PROCS = 5
+ COL_RULES = 6
+ GENERAL_COL_NUM = 7
+
+ # stats
+ COL_WHAT = 0
+
+ # rules
+ COL_R_NODE = 1
+ COL_R_NAME = 2
+ COL_R_ENABLED = 3
+ COL_R_ACTION = 4
+ COL_R_DURATION = 5
+ COL_R_OP_TYPE = 6
+ COL_R_OP_OPERAND = 7
+
+ # procs
+ COL_PID = 6
+
+ TAB_MAIN = 0
+ TAB_NODES = 1
+ TAB_RULES = 2
+ TAB_HOSTS = 3
+ TAB_PROCS = 4
+ TAB_ADDRS = 5
+ TAB_PORTS = 6
+ TAB_USERS = 7
+
+ # row of entries
+ RULES_TREE_APPS = 0
+ RULES_TREE_NODES = 1
+ RULES_TREE_PERMANENT = 0
+ RULES_TREE_TEMPORARY = 1
+
+ RULES_COMBO_PERMANENT = 1
+ RULES_COMBO_TEMPORARY = 2
+
+ RULES_TYPE_PERMANENT = 0
+ RULES_TYPE_TEMPORARY = 1
+
+ FILTER_TREE_APPS = 0
+ FILTER_TREE_NODES = 3
+
+ # FIXME: don't translate, used only for default argument on _update_status_label
+ FIREWALL_DISABLED = "Disabled"
+
+ # if the user clicks on an item of a table, it'll enter into the detail
+ # view. From there, deny further clicks on the items.
+ IN_DETAIL_VIEW = {
+ TAB_MAIN: False,
+ TAB_NODES: False,
+ TAB_RULES: False,
+ TAB_HOSTS: False,
+ TAB_PROCS: False,
+ TAB_ADDRS: False,
+ TAB_PORTS: False,
+ TAB_USERS: False
+ }
+ # restore scrollbar position when going back from a detail view
+ LAST_SCROLL_VALUE = None
+ # try to restore last selection
+ LAST_SELECTED_ITEM = ""
+
+ commonDelegateConf = {
+ Config.ACTION_DENY: RED,
+ Config.ACTION_REJECT: PURPLE,
+ Config.ACTION_ALLOW: GREEN,
+ 'alignment': QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter
+ }
+
+ commonTableConf = {
+ "name": "",
+ "label": None,
+ "cmd": None,
+ "view": None,
+ "model": None,
+ "delegate": commonDelegateConf,
+ "display_fields": "*"
+ }
+
+ TABLES = {
+ TAB_MAIN: {
+ "name": "connections",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": commonDelegateConf,
+ "display_fields": "time as Time, " \
+ "node as Node, " \
+ "action as Action, " \
+ "CASE dst_host WHEN ''" \
+ " THEN dst_ip || ' -> ' || dst_port " \
+ " ELSE dst_host || ' -> ' || dst_port " \
+ "END Destination, " \
+ "protocol as Protocol, " \
+ "process as Process, " \
+ "rule as Rule",
+ "group_by": LAST_GROUP_BY,
+ "last_order_by": "1",
+ "last_order_to": 1,
+ "rows_selected": False
+ },
+ TAB_NODES: {
+ "name": "nodes",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": {
+ Config.ACTION_DENY: RED,
+ Config.ACTION_REJECT: PURPLE,
+ Config.ACTION_ALLOW: GREEN,
+ Nodes.OFFLINE: RED,
+ Nodes.ONLINE: GREEN,
+ 'alignment': QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter
+ },
+ "display_fields": "last_connection as LastConnection, "\
+ "addr as Addr, " \
+ "status as Status, " \
+ "hostname as Hostname, " \
+ "daemon_version as Version, " \
+ "daemon_uptime as Uptime, " \
+ "daemon_rules as Rules," \
+ "cons as Connections," \
+ "cons_dropped as Dropped," \
+ "version as Version",
+ "header_labels": [],
+ "last_order_by": "1",
+ "last_order_to": 1,
+ "rows_selected": False
+ },
+ TAB_RULES: {
+ "name": "rules",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": commonDelegateConf,
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 0,
+ "rows_selected": False
+ },
+ TAB_HOSTS: {
+ "name": "hosts",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": commonDelegateConf,
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1,
+ "rows_selected": False
+ },
+ TAB_PROCS: {
+ "name": "procs",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": commonDelegateConf,
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1,
+ "rows_selected": False
+ },
+ TAB_ADDRS: {
+ "name": "addrs",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": commonDelegateConf,
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1,
+ "rows_selected": False
+ },
+ TAB_PORTS: {
+ "name": "ports",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": commonDelegateConf,
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1,
+ "rows_selected": False
+ },
+ TAB_USERS: {
+ "name": "users",
+ "label": None,
+ "cmd": None,
+ "cmdCleanStats": None,
+ "view": None,
+ "filterLine": None,
+ "model": None,
+ "delegate": commonDelegateConf,
+ "display_fields": "*",
+ "header_labels": [],
+ "last_order_by": "2",
+ "last_order_to": 1,
+ "rows_selected": False
+ }
+ }
+
+ def __init__(self, parent=None, address=None, db=None, dbname="db", appicon=None):
+ super(StatsDialog, self).__init__(parent)
+ QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowStaysOnTopHint)
+
+ self._current_desktop = os.environ['XDG_CURRENT_DESKTOP'] if os.environ.get("XDG_CURRENT_DESKTOP") != None else None
+
+ self.setWindowFlags(QtCore.Qt.Window)
+ self.setupUi(self)
+ self.setWindowIcon(appicon)
+
+ # columns names. Must be added here in order to names be translated.
+ self.COL_STR_NAME = QC.translate("stats", "Name", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_ADDR = QC.translate("stats", "Address", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_STATUS = QC.translate("stats", "Status", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_HOSTNAME = QC.translate("stats", "Hostname", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_UPTIME = QC.translate("stats", "Uptime", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_VERSION = QC.translate("stats", "Version", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_RULES_NUM = QC.translate("stats", "Rules", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_TIME = QC.translate("stats", "Time", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_ACTION = QC.translate("stats", "Action", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_DURATION = QC.translate("stats", "Duration", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_NODE = QC.translate("stats", "Node", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_ENABLED = QC.translate("stats", "Enabled", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_PRECEDENCE = QC.translate("stats", "Precedence", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_HITS = QC.translate("stats", "Hits", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_PROTOCOL = QC.translate("stats", "Protocol", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_PROCESS = QC.translate("stats", "Process", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_PROC_ARGS = QC.translate("stats", "Args", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_DESTINATION = QC.translate("stats", "Destination", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_DST_IP = QC.translate("stats", "DstIP", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_DST_HOST = QC.translate("stats", "DstHost", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_DST_PORT = QC.translate("stats", "DstPort", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_RULE = QC.translate("stats", "Rule", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_UID = QC.translate("stats", "UserID", "This is a word, without spaces and symbols.").replace(" ", "")
+ self.COL_STR_LAST_CONNECTION = QC.translate("stats", "LastConnection", "This is a word, without spaces and symbols.").replace(" ", "")
+
+ self.FIREWALL_STOPPED = QC.translate("stats", "Not running")
+ self.FIREWALL_DISABLED = QC.translate("stats", "Disabled")
+ self.FIREWALL_RUNNING = QC.translate("stats", "Running")
+
+ self._db = db
+ self._db_sqlite = self._db.get_db()
+ self._db_name = dbname
+
+ self.asndb = AsnDB.instance()
+
+ self._cfg = Config.get()
+ self._nodes = Nodes.instance()
+
+ # TODO: allow to display multiples dialogs
+ self._proc_details_dialog = ProcessDetailsDialog(appicon=appicon)
+ # TODO: allow to navigate records by offsets
+ self.prevButton.setVisible(False)
+ self.nextButton.setVisible(False)
+
+ self.daemon_connected = False
+ # skip table updates if a context menu is active
+ self._context_menu_active = False
+ # used to skip updates while the user is moving the scrollbar
+ self.scrollbar_active = False
+
+ self._lock = threading.RLock()
+ self._address = address
+ self._stats = None
+ self._notifications_sent = {}
+
+ self._prefs_dialog = PreferencesDialog(appicon=appicon)
+ self._rules_dialog = RulesEditorDialog(appicon=appicon)
+ self._prefs_dialog.saved.connect(self._on_settings_saved)
+ self._trigger.connect(self._on_update_triggered)
+ self._notification_callback.connect(self._cb_notification_callback)
+
+ self.nodeLabel.setText("")
+ self.nodeLabel.setStyleSheet('color: green;font-size:12pt; font-weight:600;')
+ self.rulesSplitter.setStretchFactor(0,0)
+ self.rulesSplitter.setStretchFactor(1,2)
+
+ self.startButton.clicked.connect(self._cb_start_clicked)
+ self.prefsButton.clicked.connect(self._cb_prefs_clicked)
+ self.saveButton.clicked.connect(self._on_save_clicked)
+ self.comboAction.currentIndexChanged.connect(self._cb_combo_action_changed)
+ self.limitCombo.currentIndexChanged.connect(self._cb_limit_combo_changed)
+ self.tabWidget.currentChanged.connect(self._cb_tab_changed)
+ self.delRuleButton.clicked.connect(self._cb_del_rule_clicked)
+ self.rulesSplitter.splitterMoved.connect(self._cb_rules_splitter_moved)
+ self.rulesTreePanel.itemClicked.connect(self._cb_rules_tree_item_clicked)
+ self.enableRuleCheck.clicked.connect(self._cb_enable_rule_toggled)
+ self.editRuleButton.clicked.connect(self._cb_edit_rule_clicked)
+ self.newRuleButton.clicked.connect(self._cb_new_rule_clicked)
+ self.cmdProcDetails.clicked.connect(self._cb_proc_details_clicked)
+ self.comboRulesFilter.currentIndexChanged.connect(self._cb_rules_filter_combo_changed)
+ self.helpButton.clicked.connect(self._cb_help_button_clicked)
+ self.nextButton.clicked.connect(self._cb_next_button_clicked)
+ self.prevButton.clicked.connect(self._cb_prev_button_clicked)
+
+ self.enableRuleCheck.setVisible(False)
+ self.delRuleButton.setVisible(False)
+ self.editRuleButton.setVisible(False)
+ self.nodeRuleLabel.setVisible(False)
+ self.comboRulesFilter.setVisible(False)
+
+ # translations must be done here, otherwise they don't take effect
+ self.TABLES[self.TAB_NODES]['header_labels'] = [
+ self.COL_STR_LAST_CONNECTION,
+ self.COL_STR_ADDR,
+ self.COL_STR_STATUS,
+ self.COL_STR_HOSTNAME,
+ self.COL_STR_VERSION,
+ self.COL_STR_UPTIME,
+ QC.translate("stats", "Rules", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Connections", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Dropped", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Version", "This is a word, without spaces and symbols.").replace(" ", ""),
+ ]
+
+ self.TABLES[self.TAB_RULES]['header_labels'] = [
+ self.COL_STR_TIME,
+ self.COL_STR_NODE,
+ self.COL_STR_NAME,
+ self.COL_STR_ENABLED,
+ self.COL_STR_PRECEDENCE,
+ self.COL_STR_ACTION,
+ self.COL_STR_DURATION,
+ "operator_type",
+ "operator_sensitive",
+ "operator_operand",
+ "operator_data",
+ ]
+
+ stats_headers = [
+ QC.translate("stats", "What", "This is a word, without spaces and symbols.").replace(" ", ""),
+ QC.translate("stats", "Hits", "This is a word, without spaces and symbols.").replace(" ", ""),
+ ]
+
+ self.TABLES[self.TAB_HOSTS]['header_labels'] = stats_headers
+ self.TABLES[self.TAB_PROCS]['header_labels'] = stats_headers
+ self.TABLES[self.TAB_ADDRS]['header_labels'] = stats_headers
+ self.TABLES[self.TAB_USERS]['header_labels'] = stats_headers
+
+ self.TABLES[self.TAB_MAIN]['view'] = self._setup_table(QtWidgets.QTableView, self.eventsTable, "connections",
+ self.TABLES[self.TAB_MAIN]['display_fields'],
+ order_by="1",
+ group_by=self.TABLES[self.TAB_MAIN]['group_by'],
+ delegate=self.TABLES[self.TAB_MAIN]['delegate'],
+ resize_cols=(),
+ model=GenericTableModel("connections", [
+ self.COL_STR_TIME,
+ self.COL_STR_NODE,
+ self.COL_STR_ACTION,
+ self.COL_STR_DESTINATION,
+ self.COL_STR_PROTOCOL,
+ self.COL_STR_PROCESS,
+ self.COL_STR_RULE,
+ ]),
+ verticalScrollBar=self.connectionsTableScrollBar,
+ limit=self._get_limit()
+ )
+ self.TABLES[self.TAB_NODES]['view'] = self._setup_table(QtWidgets.QTableView, self.nodesTable, "nodes",
+ self.TABLES[self.TAB_NODES]['display_fields'],
+ order_by="3,2,1",
+ resize_cols=(self.COL_NODE,),
+ model=GenericTableModel("nodes", self.TABLES[self.TAB_NODES]['header_labels']),
+ verticalScrollBar=self.verticalScrollBar,
+ sort_direction=self.SORT_ORDER[1],
+ delegate=self.TABLES[self.TAB_NODES]['delegate'])
+ self.TABLES[self.TAB_RULES]['view'] = self._setup_table(QtWidgets.QTableView,
+ self.rulesTable, "rules",
+ model=GenericTableModel("rules", self.TABLES[self.TAB_RULES]['header_labels']),
+ verticalScrollBar=self.rulesScrollBar,
+ delegate=self.TABLES[self.TAB_RULES]['delegate'],
+ order_by="2",
+ sort_direction=self.SORT_ORDER[0])
+ self.TABLES[self.TAB_HOSTS]['view'] = self._setup_table(QtWidgets.QTableView,
+ self.hostsTable, "hosts",
+ model=GenericTableModel("hosts", self.TABLES[self.TAB_HOSTS]['header_labels']),
+ verticalScrollBar=self.hostsScrollBar,
+ resize_cols=(self.COL_WHAT,),
+ delegate=self.TABLES[self.TAB_HOSTS]['delegate'],
+ order_by="2",
+ limit=self._get_limit()
+ )
+ self.TABLES[self.TAB_PROCS]['view'] = self._setup_table(QtWidgets.QTableView,
+ self.procsTable, "procs",
+ model=GenericTableModel("procs", self.TABLES[self.TAB_PROCS]['header_labels']),
+ verticalScrollBar=self.procsScrollBar,
+ resize_cols=(self.COL_WHAT,),
+ delegate=self.TABLES[self.TAB_PROCS]['delegate'],
+ order_by="2",
+ limit=self._get_limit()
+ )
+ self.TABLES[self.TAB_ADDRS]['view'] = self._setup_table(QtWidgets.QTableView,
+ self.addrTable, "addrs",
+ model=AddressTableModel("addrs", self.TABLES[self.TAB_ADDRS]['header_labels']),
+ verticalScrollBar=self.addrsScrollBar,
+ resize_cols=(self.COL_WHAT,),
+ delegate=self.TABLES[self.TAB_ADDRS]['delegate'],
+ order_by="2",
+ limit=self._get_limit()
+ )
+ self.TABLES[self.TAB_PORTS]['view'] = self._setup_table(QtWidgets.QTableView,
+ self.portsTable, "ports",
+ model=GenericTableModel("ports", self.TABLES[self.TAB_PORTS]['header_labels']),
+ verticalScrollBar=self.portsScrollBar,
+ resize_cols=(self.COL_WHAT,),
+ delegate=self.TABLES[self.TAB_PORTS]['delegate'],
+ order_by="2",
+ limit=self._get_limit()
+ )
+ self.TABLES[self.TAB_USERS]['view'] = self._setup_table(QtWidgets.QTableView,
+ self.usersTable, "users",
+ model=GenericTableModel("users", self.TABLES[self.TAB_USERS]['header_labels']),
+ verticalScrollBar=self.usersScrollBar,
+ resize_cols=(self.COL_WHAT,),
+ delegate=self.TABLES[self.TAB_USERS]['delegate'],
+ order_by="2",
+ limit=self._get_limit()
+ )
+
+ self.TABLES[self.TAB_NODES]['label'] = self.nodesLabel
+ self.TABLES[self.TAB_RULES]['label'] = self.ruleLabel
+ self.TABLES[self.TAB_HOSTS]['label'] = self.hostsLabel
+ self.TABLES[self.TAB_PROCS]['label'] = self.procsLabel
+ self.TABLES[self.TAB_ADDRS]['label'] = self.addrsLabel
+ self.TABLES[self.TAB_PORTS]['label'] = self.portsLabel
+ self.TABLES[self.TAB_USERS]['label'] = self.usersLabel
+
+ self.TABLES[self.TAB_NODES]['cmd'] = self.cmdNodesBack
+ self.TABLES[self.TAB_RULES]['cmd'] = self.cmdRulesBack
+ self.TABLES[self.TAB_HOSTS]['cmd'] = self.cmdHostsBack
+ self.TABLES[self.TAB_PROCS]['cmd'] = self.cmdProcsBack
+ self.TABLES[self.TAB_ADDRS]['cmd'] = self.cmdAddrsBack
+ self.TABLES[self.TAB_PORTS]['cmd'] = self.cmdPortsBack
+ self.TABLES[self.TAB_USERS]['cmd'] = self.cmdUsersBack
+
+ self.TABLES[self.TAB_MAIN]['cmdCleanStats'] = self.cmdCleanSql
+ self.TABLES[self.TAB_NODES]['cmdCleanStats'] = self.cmdCleanSql
+ self.TABLES[self.TAB_RULES]['cmdCleanStats'] = self.cmdCleanSql
+ self.TABLES[self.TAB_HOSTS]['cmdCleanStats'] = self.cmdCleanSql
+ self.TABLES[self.TAB_PROCS]['cmdCleanStats'] = self.cmdCleanSql
+ self.TABLES[self.TAB_ADDRS]['cmdCleanStats'] = self.cmdCleanSql
+ self.TABLES[self.TAB_PORTS]['cmdCleanStats'] = self.cmdCleanSql
+ self.TABLES[self.TAB_USERS]['cmdCleanStats'] = self.cmdCleanSql
+ # the rules clean button is only for a particular rule, not all.
+ self.TABLES[self.TAB_RULES]['cmdCleanStats'].setVisible(False)
+ self.TABLES[self.TAB_NODES]['cmdCleanStats'].setVisible(False)
+ self.TABLES[self.TAB_MAIN]['cmdCleanStats'].clicked.connect(lambda: self._cb_clean_sql_clicked(self.TAB_MAIN))
+
+ self.TABLES[self.TAB_MAIN]['filterLine'] = self.filterLine
+ self.TABLES[self.TAB_MAIN]['view'].doubleClicked.connect(self._cb_main_table_double_clicked)
+ self.TABLES[self.TAB_MAIN]['view'].installEventFilter(self)
+ self.TABLES[self.TAB_MAIN]['filterLine'].textChanged.connect(self._cb_events_filter_line_changed)
+
+ self.TABLES[self.TAB_RULES]['view'].setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+ self.TABLES[self.TAB_RULES]['view'].customContextMenuRequested.connect(self._cb_table_context_menu)
+ for idx in range(1,8):
+ self.TABLES[idx]['cmd'].hide()
+ self.TABLES[idx]['cmd'].setVisible(False)
+ self.TABLES[idx]['cmd'].clicked.connect(lambda: self._cb_cmd_back_clicked(idx))
+ if self.TABLES[idx]['cmdCleanStats'] != None:
+ self.TABLES[idx]['cmdCleanStats'].clicked.connect(lambda: self._cb_clean_sql_clicked(idx))
+ self.TABLES[idx]['label'].setStyleSheet('color: blue; font-size:9pt; font-weight:600;')
+ self.TABLES[idx]['label'].setVisible(False)
+ self.TABLES[idx]['view'].doubleClicked.connect(self._cb_table_double_clicked)
+ self.TABLES[idx]['view'].selectionModel().selectionChanged.connect(self._cb_table_selection_changed)
+ self.TABLES[idx]['view'].installEventFilter(self)
+
+ self._load_settings()
+
+ self._tables = ( \
+ self.TABLES[self.TAB_MAIN]['view'],
+ self.TABLES[self.TAB_NODES]['view'],
+ self.TABLES[self.TAB_RULES]['view'],
+ self.TABLES[self.TAB_HOSTS]['view'],
+ self.TABLES[self.TAB_PROCS]['view'],
+ self.TABLES[self.TAB_ADDRS]['view'],
+ self.TABLES[self.TAB_PORTS]['view'],
+ self.TABLES[self.TAB_USERS]['view']
+ )
+ self._file_names = ( \
+ 'events.csv',
+ 'nodes.csv',
+ 'rules.csv',
+ 'hosts.csv',
+ 'procs.csv',
+ 'addrs.csv',
+ 'ports.csv',
+ 'users.csv'
+ )
+
+ self.iconStart = QtGui.QIcon().fromTheme("media-playback-start")
+ self.iconPause = QtGui.QIcon().fromTheme("media-playback-pause")
+
+ if QtGui.QIcon.hasThemeIcon("document-new") == False:
+ self._configure_buttons_icons()
+
+ #Sometimes a maximized window which had been minimized earlier won't unminimize
+ #To workaround, we explicitely maximize such windows when unminimizing happens
+ def changeEvent(self, event):
+ if event.type() == QtCore.QEvent.WindowStateChange:
+ if event.oldState() & QtCore.Qt.WindowMinimized and event.oldState() & QtCore.Qt.WindowMaximized:
+ #a previously minimized maximized window ...
+ if self.windowState() ^ QtCore.Qt.WindowMinimized and self._current_desktop == "KDE":
+ # is not minimized anymore, i.e. it was unminimized
+ # docs: https://doc.qt.io/qt-5/qwidget.html#setWindowState
+ self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
+
+ def showEvent(self, event):
+ super(StatsDialog, self).showEvent(event)
+ self._shown_trigger.emit()
+ window_title = QC.translate("stats", "OpenSnitch Network Statistics {0}").format(version)
+ if self._address is not None:
+ window_title = QC.translate("stats", "OpenSnitch Network Statistics for {0}").format(self._address)
+ self.nodeLabel.setText(self._address)
+ self._load_settings()
+ self._add_rulesTree_nodes()
+ self.setWindowTitle(window_title)
+ self._refresh_active_table()
+
+ def eventFilter(self, source, event):
+ if event.type() == QtCore.QEvent.KeyPress:
+ if event.matches(QtGui.QKeySequence.Copy):
+ self._copy_selected_rows()
+ return True
+ elif event.key() == QtCore.Qt.Key_Delete:
+ table = self._get_active_table()
+ selection = table.selectionModel().selectedRows()
+ if selection:
+ model = table.model()
+ self._table_menu_delete(2, model, selection)
+ # we need to manually refresh the model
+ table.selectionModel().clear()
+ self._refresh_active_table()
+ return True
+ return super(StatsDialog, self).eventFilter(source, event)
+
+ def _configure_buttons_icons(self):
+ self.iconStart = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPlay"))
+ self.iconPause = self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_MediaPause"))
+
+ self.newRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileIcon")))
+ self.delRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_TrashIcon")))
+ self.editRuleButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogDetailedView")))
+ self.saveButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogSaveButton")))
+ self.prefsButton.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogDetailedView")))
+ self.startButton.setIcon(self.iconStart)
+ self.cmdProcDetails.setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_FileDialogContentsView")))
+ self.TABLES[self.TAB_MAIN]['cmdCleanStats'].setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogResetButton")))
+ for idx in range(1,8):
+ self.TABLES[idx]['cmd'].setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_ArrowLeft")))
+ if self.TABLES[idx]['cmdCleanStats'] != None:
+ self.TABLES[idx]['cmdCleanStats'].setIcon(self.style().standardIcon(getattr(QtWidgets.QStyle, "SP_DialogResetButton")))
+
+ def _load_settings(self):
+ dialog_geometry = self._cfg.getSettings(Config.STATS_GEOMETRY)
+ dialog_last_tab = self._cfg.getSettings(Config.STATS_LAST_TAB)
+ dialog_general_filter_text = self._cfg.getSettings(Config.STATS_FILTER_TEXT)
+ dialog_general_filter_action = self._cfg.getSettings(Config.STATS_FILTER_ACTION)
+ dialog_general_limit_results = self._cfg.getSettings(Config.STATS_LIMIT_RESULTS)
+ if dialog_geometry != None:
+ self.restoreGeometry(dialog_geometry)
+ if dialog_last_tab != None:
+ self.tabWidget.setCurrentIndex(int(dialog_last_tab))
+ if dialog_general_filter_text != None:
+ # prevent from firing textChanged signal
+ self.filterLine.blockSignals(True);
+ self.filterLine.setText(dialog_general_filter_text)
+ self.filterLine.blockSignals(False);
+ if dialog_general_filter_action != None:
+ self.comboAction.setCurrentIndex(int(dialog_general_filter_action))
+ if dialog_general_limit_results != None:
+ # XXX: a little hack, because if the saved index is 0, the signal is not fired.
+ # XXX: this causes to fire the event twice
+ self.limitCombo.blockSignals(True);
+ self.limitCombo.setCurrentIndex(4)
+ self.limitCombo.setCurrentIndex(int(dialog_general_limit_results))
+ self.limitCombo.blockSignals(False);
+
+ rules_splitter_pos = self._cfg.getSettings(Config.STATS_RULES_SPLITTER_POS)
+ if type(rules_splitter_pos) == QtCore.QByteArray:
+ self.rulesSplitter.restoreState(rules_splitter_pos)
+ rulesSizes = self.rulesSplitter.sizes()
+ if self.IN_DETAIL_VIEW[self.TAB_RULES] == True:
+ self.comboRulesFilter.setVisible(False)
+ elif len(rulesSizes) > 0:
+ self.comboRulesFilter.setVisible(rulesSizes[0] == 0)
+ else:
+ w = self.rulesSplitter.width()
+ self.rulesSplitter.setSizes([int(w/4), int(w/2)])
+
+ self._restore_details_view_columns(self.eventsTable.horizontalHeader(), Config.STATS_GENERAL_COL_STATE)
+ self._restore_details_view_columns(self.nodesTable.horizontalHeader(), Config.STATS_NODES_COL_STATE)
+ self._restore_details_view_columns(self.rulesTable.horizontalHeader(), Config.STATS_RULES_COL_STATE)
+
+ rulesTreeNodes_expanded = self._cfg.getBool(Config.STATS_RULES_TREE_EXPANDED_1)
+ if rulesTreeNodes_expanded != None:
+ rules_tree_nodes = self._get_rulesTree_item(self.RULES_TREE_NODES)
+ if rules_tree_nodes != None:
+ rules_tree_nodes.setExpanded(rulesTreeNodes_expanded)
+ rulesTreeApps_expanded = self._cfg.getBool(Config.STATS_RULES_TREE_EXPANDED_0)
+ if rulesTreeApps_expanded != None:
+ rules_tree_apps = self._get_rulesTree_item(self.RULES_TREE_APPS)
+ if rules_tree_apps != None:
+ rules_tree_apps.setExpanded(rulesTreeApps_expanded)
+
+
+ def _save_settings(self):
+ self._cfg.setSettings(Config.STATS_GEOMETRY, self.saveGeometry())
+ self._cfg.setSettings(Config.STATS_LAST_TAB, self.tabWidget.currentIndex())
+ self._cfg.setSettings(Config.STATS_LIMIT_RESULTS, self.limitCombo.currentIndex())
+ self._cfg.setSettings(Config.STATS_FILTER_TEXT, self.filterLine.text())
+
+ header = self.eventsTable.horizontalHeader()
+ self._cfg.setSettings(Config.STATS_GENERAL_COL_STATE, header.saveState())
+ nodesHeader = self.nodesTable.horizontalHeader()
+ self._cfg.setSettings(Config.STATS_NODES_COL_STATE, nodesHeader.saveState())
+ rulesHeader = self.rulesTable.horizontalHeader()
+ self._cfg.setSettings(Config.STATS_RULES_COL_STATE, rulesHeader.saveState())
+
+ rules_tree_apps = self._get_rulesTree_item(self.RULES_TREE_APPS)
+ if rules_tree_apps != None:
+ self._cfg.setSettings(Config.STATS_RULES_TREE_EXPANDED_0, rules_tree_apps.isExpanded())
+ rules_tree_nodes = self._get_rulesTree_item(self.RULES_TREE_NODES)
+ if rules_tree_nodes != None:
+ self._cfg.setSettings(Config.STATS_RULES_TREE_EXPANDED_1, rules_tree_nodes.isExpanded())
+
+
+ def _del_rule(self, rule_name, node_addr):
+ nid, noti = self._nodes.delete_rule(rule_name, node_addr, self._notification_callback)
+ self._notifications_sent[nid] = noti
+
+ # https://stackoverflow.com/questions/40225270/copy-paste-multiple-items-from-qtableview-in-pyqt4
+ def _copy_selected_rows(self):
+ cur_idx = self.tabWidget.currentIndex()
+ selection = self.TABLES[cur_idx]['view'].selectedIndexes()
+ if selection:
+ rows = sorted(index.row() for index in selection)
+ columns = sorted(index.column() for index in selection)
+ rowcount = rows[-1] - rows[0] + 1
+ colcount = columns[-1] - columns[0] + 1
+ table = [[''] * colcount for _ in range(rowcount)]
+ for index in selection:
+ row = index.row() - rows[0]
+ column = index.column() - columns[0]
+ table[row][column] = index.data()
+ stream = io.StringIO()
+ csv.writer(stream, delimiter=',').writerows(table)
+ QtWidgets.qApp.clipboard().setText(stream.getvalue())
+
+
+ def _configure_rules_contextual_menu(self, pos):
+ try:
+ cur_idx = self.tabWidget.currentIndex()
+ table = self._get_active_table()
+ model = table.model()
+
+ selection = table.selectionModel().selectedRows()
+ if not selection:
+ return
+
+ menu = QtWidgets.QMenu()
+ durMenu = QtWidgets.QMenu(self.COL_STR_DURATION)
+ actionMenu = QtWidgets.QMenu(self.COL_STR_ACTION)
+ nodesMenu = QtWidgets.QMenu(QC.translate("stats", "Apply to"))
+
+ nodes_menu = []
+ if self._nodes.count() > 0:
+ for node in self._nodes.get_nodes():
+ nodes_menu.append([nodesMenu.addAction(node), node])
+ menu.addMenu(nodesMenu)
+
+ _actAllow = actionMenu.addAction(QC.translate("stats", "Allow"))
+ _actDeny = actionMenu.addAction(QC.translate("stats", "Deny"))
+ _actReject = actionMenu.addAction(QC.translate("stats", "Reject"))
+ menu.addMenu(actionMenu)
+
+ _durAlways = durMenu.addAction(QC.translate("stats", "Always"))
+ _durUntilReboot = durMenu.addAction(QC.translate("stats", "Until reboot"))
+ _dur1h = durMenu.addAction(Config.DURATION_1h)
+ _dur30m = durMenu.addAction(Config.DURATION_30m)
+ _dur15m = durMenu.addAction(Config.DURATION_15m)
+ _dur5m = durMenu.addAction(Config.DURATION_5m)
+ menu.addMenu(durMenu)
+
+ is_rule_enabled = model.index(selection[0].row(), self.COL_R_ENABLED).data()
+ menu_label_enable = QC.translate("stats", "Disable")
+ if is_rule_enabled == "False":
+ menu_label_enable = QC.translate("stats", "Enable")
+
+ _menu_enable = menu.addAction(QC.translate("stats", menu_label_enable))
+ _menu_duplicate = menu.addAction(QC.translate("stats", "Duplicate"))
+ _menu_edit = menu.addAction(QC.translate("stats", "Edit"))
+ _menu_delete = menu.addAction(QC.translate("stats", "Delete"))
+
+ # move away menu a few pixels to the right, to avoid clicking on it by mistake
+ point = QtCore.QPoint(pos.x()+10, pos.y()+5)
+ action = menu.exec_(table.mapToGlobal(point))
+
+ model = table.model()
+
+ if self._nodes.count() > 0:
+ for nmenu in nodes_menu:
+ node_action = nmenu[0]
+ node_addr = nmenu[1]
+ if action == node_action:
+ ret = Message.yes_no(
+ QC.translate("stats", " Apply this rule to {0} ".format(node_addr)),
+ QC.translate("stats", " Are you sure?"),
+ QtWidgets.QMessageBox.Warning)
+ if ret == QtWidgets.QMessageBox.Cancel:
+ return False
+ self._table_menu_apply_to_node(cur_idx, model, selection, node_addr)
+ return
+
+ if action == _menu_delete:
+ self._table_menu_delete(cur_idx, model, selection)
+ elif action == _menu_edit:
+ self._table_menu_edit(cur_idx, model, selection)
+ elif action == _menu_enable:
+ self._table_menu_enable(cur_idx, model, selection, is_rule_enabled)
+ elif action == _menu_duplicate:
+ self._table_menu_duplicate(cur_idx, model, selection)
+ elif action == _durAlways:
+ self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_ALWAYS)
+ elif action == _dur1h:
+ self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_1h)
+ elif action == _dur30m:
+ self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_30m)
+ elif action == _dur15m:
+ self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_15m)
+ elif action == _dur5m:
+ self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_5m)
+ elif action == _durUntilReboot:
+ self._table_menu_change_rule_field(cur_idx, model, selection, "duration", Config.DURATION_UNTIL_RESTART)
+ elif action == _actAllow:
+ self._table_menu_change_rule_field(cur_idx, model, selection, "action", Config.ACTION_ALLOW)
+ elif action == _actDeny:
+ self._table_menu_change_rule_field(cur_idx, model, selection, "action", Config.ACTION_DENY)
+ elif action == _actReject:
+ self._table_menu_change_rule_field(cur_idx, model, selection, "action", Config.ACTION_REJECT)
+
+ except Exception as e:
+ print(e)
+ finally:
+ self._clear_rows_selection()
+ return True
+
+ def _table_menu_duplicate(self, cur_idx, model, selection):
+
+ for idx in selection:
+ rule_name = model.index(idx.row(), self.COL_R_NAME).data()
+ node_addr = model.index(idx.row(), self.COL_R_NODE).data()
+
+ records = None
+ for idx in range(0,100):
+ records = self._get_rule(rule_name, node_addr)
+ if records == None or records.size() == -1:
+ rule = self._rules_dialog.get_rule_from_records(records)
+ rule.name = "cloned-{0}-{1}".format(idx, rule.name)
+ self._db.insert_rule(rule, node_addr)
+ break
+
+ if records != None and records.size() == -1:
+ noti = ui_pb2.Notification(type=ui_pb2.CHANGE_RULE, rules=[rule])
+ nid = self._nodes.send_notification(node_addr, noti, self._notification_callback)
+ if nid != None:
+ self._notifications_sent[nid] = noti
+
+ def _table_menu_apply_to_node(self, cur_idx, model, selection, node_addr):
+
+ for idx in selection:
+ rule_name = model.index(idx.row(), self.COL_R_NAME).data()
+ records = self._get_rule(rule_name, None)
+ rule = self._rules_dialog.get_rule_from_records(records)
+
+ noti = ui_pb2.Notification(type=ui_pb2.CHANGE_RULE, rules=[rule])
+ nid = self._nodes.send_notification(node_addr, noti, self._notification_callback)
+ if nid != None:
+ self._db.insert_rule(rule, node_addr)
+ self._notifications_sent[nid] = noti
+
+ def _table_menu_change_rule_field(self, cur_idx, model, selection, field, value):
+ for idx in selection:
+ rule_name = model.index(idx.row(), self.COL_R_NAME).data()
+ node_addr = model.index(idx.row(), self.COL_R_NODE).data()
+
+ records = self._get_rule(rule_name, node_addr)
+ rule = self._rules_dialog.get_rule_from_records(records)
+
+ self._db.update(table="rules", fields="{0}=?".format(field),
+ values=[value], condition="name='{0}' AND node='{1}'".format(rule_name, node_addr),
+ action_on_conflict="")
+
+ if field == "action":
+ rule.action = value
+ elif field == "duration":
+ rule.duration = value
+ elif field == "precedence":
+ rule.precedence = value
+
+ noti = ui_pb2.Notification(type=ui_pb2.CHANGE_RULE, rules=[rule])
+ nid = self._nodes.send_notification(node_addr, noti, self._notification_callback)
+ if nid != None:
+ self._notifications_sent[nid] = noti
+
+ def _table_menu_enable(self, cur_idx, model, selection, is_rule_enabled):
+ rule_status = "False" if is_rule_enabled == "True" else "True"
+
+ for idx in selection:
+ rule_name = model.index(idx.row(), self.COL_R_NAME).data()
+ node_addr = model.index(idx.row(), self.COL_R_NODE).data()
+
+ records = self._get_rule(rule_name, node_addr)
+ rule = self._rules_dialog.get_rule_from_records(records)
+ rule_type = ui_pb2.DISABLE_RULE if is_rule_enabled == "True" else ui_pb2.ENABLE_RULE
+
+ self._db.update(table="rules", fields="enabled=?",
+ values=[rule_status], condition="name='{0}' AND node='{1}'".format(rule_name, node_addr),
+ action_on_conflict="")
+
+ noti = ui_pb2.Notification(type=rule_type, rules=[rule])
+ nid = self._nodes.send_notification(node_addr, noti, self._notification_callback)
+ if nid != None:
+ self._notifications_sent[nid] = noti
+
+ def _table_menu_delete(self, cur_idx, model, selection):
+ ret = Message.yes_no(
+ QC.translate("stats", " Your are about to delete this rule. "),
+ QC.translate("stats", " Are you sure?"),
+ QtWidgets.QMessageBox.Warning)
+ if ret == QtWidgets.QMessageBox.Cancel:
+ return False
+
+ for idx in selection:
+ name = model.index(idx.row(), self.COL_R_NAME).data()
+ node = model.index(idx.row(), self.COL_R_NODE).data()
+ self._del_rule(name, node)
+
+ def _table_menu_edit(self, cur_idx, model, selection):
+
+ for idx in selection:
+ name = model.index(idx.row(), self.COL_R_NAME).data()
+ node = model.index(idx.row(), self.COL_R_NODE).data()
+ records = self._get_rule(name, node)
+ if records == None or records == -1:
+ Message.ok("Rule error",
+ QC.translate("stats", "Rule not found by that name and node"),
+ QtWidgets.QMessageBox.Warning)
+ return
+ self._rules_dialog.edit_rule(records, node)
+ break
+
+ # ignore updates while the user is using the scrollbar.
+ def _cb_scrollbar_pressed(self):
+ self.scrollbar_active = True
+
+ def _cb_scrollbar_released(self):
+ self.scrollbar_active = False
+
+ def _cb_proc_details_clicked(self):
+ table = self._tables[self.tabWidget.currentIndex()]
+ nrows = table.model().rowCount()
+ pids = {}
+ for row in range(0, nrows):
+ pid = table.model().index(row, self.COL_PID).data()
+ node = table.model().index(row, self.COL_NODE).data()
+ if pid not in pids:
+ pids[pid] = node
+
+ self._proc_details_dialog.monitor(pids)
+
+ @QtCore.pyqtSlot(ui_pb2.NotificationReply)
+ def _cb_notification_callback(self, reply):
+ if reply.id in self._notifications_sent:
+ if reply.code == ui_pb2.ERROR:
+ Message.ok(
+ QC.translate("stats",
+ "<b>Error:</b><br><br>",
+ "{0}").format(reply.data),
+ QtWidgets.QMessageBox.Warning)
+
+ else:
+ Message.ok(
+ QC.translate("stats", "Warning:"),
+ "{0}".format(reply.data),
+ QtWidgets.QMessageBox.Warning)
+
+ def _cb_tab_changed(self, index):
+ self.comboAction.setVisible(index == self.TAB_MAIN)
+
+ self.TABLES[index]['cmdCleanStats'].setVisible(True)
+ if index == self.TAB_MAIN:
+ self._set_events_query()
+ else:
+ if index == self.TAB_RULES:
+ # display the clean buton only if not in detail view
+ self.TABLES[index]['cmdCleanStats'].setVisible( self.IN_DETAIL_VIEW[index] )
+ self._add_rulesTree_nodes()
+
+ elif index == self.TAB_PROCS:
+ # make the button visible depending if we're in the detail view
+ nrows = self._get_active_table().model().rowCount()
+ self.cmdProcDetails.setVisible(self.IN_DETAIL_VIEW[index] and nrows > 0)
+ elif index == self.TAB_NODES:
+ self.TABLES[index]['cmdCleanStats'].setVisible( self.IN_DETAIL_VIEW[index] )
+
+ self._refresh_active_table()
+
+ def _cb_table_context_menu(self, pos):
+ cur_idx = self.tabWidget.currentIndex()
+ if cur_idx != self.TAB_RULES or self.IN_DETAIL_VIEW[self.TAB_RULES] == True:
+ # the only table with context menu for now is the main rules table
+ return
+
+ self._context_menu_active = True
+ refresh_table = self._configure_rules_contextual_menu(pos)
+ self._context_menu_active = False
+ if refresh_table:
+ self._refresh_active_table()
+
+
+ def _cb_table_header_clicked(self, pos, sortIdx):
+ cur_idx = self.tabWidget.currentIndex()
+ # TODO: allow ordering by Network column
+ if cur_idx == self.TAB_ADDRS and pos == 2:
+ return
+
+ model = self._get_active_table().model()
+ qstr = model.query().lastQuery().split("ORDER BY")[0]
+
+ q = qstr.strip(" ") + " ORDER BY %d %s" % (pos+1, self.SORT_ORDER[sortIdx])
+ if cur_idx > 0 and self.TABLES[cur_idx]['cmd'].isVisible() == False:
+ self.TABLES[cur_idx]['last_order_by'] = pos+1
+ self.TABLES[cur_idx]['last_order_to'] = sortIdx
+
+ q = qstr.strip(" ") + self._get_order()
+
+ q += self._get_limit()
+ self.setQuery(model, q)
+
+ def _cb_events_filter_line_changed(self, text):
+ cur_idx = self.tabWidget.currentIndex()
+
+ model = self.TABLES[cur_idx]['view'].model()
+ qstr = None
+ if cur_idx == StatsDialog.TAB_MAIN:
+ self._cfg.setSettings(Config.STATS_FILTER_TEXT, text)
+ self._set_events_query()
+ return
+ elif cur_idx == StatsDialog.TAB_NODES:
+ qstr = self._get_nodes_filter_query(model.query().lastQuery(), text)
+ elif self.IN_DETAIL_VIEW[cur_idx] == True:
+ qstr = self._get_indetail_filter_query(model.query().lastQuery(), text)
+ else:
+ where_clause = self._get_filter_line_clause(cur_idx, text)
+ qstr = self._db.get_query( self.TABLES[cur_idx]['name'], self.TABLES[cur_idx]['display_fields'] ) + \
+ where_clause + self._get_order()
+ if text == "":
+ qstr = qstr + self._get_limit()
+
+ if qstr != None:
+ self.setQuery(model, qstr)
+
+ def _cb_limit_combo_changed(self, idx):
+ if self.tabWidget.currentIndex() == self.TAB_MAIN:
+ self._set_events_query()
+ else:
+ model = self._get_active_table().model()
+ qstr = model.query().lastQuery()
+ if "LIMIT" in qstr:
+ qs = qstr.split(" LIMIT ")
+ q = qs[0]
+ l = qs[1]
+ qstr = q + self._get_limit()
+ else:
+ qstr = qstr + self._get_limit()
+ self.setQuery(model, qstr)
+
+ def _cb_combo_action_changed(self, idx):
+ if self.tabWidget.currentIndex() != self.TAB_MAIN:
+ return
+
+ self._cfg.setSettings(Config.STATS_GENERAL_FILTER_ACTION, idx)
+ self._set_events_query()
+
+ def _cb_clean_sql_clicked(self, idx):
+ cur_idx = self.tabWidget.currentIndex()
+ if self.tabWidget.currentIndex() == StatsDialog.TAB_RULES:
+ self._db.empty_rule(self.TABLES[cur_idx]['label'].text())
+ elif self.IN_DETAIL_VIEW[cur_idx]:
+ model = self._get_active_table().model()
+ # get left side of the query: * GROUP BY ...
+ qstr = model.query().lastQuery().split("GROUP BY")[0]
+ # get right side of the query: ... WHERE *
+ q = qstr.split("WHERE")
+
+ table = self.TABLES[cur_idx]['name']
+ label = self.TABLES[cur_idx]['label'].text()
+
+ field = "dst_host"
+ if cur_idx == self.TAB_NODES:
+ field = "node"
+ if label[0] == '/':
+ label = "unix:{0}".format(label)
+ elif cur_idx == self.TAB_PROCS:
+ field = "process"
+ elif cur_idx == self.TAB_ADDRS:
+ field = "dst_ip"
+ elif cur_idx == self.TAB_PORTS:
+ field = "dst_port"
+ elif cur_idx == self.TAB_USERS:
+ field = "uid"
+
+ self._db.remove("DELETE FROM {0} WHERE what = '{1}'".format(table, label))
+ self._db.remove("DELETE FROM connections WHERE {0} = '{1}'".format(field, label))
+ else:
+ self._db.clean(self.TABLES[cur_idx]['name'])
+ self._refresh_active_table()
+
+ def _cb_cmd_back_clicked(self, idx):
+ try:
+ cur_idx = self.tabWidget.currentIndex()
+ self._clear_rows_selection()
+ self.IN_DETAIL_VIEW[cur_idx] = False
+
+ self._set_active_widgets(False)
+ if cur_idx == StatsDialog.TAB_RULES:
+ self._restore_rules_tab_widgets(True)
+ return
+ elif cur_idx == StatsDialog.TAB_PROCS:
+ self.cmdProcDetails.setVisible(False)
+
+ model = self._get_active_table().model()
+ where_clause = ""
+ if self.TABLES[cur_idx]['filterLine'] != None:
+ filter_text = self.TABLES[cur_idx]['filterLine'].text()
+ where_clause = self._get_filter_line_clause(cur_idx, filter_text)
+
+ self.setQuery(model,
+ self._db.get_query(
+ self.TABLES[cur_idx]['name'],
+ self.TABLES[cur_idx]['display_fields']) + where_clause + " " + self._get_order() + self._get_limit()
+ )
+ finally:
+ self._restore_details_view_columns(
+ self.TABLES[cur_idx]['view'].horizontalHeader(),
+ "{0}{1}".format(Config.STATS_VIEW_COL_STATE, cur_idx)
+ )
+ self._restore_scroll_value()
+ self._restore_last_selected_row()
+
+ def _cb_main_table_double_clicked(self, row):
+ data = row.data()
+ idx = row.column()
+ cur_idx = 1
+
+ if idx == StatsDialog.COL_NODE:
+ cur_idx = self.TAB_NODES
+ self.IN_DETAIL_VIEW[cur_idx] = True
+ self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_NODE).data()
+ self.tabWidget.setCurrentIndex(cur_idx)
+ self._set_active_widgets(True, str(data))
+ p, addr = self._nodes.get_addr(data)
+ self._set_nodes_query(addr)
+
+ elif idx == StatsDialog.COL_PROCS:
+ cur_idx = self.TAB_PROCS
+ self.IN_DETAIL_VIEW[cur_idx] = True
+ self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_PROCS).data()
+ self.tabWidget.setCurrentIndex(cur_idx)
+ self._set_active_widgets(True, str(data))
+ self._set_process_query(data)
+
+ elif idx == StatsDialog.COL_RULES:
+ cur_idx = self.TAB_RULES
+ self.IN_DETAIL_VIEW[cur_idx] = True
+ self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_RULES).data()
+ r_name, node = self._set_rules_tab_active(row, cur_idx, self.COL_RULES, self.COL_NODE)
+ self._set_active_widgets(True, str(data))
+ self._set_rules_query(r_name, node)
+
+ else:
+ return
+
+ self._restore_details_view_columns(
+ self.TABLES[cur_idx]['view'].horizontalHeader(),
+ "{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx)
+ )
+
+ def _cb_table_double_clicked(self, row):
+ cur_idx = self.tabWidget.currentIndex()
+ if self.IN_DETAIL_VIEW[cur_idx]:
+ return
+ self.IN_DETAIL_VIEW[cur_idx] = True
+ self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_TIME).data()
+ self.LAST_SCROLL_VALUE = self.TABLES[cur_idx]['view'].vScrollBar.value()
+
+ data = row.data()
+
+ if cur_idx == self.TAB_RULES:
+ rule_name = row.model().index(row.row(), self.COL_R_NAME).data()
+ self._set_active_widgets(True, rule_name)
+ r_name, node = self._set_rules_tab_active(row, cur_idx, self.COL_R_NAME, self.COL_R_NODE)
+ self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_R_NAME).data()
+ self._set_rules_query(r_name, node)
+ self._restore_details_view_columns(
+ self.TABLES[cur_idx]['view'].horizontalHeader(),
+ "{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx)
+ )
+ return
+ if cur_idx == self.TAB_NODES:
+ data = row.model().index(row.row(), self.COL_NODE).data()
+ self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_NODE).data()
+ if cur_idx > self.TAB_RULES:
+ self.LAST_SELECTED_ITEM = row.model().index(row.row(), self.COL_WHAT).data()
+ data = row.model().index(row.row(), self.COL_WHAT).data()
+
+
+ self._set_active_widgets(True, str(data))
+
+ if cur_idx == StatsDialog.TAB_NODES:
+ self._set_nodes_query(data)
+ elif cur_idx == StatsDialog.TAB_HOSTS:
+ self._set_hosts_query(data)
+ elif cur_idx == StatsDialog.TAB_PROCS:
+ self._set_process_query(data)
+ elif cur_idx == StatsDialog.TAB_ADDRS:
+ lbl_text = self.TABLES[cur_idx]['label'].text()
+ if lbl_text != "":
+ asn = self.asndb.get_asn(lbl_text)
+ if asn != "":
+ lbl_text += " (" + asn + ")"
+ self.TABLES[cur_idx]['label'].setText(lbl_text)
+ self._set_addrs_query(data)
+ elif cur_idx == StatsDialog.TAB_PORTS:
+ self._set_ports_query(data)
+ elif cur_idx == StatsDialog.TAB_USERS:
+ self._set_users_query(data)
+
+ self._restore_details_view_columns(
+ self.TABLES[cur_idx]['view'].horizontalHeader(),
+ "{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx)
+ )
+
+ # selection changes occur before tableview's clicked event
+ # if there're no rows selected, accept the selection. Otherwise clean it.
+ def _cb_table_selection_changed(self, selected, deselected):
+ cur_idx = self.tabWidget.currentIndex()
+
+ # only update the flag (that updates data), if there's more than 1
+ # row selected. When using the keyboard to move around, 1 row will
+ # be selected to indicate where you are.
+
+ # NOTE: in some qt versions you can select a row and setQuery() won't
+ # reset the selection, but in others it gets resetted.
+ self.TABLES[cur_idx]['rows_selected'] = len(self.TABLES[cur_idx]['view'].selectionModel().selectedRows(0)) > 1
+
+ def _cb_prefs_clicked(self):
+ self._prefs_dialog.show()
+
+ def _cb_rules_filter_combo_changed(self, idx):
+ if idx == self.RULES_TREE_APPS:
+ self._set_rules_filter()
+ elif idx == self.RULES_COMBO_PERMANENT:
+ self._set_rules_filter(self.RULES_TREE_APPS, self.RULES_TREE_PERMANENT)
+ elif idx == self.RULES_COMBO_TEMPORARY:
+ self._set_rules_filter(self.RULES_TREE_APPS, self.RULES_TREE_TEMPORARY)
+
+ def _cb_rules_tree_item_clicked(self, item, col):
+ """
+ Event fired when the user clicks on the left panel of the rules tab
+ """
+ item_model = self.rulesTreePanel.indexFromItem(item, col)
+ parent = item.parent()
+ parent_row = -1
+ if parent != None:
+ parent_model = self.rulesTreePanel.indexFromItem(parent, col)
+ parent_row = parent_model.row()
+
+ self._set_rules_filter(parent_row, item_model.row(), item.text(0))
+
+ def _cb_rules_splitter_moved(self, pos, index):
+ self.comboRulesFilter.setVisible(pos == 0)
+ self._cfg.setSettings(Config.STATS_RULES_SPLITTER_POS, self.rulesSplitter.saveState())
+
+ def _cb_start_clicked(self):
+ if self.daemon_connected == False:
+ self.startButton.setChecked(False)
+ self.startButton.setIcon(self.iconStart)
+ return
+
+ self.update_interception_status(self.startButton.isChecked())
+ self._status_changed_trigger.emit(self.startButton.isChecked())
+
+ if self.startButton.isChecked():
+ nid, noti = self._nodes.start_interception(_callback=self._notification_callback)
+ else:
+ nid, noti = self._nodes.stop_interception(_callback=self._notification_callback)
+
+ self._notifications_sent[nid] = noti
+
+ def _cb_new_rule_clicked(self):
+ self._rules_dialog.new_rule()
+
+ def _cb_edit_rule_clicked(self):
+ cur_idx = self.tabWidget.currentIndex()
+ records = self._get_rule(self.TABLES[cur_idx]['label'].text(), self.nodeRuleLabel.text())
+ if records == None:
+ return
+
+ self._rules_dialog.edit_rule(records, self.nodeRuleLabel.text())
+
+ def _cb_del_rule_clicked(self):
+ ret = Message.yes_no(
+ QC.translate("stats", " You are about to delete this rule. "),
+ QC.translate("stats", " Are you sure?"),
+ QtWidgets.QMessageBox.Warning)
+ if ret == QtWidgets.QMessageBox.Cancel:
+ return
+
+ self._del_rule(self.TABLES[self.tabWidget.currentIndex()]['label'].text(), self.nodeRuleLabel.text())
+ self.TABLES[self.TAB_RULES]['cmd'].click()
+ self.nodeRuleLabel.setText("")
+ self._refresh_active_table()
+
+ def _cb_enable_rule_toggled(self, state):
+ rule = ui_pb2.Rule(name=self.TABLES[self.tabWidget.currentIndex()]['label'].text())
+ rule.enabled = False
+ rule.action = ""
+ rule.duration = ""
+ rule.operator.type = ""
+ rule.operator.operand = ""
+ rule.operator.data = ""
+
+ notType = ui_pb2.DISABLE_RULE
+ if state == True:
+ notType = ui_pb2.ENABLE_RULE
+ rule.enabled = state
+ noti = ui_pb2.Notification(type=notType, rules=[rule])
+ self._notification_trigger.emit(noti)
+
+ def _cb_prev_button_clicked(self):
+ model = self._get_active_table().model()
+ model.fetchMore()
+
+ def _cb_next_button_clicked(self):
+ model = self._get_active_table().model()
+ model.fetchMore()
+
+ def _cb_help_button_clicked(self):
+ QuickHelp.show(
+ QC.translate("stats",
+ "<p><b>Quick help</b></p>" \
+ "<p>- Use CTRL+c to copy selected rows.</p>" \
+ "<p>- Use Home,End,PgUp,PgDown,PgUp,Up or Down keys to navigate rows.</p>" \
+ "<p>- Use right click on a row to stop refreshing the view.</p>" \
+ "<p>- Selecting more than one row also stops refreshing the view.</p>"
+ "<p>- On the Events view, clicking on columns Node, Process or Rule<br>" \
+ "jumps to the view of the selected item.</p>" \
+ "<p>- On the rest of the views, double click on a row to get detailed<br>" \
+ " information.</p><br>" \
+ "<p>For more information visit the <a href=\"{0}\">wiki</a></p>" \
+ "<br>".format(Config.HELP_URL)
+ )
+ )
+
+ # must be called after setModel() or setQuery()
+ def _show_columns(self):
+ cols = self._cfg.getSettings(Config.STATS_SHOW_COLUMNS)
+ if cols == None:
+ return
+
+ for c in range(StatsDialog.GENERAL_COL_NUM):
+ self.eventsTable.setColumnHidden(c, str(c) not in cols)
+
+ def _update_status_label(self, running=False, text=FIREWALL_DISABLED):
+ self.statusLabel.setText("%12s" % text)
+ if running:
+ self.statusLabel.setStyleSheet('color: green; margin: 5px')
+ self.startButton.setIcon(self.iconPause)
+ else:
+ self.statusLabel.setStyleSheet('color: rgb(206, 92, 0); margin: 5px')
+ self.startButton.setIcon(self.iconStart)
+
+ def _get_rulesTree_item(self, index):
+ try:
+ return self.rulesTreePanel.topLevelItem(index)
+ except Exception:
+ return None
+
+ def _add_rulesTree_nodes(self):
+ if self._nodes.count() > 0:
+ nodesItem = self.rulesTreePanel.topLevelItem(self.RULES_TREE_NODES)
+ nodesItem.takeChildren()
+ for n in self._nodes.get_nodes():
+ nodesItem.addChild(QtWidgets.QTreeWidgetItem([n]))
+
+ def _clear_rows_selection(self):
+ cur_idx = self.tabWidget.currentIndex()
+ self.TABLES[cur_idx]['view'].selectionModel().reset()
+ self.TABLES[cur_idx]['rows_selected'] = False
+
+ def _are_rows_selected(self):
+ cur_idx = self.tabWidget.currentIndex()
+ return self.TABLES[cur_idx]['rows_selected']
+
+ def _get_rule(self, rule_name, node_name):
+ """
+ get rule records, given the name of the rule and the node
+ """
+ cur_idx = self.tabWidget.currentIndex()
+ records = self._db.get_rule(rule_name, node_name)
+ if records.next() == False:
+ print("[stats dialog] edit rule, no records: ", rule_name, node_name)
+ self.TABLES[cur_idx]['cmd'].click()
+ return None
+
+ return records
+
+ def _get_filter_line_clause(self, idx, text):
+ if text == "":
+ return ""
+
+
+ if idx == StatsDialog.TAB_RULES:
+ return " WHERE rules.name LIKE '%{0}%' ".format(text)
+ elif idx == StatsDialog.TAB_HOSTS or idx == StatsDialog.TAB_PROCS or \
+ idx == StatsDialog.TAB_ADDRS or idx == StatsDialog.TAB_PORTS:
+ return " WHERE what LIKE '%{0}%' ".format(text)
+
+ return ""
+
+ def _get_limit(self):
+ return " " + self.LIMITS[self.limitCombo.currentIndex()]
+
+ def _get_order(self, field=None):
+ cur_idx = self.tabWidget.currentIndex()
+ order_field = self.TABLES[cur_idx]['last_order_by']
+ if field != None:
+ order_field = field
+ return " ORDER BY %s %s" % (order_field, self.SORT_ORDER[self.TABLES[cur_idx]['last_order_to']])
+
+ def _refresh_active_table(self):
+ model = self._get_active_table().model()
+ lastQuery = model.query().lastQuery()
+ if "LIMIT" not in lastQuery:
+ lastQuery += self._get_limit()
+ self.setQuery(model, lastQuery)
+
+ def _get_active_table(self):
+ return self.TABLES[self.tabWidget.currentIndex()]['view']
+
+ def _set_active_widgets(self, state, label_txt=""):
+ cur_idx = self.tabWidget.currentIndex()
+ self._clear_rows_selection()
+ self.TABLES[cur_idx]['label'].setVisible(state)
+ self.TABLES[cur_idx]['label'].setText(label_txt)
+ self.TABLES[cur_idx]['cmd'].setVisible(state)
+
+ if self.TABLES[cur_idx]['filterLine'] != None:
+ self.TABLES[cur_idx]['filterLine'].setVisible(not state)
+
+ if self.TABLES[cur_idx].get('cmdCleanStats') != None:
+ if cur_idx == StatsDialog.TAB_RULES or cur_idx == StatsDialog.TAB_NODES:
+ self.TABLES[cur_idx]['cmdCleanStats'].setVisible(state)
+
+ header = self.TABLES[cur_idx]['view'].horizontalHeader()
+ if state == True:
+ # going to normal state
+ self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_COL_STATE, cur_idx), header.saveState())
+ else:
+ # going to details state
+ self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx), header.saveState())
+
+ def _restore_last_selected_row(self):
+ cur_idx = self.tabWidget.currentIndex()
+ col = self.COL_TIME
+ if cur_idx == self.TAB_RULES:
+ col = self.TAB_RULES
+ elif cur_idx == self.TAB_NODES:
+ col = self.TAB_RULES
+
+ self.TABLES[cur_idx]['view'].selectItem(self.LAST_SELECTED_ITEM, col)
+ self.LAST_SELECTED_ITEM = ""
+
+ def _restore_scroll_value(self):
+ if self.LAST_SCROLL_VALUE != None:
+ cur_idx = self.tabWidget.currentIndex()
+ self.TABLES[cur_idx]['view'].vScrollBar.setValue(self.LAST_SCROLL_VALUE)
+ self.LAST_SCROLL_VALUE = None
+
+ def _restore_details_view_columns(self, header, settings_key):
+ header.blockSignals(True);
+
+ col_state = self._cfg.getSettings(settings_key)
+ if type(col_state) == QtCore.QByteArray:
+ header.restoreState(col_state)
+
+ header.blockSignals(False);
+
+ def _restore_rules_tab_widgets(self, active):
+ self.delRuleButton.setVisible(not active)
+ self.editRuleButton.setVisible(not active)
+ self.nodeRuleLabel.setText("")
+ self.rulesTreePanel.setVisible(active)
+
+ if active:
+ self.rulesSplitter.refresh()
+ self.comboRulesFilter.setVisible(self.rulesTreePanel.width() == 0)
+
+ items = self.rulesTreePanel.selectedItems()
+ if len(items) == 0:
+ self._set_rules_filter()
+ return
+
+ item_m = self.rulesTreePanel.indexFromItem(items[0], 0)
+ parent = item_m.parent()
+ if parent != None:
+ self._set_rules_filter(parent.row(), item_m.row(), item_m.data())
+
+ def _set_rules_tab_active(self, row, cur_idx, name_idx, node_idx):
+ data = row.data()
+ self._restore_rules_tab_widgets(False)
+
+ self.comboRulesFilter.setVisible(False)
+
+ r_name = row.model().index(row.row(), name_idx).data()
+ node = row.model().index(row.row(), node_idx).data()
+ self.nodeRuleLabel.setText(node)
+ self.tabWidget.setCurrentIndex(cur_idx)
+
+ return r_name, node
+
+ def _set_events_query(self):
+ if self.tabWidget.currentIndex() != self.TAB_MAIN:
+ return
+
+ model = self.TABLES[self.TAB_MAIN]['view'].model()
+ qstr = self._db.get_query(self.TABLES[self.TAB_MAIN]['name'], self.TABLES[self.TAB_MAIN]['display_fields'])
+
+ filter_text = self.filterLine.text()
+ action = ""
+ if self.comboAction.currentIndex() == 1:
+ action = "Action = \"{0}\"".format(Config.ACTION_ALLOW)
+ elif self.comboAction.currentIndex() == 2:
+ action = "Action = \"{0}\"".format(Config.ACTION_DENY)
+ elif self.comboAction.currentIndex() == 3:
+ action = "Action = \"{0}\"".format(Config.ACTION_REJECT)
+
+ # FIXME: use prepared statements
+ if filter_text == "":
+ if action != "":
+ qstr += " WHERE " + action
+ else:
+ if action != "":
+ action += " AND "
+ qstr += " WHERE " + action + " ("\
+ " Process LIKE '%" + filter_text + "%'" \
+ " OR Destination LIKE '%" + filter_text + "%'" \
+ " OR Rule LIKE '%" + filter_text + "%'" \
+ " OR Node LIKE '%" + filter_text + "%'" \
+ " OR Time LIKE '%" + filter_text + "%'" \
+ " OR Protocol LIKE '%" + filter_text + "%')" \
+
+ qstr += self._get_order() + self._get_limit()
+ self.setQuery(model, qstr)
+
+ def _set_nodes_query(self, data):
+
+ s = "AND c.src_ip='%s'" % data if '/' not in data else ''
+ model = self._get_active_table().model()
+ self.setQuery(model, "SELECT " \
+ "MAX(c.time) as {0}, " \
+ "c.action as {1}, " \
+ "count(c.process) as {2}, " \
+ "c.uid as {3}, " \
+ "c.protocol as {4}, " \
+ "c.dst_ip as {5}, " \
+ "c.dst_host as {6}, " \
+ "c.dst_port as {7}, " \
+ "c.process || ' (' || c.pid || ')' as {8}, " \
+ "c.process_args as {9}, " \
+ "c.process_cwd as CWD, " \
+ "c.rule as {10} " \
+ "FROM connections as c " \
+ "WHERE c.node LIKE '%{11}%' {12} GROUP BY {13}, c.process_args, c.uid, c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.protocol {14}".format(
+ self.COL_STR_TIME,
+ self.COL_STR_ACTION,
+ self.COL_STR_HITS,
+ self.COL_STR_UID,
+ self.COL_STR_PROTOCOL,
+ self.COL_STR_DST_IP,
+ self.COL_STR_DST_HOST,
+ self.COL_STR_DST_PORT,
+ self.COL_STR_PROCESS,
+ self.COL_STR_PROC_ARGS,
+ self.COL_STR_RULE,
+ data, s,
+ self.COL_STR_PROCESS,
+ self._get_order() + self._get_limit()))
+
+ def _get_nodes_filter_query(self, lastQuery, text):
+ base_query = lastQuery.split("GROUP BY")
+ qstr = base_query[0]
+ if "AND" in qstr:
+ # strip out ANDs if any
+ os = qstr.split('AND')
+ qstr = os[0]
+
+ if text != "":
+ qstr += "AND (c.time LIKE '%{0}%' OR " \
+ "c.action LIKE '%{0}%' OR " \
+ "c.pid LIKE '%{0}%' OR " \
+ "c.src_port LIKE '%{0}%' OR " \
+ "c.dst_port LIKE '%{0}%' OR " \
+ "c.src_ip LIKE '%{0}%' OR " \
+ "c.dst_ip LIKE '%{0}%' OR " \
+ "c.dst_host LIKE '%{0}%' OR " \
+ "c.process LIKE '%{0}%' OR " \
+ "c.process_args LIKE '%{0}%')".format(text)
+ if len(base_query) > 1:
+ qstr += " GROUP BY" + base_query[1]
+
+ return qstr
+
+ def _set_rules_filter(self, parent_row=-1, item_row=0, what=""):
+ section = self.FILTER_TREE_APPS
+
+ if parent_row == -1:
+ if item_row == self.RULES_TREE_NODES:
+ section=self.FILTER_TREE_NODES
+ what=""
+ else:
+ section=self.FILTER_TREE_APPS
+ what=""
+
+ elif parent_row == self.RULES_TREE_APPS:
+ if item_row == self.RULES_TREE_PERMANENT:
+ section=self.FILTER_TREE_APPS
+ what=self.RULES_TYPE_PERMANENT
+ elif item_row == self.RULES_TREE_TEMPORARY:
+ section=self.FILTER_TREE_APPS
+ what=self.RULES_TYPE_TEMPORARY
+
+ elif parent_row == self.RULES_TREE_NODES:
+ section=self.FILTER_TREE_NODES
+
+ if section == self.FILTER_TREE_APPS:
+ if what == self.RULES_TYPE_TEMPORARY:
+ what = "WHERE r.duration != '%s'" % Config.DURATION_ALWAYS
+ elif what == self.RULES_TYPE_PERMANENT:
+ what = "WHERE r.duration = '%s'" % Config.DURATION_ALWAYS
+ elif section == self.FILTER_TREE_NODES and what != "":
+ what = "WHERE r.node = '%s'" % what
+
+ filter_text = self.filterLine.text()
+ if filter_text != "":
+ if what == "":
+ what = "WHERE"
+ else:
+ what = what + " AND"
+ what = what + " r.name LIKE '%{0}%'".format(filter_text)
+ model = self._get_active_table().model()
+ self.setQuery(model, "SELECT * FROM rules as r %s %s %s" % (what, self._get_order(), self._get_limit()))
+ self._restore_details_view_columns(
+ self.TABLES[self.TAB_RULES]['view'].horizontalHeader(),
+ "{0}{1}".format(Config.STATS_VIEW_COL_STATE, self.TAB_RULES)
+ )
+
+ def _set_rules_query(self, rule_name="", node=""):
+ if node != "":
+ node = "c.node = '%s'" % node
+ if rule_name != "":
+ rule_name = "c.rule = '%s'" % rule_name
+
+ condition = "%s AND %s" % (rule_name, node) if rule_name != "" and node != "" else ""
+
+ model = self._get_active_table().model()
+ self.setQuery(model, "SELECT " \
+ "MAX(c.time) as {0}, " \
+ "c.node as {1}, " \
+ "count(c.process) as {2}, " \
+ "c.uid as {3}, " \
+ "c.protocol as {4}, " \
+ "c.dst_port as {5}, " \
+ "CASE c.dst_host WHEN ''" \
+ " THEN c.dst_ip " \
+ " ELSE c.dst_host " \
+ "END {6}, " \
+ "c.process as {7}, " \
+ "c.process_args as {8}, " \
+ "c.process_cwd as CWD " \
+ "FROM connections as c " \
+ "WHERE {9} GROUP BY c.process, c.process_args, c.uid, {10}, c.dst_port {11}".format(
+ self.COL_STR_TIME,
+ self.COL_STR_NODE,
+ self.COL_STR_HITS,
+ self.COL_STR_UID,
+ self.COL_STR_PROTOCOL,
+ self.COL_STR_DST_PORT,
+ self.COL_STR_DESTINATION,
+ self.COL_STR_PROCESS,
+ self.COL_STR_PROC_ARGS,
+ condition,
+ self.COL_STR_DESTINATION,
+ self._get_order() + self._get_limit()))
+
+ def _set_hosts_query(self, data):
+ model = self._get_active_table().model()
+ self.setQuery(model, "SELECT " \
+ "MAX(c.time) as {0}, " \
+ "c.node as {1}, " \
+ "count(c.process) as {2}, " \
+ "c.action as {3}, " \
+ "c.uid as {4}, " \
+ "c.protocol as {5}, " \
+ "c.dst_port as {6}, " \
+ "c.dst_ip as {7}, " \
+ "c.process || ' (' || c.pid || ')' as {8}, " \
+ "c.process_args as {9}, " \
+ "c.process_cwd as CWD, " \
+ "c.rule as {10} " \
+ "FROM connections as c " \
+ "WHERE c.dst_host = '{11}' GROUP BY c.pid, {12}, c.process_args, c.src_ip, c.dst_ip, c.dst_port, c.protocol, c.action, c.node {13}".format(
+ self.COL_STR_TIME,
+ self.COL_STR_NODE,
+ self.COL_STR_HITS,
+ self.COL_STR_ACTION,
+ self.COL_STR_UID,
+ self.COL_STR_PROTOCOL,
+ self.COL_STR_DST_PORT,
+ self.COL_STR_DST_IP,
+ self.COL_STR_PROCESS,
+ self.COL_STR_PROC_ARGS,
+ self.COL_STR_RULE,
+ data,
+ self.COL_STR_PROCESS,
+ self._get_order("1") + self._get_limit()))
+
+ def _set_process_query(self, data):
+ model = self._get_active_table().model()
+ self.setQuery(model, "SELECT " \
+ "MAX(c.time) as {0}, " \
+ "c.node as {1}, " \
+ "count(c.dst_ip) as {2}, " \
+ "c.action as {3}, " \
+ "c.uid as {4}, " \
+ "CASE c.dst_host WHEN ''" \
+ " THEN c.dst_ip || ' -> ' || c.dst_port " \
+ " ELSE c.dst_host || ' -> ' || c.dst_port " \
+ "END {5}, " \
+ "c.pid as PID, " \
+ "c.process_args as {6}, " \
+ "c.process_cwd as CWD, " \
+ "c.rule as {7} " \
+ "FROM connections as c " \
+ "WHERE c.process = '{8}' " \
+ "GROUP BY c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.uid, c.action, c.node, c.pid, c.process_args {9}".format(
+ self.COL_STR_TIME,
+ self.COL_STR_NODE,
+ self.COL_STR_HITS,
+ self.COL_STR_ACTION,
+ self.COL_STR_UID,
+ self.COL_STR_DESTINATION,
+ self.COL_STR_PROC_ARGS,
+ self.COL_STR_RULE,
+ data,
+ self._get_order("1") + self._get_limit()))
+
+ nrows = self._get_active_table().model().rowCount()
+ self.cmdProcDetails.setVisible(nrows != 0)
+
+ def _set_addrs_query(self, data):
+ model = self._get_active_table().model()
+ self.setQuery(model, "SELECT " \
+ "MAX(c.time) as {0}, " \
+ "c.node as {1}, " \
+ "count(c.dst_ip) as {2}, " \
+ "c.action as {3}, " \
+ "c.uid as {4}, " \
+ "c.protocol as {5}, " \
+ "CASE c.dst_host WHEN ''" \
+ " THEN c.dst_ip " \
+ " ELSE c.dst_host " \
+ "END {6}, " \
+ "c.dst_port as {7}, " \
+ "c.process || ' (' || c.pid || ')' as {8}, " \
+ "c.process_args as {9}, " \
+ "c.process_cwd as CWD, " \
+ "c.rule as {10} " \
+ "FROM connections as c " \
+ "WHERE c.dst_ip = '{11}' GROUP BY c.pid, {12}, c.process_args, c.src_ip, c.dst_port, {13}, c.protocol, c.action, c.uid, c.node {14}".format(
+ self.COL_STR_TIME,
+ self.COL_STR_NODE,
+ self.COL_STR_HITS,
+ self.COL_STR_ACTION,
+ self.COL_STR_UID,
+ self.COL_STR_PROTOCOL,
+ self.COL_STR_DESTINATION,
+ self.COL_STR_DST_PORT,
+ self.COL_STR_PROCESS,
+ self.COL_STR_PROC_ARGS,
+ self.COL_STR_RULE,
+ data,
+ self.COL_STR_PROCESS,
+ self.COL_STR_DESTINATION,
+ self._get_order("1") + self._get_limit()))
+
+ def _set_ports_query(self, data):
+ model = self._get_active_table().model()
+ self.setQuery(model, "SELECT " \
+ "MAX(c.time) as {0}, " \
+ "c.node as {1}, " \
+ "count(c.dst_ip) as {2}, " \
+ "c.action as {3}, " \
+ "c.uid as {4}, " \
+ "c.protocol as {5}, " \
+ "c.dst_ip as {6}, " \
+ "CASE c.dst_host WHEN ''" \
+ " THEN c.dst_ip " \
+ " ELSE c.dst_host " \
+ "END {7}, " \
+ "c.process || ' (' || c.pid || ')' as {8}, " \
+ "c.process_args as {9}, " \
+ "c.process_cwd as CWD, " \
+ "c.rule as {10} " \
+ "FROM connections as c " \
+ "WHERE c.dst_port = '{11}' GROUP BY c.pid, {12}, c.process_args, {13}, c.src_ip, c.dst_ip, c.protocol, c.action, c.uid, c.node {14}".format(
+ self.COL_STR_TIME,
+ self.COL_STR_NODE,
+ self.COL_STR_HITS,
+ self.COL_STR_ACTION,
+ self.COL_STR_UID,
+ self.COL_STR_PROTOCOL,
+ self.COL_STR_DST_IP,
+ self.COL_STR_DESTINATION,
+ self.COL_STR_PROCESS,
+ self.COL_STR_PROC_ARGS,
+ self.COL_STR_RULE,
+ data,
+ self.COL_STR_PROCESS,
+ self.COL_STR_DESTINATION,
+ self._get_order("1") + self._get_limit()))
+
+ def _set_users_query(self, data):
+ uid = data.split(" ")
+ if len(uid) == 2:
+ uid = uid[1].strip("()")
+ else:
+ uid = uid[0]
+ model = self._get_active_table().model()
+ self.setQuery(model, "SELECT " \
+ "MAX(c.time) as {0}, " \
+ "c.uid, " \
+ "c.node as {1}, " \
+ "count(c.dst_ip) as {2}, " \
+ "c.action as {3}, " \
+ "c.protocol as {4}, " \
+ "c.dst_ip as {5}, " \
+ "c.dst_host as {6}, " \
+ "c.dst_port as {7}, " \
+ "c.process || ' (' || c.pid || ')' as {8}, " \
+ "c.process_args as {9}, " \
+ "c.process_cwd as CWD, " \
+ "c.rule as {10} " \
+ "FROM connections as c " \
+ "WHERE c.uid = '{11}' GROUP BY c.pid, {12}, c.process_args, c.src_ip, c.dst_ip, c.dst_host, c.dst_port, c.protocol, c.action, c.node {13}".format(
+ self.COL_STR_TIME,
+ self.COL_STR_NODE,
+ self.COL_STR_HITS,
+ self.COL_STR_ACTION,
+ self.COL_STR_PROTOCOL,
+ self.COL_STR_DST_IP,
+ self.COL_STR_DESTINATION,
+ self.COL_STR_DST_PORT,
+ self.COL_STR_PROCESS,
+ self.COL_STR_PROC_ARGS,
+ self.COL_STR_RULE,
+ uid,
+ self.COL_STR_PROCESS,
+ self._get_order("1") + self._get_limit()))
+
+ # get the query filtering by text when a tab is in the detail view.
+ def _get_indetail_filter_query(self, lastQuery, text):
+ try:
+ cur_idx = self.tabWidget.currentIndex()
+ base_query = lastQuery.split("GROUP BY")
+ qstr = base_query[0]
+ where = qstr.split("WHERE")[1] # get SELECT ... WHERE (*)
+ ands = where.split("AND (")[0] # get WHERE (*) AND (...)
+ qstr = qstr.split("WHERE")[0] # get * WHERE ...
+ qstr += "WHERE %s" % ands
+
+ # if there's no text to filter, strip the filter "AND ()", and
+ # return the original query.
+ if text == "":
+ return
+
+ qstr += "AND (c.time LIKE '%{0}%' OR " \
+ "c.action LIKE '%{0}%' OR " \
+ "c.pid LIKE '%{0}%' OR " \
+ "c.src_port LIKE '%{0}%' OR " \
+ "c.src_ip LIKE '%{0}%' OR ".format(text)
+
+ # exclude from query the field of the view we're filtering by
+ if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_PORTS:
+ qstr += "c.dst_port LIKE '%{0}%' OR ".format(text)
+ if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_ADDRS:
+ qstr += "c.dst_ip LIKE '%{0}%' OR ".format(text)
+ if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_HOSTS:
+ qstr += "c.dst_host LIKE '%{0}%' OR ".format(text)
+ if self.IN_DETAIL_VIEW[cur_idx] != self.TAB_PROCS:
+ qstr += "c.process LIKE '%{0}%' OR ".format(text)
+
+ qstr += "c.process_args LIKE '%{0}%')".format(text)
+
+ finally:
+ if len(base_query) > 1:
+ qstr += " GROUP BY" + base_query[1]
+ return qstr
+
+ @QtCore.pyqtSlot()
+ def _on_settings_saved(self):
+ self.settings_saved.emit()
+
+ def _on_save_clicked(self):
+ tab_idx = self.tabWidget.currentIndex()
+
+ filename = QtWidgets.QFileDialog.getSaveFileName(self,
+ QC.translate("stats", 'Save as CSV'),
+ self._file_names[tab_idx],
+ 'All Files (*);;CSV Files (*.csv)')[0].strip()
+ if filename == '':
+ return
+
+ with self._lock:
+ table = self._tables[tab_idx]
+ ncols = table.model().columnCount()
+ nrows = table.model().rowCount()
+ cols = []
+
+ for col in range(0, ncols):
+ cols.append(table.model().headerData(col, QtCore.Qt.Horizontal))
+
+ with open(filename, 'w') as csvfile:
+ w = csv.writer(csvfile, dialect='excel')
+ w.writerow(cols)
+
+ if tab_idx == self.TAB_MAIN:
+ w.writerows(table.model().dumpRows())
+ else:
+ for row in range(0, nrows):
+ values = []
+ for col in range(0, ncols):
+ values.append(table.model().index(row, col).data())
+ w.writerow(values)
+
+ def _setup_table(self, widget, tableWidget, table_name, fields="*", group_by="", order_by="2", sort_direction=SORT_ORDER[1], limit="", resize_cols=(), model=None, delegate=None, verticalScrollBar=None):
+ tableWidget.setSortingEnabled(True)
+ if model == None:
+ model = self._db.get_new_qsql_model()
+ if delegate != None:
+ tableWidget.setItemDelegate(ColorizedDelegate(self, config=delegate))
+
+ if verticalScrollBar != None:
+ tableWidget.setVerticalScrollBar(verticalScrollBar)
+ tableWidget.vScrollBar.sliderPressed.connect(self._cb_scrollbar_pressed)
+ tableWidget.vScrollBar.sliderReleased.connect(self._cb_scrollbar_released)
+
+ self.setQuery(model, "SELECT " + fields + " FROM " + table_name + group_by + " ORDER BY " + order_by + " " + sort_direction + limit)
+ tableWidget.setModel(model)
+
+ header = tableWidget.horizontalHeader()
+ if header != None:
+ header.sortIndicatorChanged.connect(self._cb_table_header_clicked)
+
+ for _, col in enumerate(resize_cols):
+ header.setSectionResizeMode(col, QtWidgets.QHeaderView.ResizeToContents)
+
+ cur_idx = self.tabWidget.currentIndex()
+ self._cfg.setSettings("{0}{1}".format(Config.STATS_VIEW_DETAILS_COL_STATE, cur_idx), header.saveState())
+ return tableWidget
+
+ def update_interception_status(self, enabled):
+ self.startButton.setDown(enabled)
+ self.startButton.setChecked(enabled)
+ if enabled:
+ self._update_status_label(running=True, text=self.FIREWALL_RUNNING)
+ else:
+ self._update_status_label(running=False, text=self.FIREWALL_DISABLED)
+
+ # launched from a thread
+ def update(self, is_local=True, stats=None, need_query_update=True):
+ # lock mandatory when there're multiple clients
+ with self._lock:
+ if stats is not None:
+ self._stats = stats
+ # do not update any tab if the window is not visible
+ if self.isVisible() and self.isMinimized() == False:
+ self._trigger.emit(is_local, need_query_update)
+
+ def update_status(self):
+ self.startButton.setDown(self.daemon_connected)
+ self.startButton.setChecked(self.daemon_connected)
+ self.startButton.setDisabled(not self.daemon_connected)
+ if self.daemon_connected:
+ self._update_status_label(running=True, text=self.FIREWALL_RUNNING)
+ else:
+ self._update_status_label(running=False, text=self.FIREWALL_STOPPED)
+ self.statusLabel.setStyleSheet('color: red; margin: 5px')
+
+ @QtCore.pyqtSlot(bool, bool)
+ def _on_update_triggered(self, is_local, need_query_update=False):
+ if self._stats is None:
+ self.daemonVerLabel.setText("")
+ self.uptimeLabel.setText("")
+ self.rulesLabel.setText("")
+ self.consLabel.setText("")
+ self.droppedLabel.setText("")
+ else:
+ nodes = self._nodes.count()
+ self.daemonVerLabel.setText(self._stats.daemon_version)
+ if nodes <= 1:
+ self.uptimeLabel.setText(str(datetime.timedelta(seconds=self._stats.uptime)))
+ self.rulesLabel.setText("%s" % self._stats.rules)
+ self.consLabel.setText("%s" % self._stats.connections)
+ self.droppedLabel.setText("%s" % self._stats.dropped)
+ else:
+ self.uptimeLabel.setText("")
+ self.rulesLabel.setText("")
+ self.consLabel.setText("")
+ self.droppedLabel.setText("")
+
+ if need_query_update:
+ self._refresh_active_table()
+
+ # prevent a click on the window's x
+ # from quitting the whole application
+ def closeEvent(self, e):
+ self._save_settings()
+ e.accept()
+ self.hide()
+
+ def hideEvent(self, e):
+ self._save_settings()
+
+ # https://gis.stackexchange.com/questions/86398/how-to-disable-the-escape-key-for-a-dialog
+ def keyPressEvent(self, event):
+ if not event.key() == QtCore.Qt.Key_Escape:
+ super(StatsDialog, self).keyPressEvent(event)
+
+ def setQuery(self, model, q):
+ if self._context_menu_active == True or self.scrollbar_active == True or self._are_rows_selected():
+ return
+ with self._lock:
+ try:
+ model.query().clear()
+ model.setQuery(q, self._db_sqlite)
+ if model.lastError().isValid():
+ print("setQuery() error: ", model.lastError().text())
+
+ if self.tabWidget.currentIndex() != self.TAB_MAIN:
+ self.labelRowsCount.setText("{0}".format(model.rowCount()))
+ else:
+ self.labelRowsCount.setText("")
+ except Exception as e:
+ print(self._address, "setQuery() exception: ", e)
+ finally:
+ self._show_columns()
--- /dev/null
+from queue import Queue
+from datetime import datetime
+import time
+import json
+
+from opensnitch import ui_pb2
+from opensnitch.database import Database
+from opensnitch.config import Config
+
+class Nodes():
+ __instance = None
+ LOG_TAG = "[Nodes]: "
+ ONLINE = "\u2713 online"
+ OFFLINE = "\u2613 offline"
+ WARNING = "\u26a0"
+
+ @staticmethod
+ def instance():
+ if Nodes.__instance == None:
+ Nodes.__instance = Nodes()
+ return Nodes.__instance
+
+ def __init__(self):
+ self._db = Database.instance()
+ self._nodes = {}
+ self._notifications_sent = {}
+
+ def count(self):
+ return len(self._nodes)
+
+ def add(self, peer, client_config=None):
+ try:
+ proto, _addr = self.get_addr(peer)
+ addr = "%s:%s" % (proto, _addr)
+ if addr not in self._nodes:
+ self._nodes[addr] = {
+ 'notifications': Queue(),
+ 'online': True,
+ 'last_seen': datetime.now()
+ }
+ else:
+ self._nodes[addr]['last_seen'] = datetime.now()
+
+ self._nodes[addr]['online'] = True
+ self.add_data(addr, client_config)
+ self.update(proto, _addr)
+
+ return self._nodes[addr]
+
+ except Exception as e:
+ print(self.LOG_TAG, "exception adding/updating node: ", e, "addr:", addr, "config:", client_config)
+
+ return None
+
+ def add_data(self, addr, client_config):
+ if client_config != None:
+ self._nodes[addr]['data'] = self.get_client_config(client_config)
+ self.add_rules(addr, client_config.rules)
+
+ def add_rule(self, time, node, name, enabled, precedence, action, duration, op_type, op_sensitive, op_operand, op_data):
+ # don't add rule if the user has selected to exclude temporary
+ # rules
+ if duration in Config.RULES_DURATION_FILTER:
+ return
+
+ self._db.insert("rules",
+ "(time, node, name, enabled, precedence, action, duration, operator_type, operator_sensitive, operator_operand, operator_data)",
+ (time, node, name, enabled, precedence, action, duration, op_type, op_sensitive, op_operand, op_data),
+ action_on_conflict="REPLACE")
+
+ def add_rules(self, addr, rules):
+ try:
+ for _,r in enumerate(rules):
+ self.add_rule(datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ addr,
+ r.name, str(r.enabled), str(r.precedence), r.action, r.duration,
+ r.operator.type,
+ str(r.operator.sensitive),
+ r.operator.operand,
+ r.operator.data)
+ except Exception as e:
+ print(self.LOG_TAG + " exception adding node to db: ", e)
+
+ def update_rule_time(self, time, rule_name, addr):
+ self._db.update("rules",
+ "time=?",
+ (time, rule_name, addr),
+ "name=? AND node=?",
+ action_on_conflict="OR REPLACE"
+ )
+
+ def delete_all(self):
+ self.send_notifications(None)
+ self._nodes = {}
+
+ def delete(self, peer):
+ proto, addr = self.get_addr(peer)
+ addr = "%s:%s" % (proto, addr)
+ # Force the node to get one new item from queue,
+ # in order to loop and exit.
+ self._nodes[addr]['notifications'].put(None)
+ if addr in self._nodes:
+ del self._nodes[addr]
+
+ def get(self):
+ return self._nodes
+
+ def get_node(self, addr):
+ try:
+ return self._nodes[addr]
+ except Exception as e:
+ return None
+
+ def get_nodes(self):
+ return self._nodes
+
+ def get_node_config(self, addr):
+ try:
+ return self._nodes[addr]['data'].config
+ except Exception as e:
+ print(self.LOG_TAG + " exception get_node_config(): ", e)
+ return None
+
+ def get_client_config(self, client_config):
+ try:
+ node_config = json.loads(client_config.config)
+ if 'LogLevel' not in node_config:
+ node_config['LogLevel'] = 1
+ client_config.config = json.dumps(node_config)
+ except Exception as e:
+ print(self.LOG_TAG, "exception parsing client config", e)
+
+ return client_config
+
+ def get_addr(self, peer):
+ peer = peer.split(":")
+ # WA for backward compatibility
+ if peer[0] == "unix" and peer[1] == "":
+ peer[1] = "/local"
+ return peer[0], peer[1]
+
+ def get_notifications(self):
+ notlist = []
+ try:
+ for c in self._nodes:
+ if self._nodes[c]['online'] == False:
+ continue
+ if self._nodes[c]['notifications'].empty():
+ continue
+ notif = self._nodes[c]['notifications'].get(False)
+ if notif != None:
+ self._nodes[c]['notifications'].task_done()
+ notlist.append(notif)
+ except Exception as e:
+ print(self.LOG_TAG + " exception get_notifications(): ", e)
+
+ return notlist
+
+ def save_node_config(self, addr, config):
+ try:
+ self._nodes[addr]['data'].config = config
+ except Exception as e:
+ print(self.LOG_TAG + " exception saving node config: ", e, addr, config)
+
+ def save_nodes_config(self, config):
+ try:
+ for c in self._nodes:
+ self._nodes[c]['data'].config = config
+ except Exception as e:
+ print(self.LOG_TAG + " exception saving nodes config: ", e, config)
+
+ def start_interception(self, _addr=None, _callback=None):
+ return self.firewall(not_type=ui_pb2.LOAD_FIREWALL, addr=_addr, callback=_callback)
+
+ def stop_interception(self, _addr=None, _callback=None):
+ return self.firewall(not_type=ui_pb2.UNLOAD_FIREWALL, addr=_addr, callback=_callback)
+
+ def firewall(self, not_type=ui_pb2.LOAD_FIREWALL, addr=None, callback=None):
+ noti = ui_pb2.Notification(clientName="", serverName="", type=not_type, data="", rules=[])
+ if addr == None:
+ nid = self.send_notifications(noti, callback)
+ else:
+ nid = self.send_notification(addr, noti, callback)
+
+ return nid, noti
+
+ def send_notification(self, addr, notification, callback_signal=None):
+ try:
+ notification.id = int(str(time.time()).replace(".", ""))
+ if addr not in self._nodes:
+ # FIXME: the reply is sent before we return the notification id
+ if callback_signal != None:
+ callback_signal.emit(
+ ui_pb2.NotificationReply(
+ id=notification.id,
+ code=ui_pb2.ERROR,
+ data="node not connected: {0}".format(addr)
+ )
+ )
+ return notification.id
+
+ self._notifications_sent[notification.id] = {
+ 'callback': callback_signal,
+ 'type': notification.type
+ }
+ self._nodes[addr]['notifications'].put(notification)
+ except Exception as e:
+ print(self.LOG_TAG + " exception sending notification: ", e, addr, notification)
+ if callback_signal != None:
+ callback_signal.emit(
+ ui_pb2.NotificationReply(
+ id=notification.id,
+ code=ui_pb2.ERROR,
+ data="Notification not sent ({0}):<br>{1}".format(addr, e)
+ )
+ )
+
+ return notification.id
+
+ def send_notifications(self, notification, callback_signal=None):
+ """
+ Enqueues a notification to the clients queue.
+ It'll be retrieved and delivered by get_notifications
+ """
+ try:
+ notification.id = int(str(time.time()).replace(".", ""))
+ for c in self._nodes:
+ self._nodes[c]['notifications'].put(notification)
+ self._notifications_sent[notification.id] = {
+ 'callback': callback_signal,
+ 'type': notification.type
+ }
+ except Exception as e:
+ print(self.LOG_TAG + " exception sending notifications: ", e, notification)
+
+ return notification.id
+
+ def reply_notification(self, addr, reply):
+ if reply == None:
+ print(self.LOG_TAG, " reply notification None")
+ return
+
+ if reply.id in self._notifications_sent:
+ if self._notifications_sent[reply.id] != None:
+ if self._notifications_sent[reply.id]['callback'] != None:
+ self._notifications_sent[reply.id]['callback'].emit(reply)
+
+ # delete only one-time notifications
+ # we need the ID of streaming notifications from the server
+ # (monitor_process for example) to keep track of the data sent to us.
+ if self._notifications_sent[reply.id]['type'] != ui_pb2.MONITOR_PROCESS:
+ del self._notifications_sent[reply.id]
+
+ def stop_notifications(self):
+ """Send a dummy notification to force Notifications class to exit.
+ """
+ exit_noti = ui_pb2.Notification(clientName="", serverName="", type=0, data="", rules=[])
+ self.send_notifications(exit_noti)
+
+ def update(self, proto, addr, status=ONLINE):
+ try:
+ self._db.update("nodes",
+ "hostname=?,version=?,last_connection=?,status=?",
+ (
+ self._nodes[proto+":"+addr]['data'].name,
+ self._nodes[proto+":"+addr]['data'].version,
+ datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ status,
+ addr),
+ "addr=?"
+ )
+ except Exception as e:
+ print(self.LOG_TAG + " exception updating DB: ", e, addr)
+
+ def update_all(self, status=OFFLINE):
+ try:
+ for peer in self._nodes:
+ proto, addr = self.get_addr(peer)
+ self._db.update("nodes",
+ "hostname=?,version=?,last_connection=?,status=?",
+ (
+ self._nodes[proto+":"+addr]['data'].name,
+ self._nodes[proto+":"+addr]['data'].version,
+ datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
+ status,
+ addr),
+ "addr=?"
+ )
+ except Exception as e:
+ print(self.LOG_TAG + " exception updating nodes: ", e)
+
+ def delete_rule(self, rule_name, addr, callback):
+ rule = ui_pb2.Rule(name=rule_name)
+ rule.enabled = False
+ rule.action = ""
+ rule.duration = ""
+ rule.operator.type = ""
+ rule.operator.operand = ""
+ rule.operator.data = ""
+
+ noti = ui_pb2.Notification(type=ui_pb2.DELETE_RULE, rules=[rule])
+ if addr != None:
+ nid = self.send_notification(addr, noti, None)
+ else:
+ nid = self.send_notifications(noti, None)
+ self._db.delete_rule(rule.name, addr)
+
+ return nid, noti
--- /dev/null
+#!/usr/bin/python
+
+from PyQt5.QtCore import QCoreApplication as QC
+import os
+from opensnitch.utils import Utils
+from opensnitch.config import Config
+
+class DesktopNotifications():
+ """DesktopNotifications display informative pop-ups using the system D-Bus.
+ The notifications are handled and configured by the system.
+
+ The notification daemon also decides where to show the notifications, as well
+ as how to group them.
+
+ The body of a notification supports markup (if the implementation supports it):
+ https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#markup
+ Basically: <a>, <u>, <b>, <i> and <img>. New lines can be added with the regular \n.
+
+ It also support actions (buttons).
+
+ https://notify2.readthedocs.io/en/latest/
+ """
+
+ _cfg = Config.init()
+
+ # list of hints:
+ # https://people.gnome.org/~mccann/docs/notification-spec/notification-spec-latest.html#hints
+ HINT_DESKTOP_ENTRY = "desktop-entry"
+ CATEGORY_NETWORK = "network"
+
+ EXPIRES_DEFAULT = 0
+ NEVER_EXPIRES = -1
+
+ def __init__(self):
+ self.ACTION_ALLOW = QC.translate("popups", "Allow")
+ self.ACTION_DENY = QC.translate("popups", "Deny")
+ self.IS_LIBNOTIFY_AVAILABLE = True
+ self.DOES_SUPPORT_ACTIONS = True
+
+ try:
+ import notify2
+ self.ntf2 = notify2
+ mloop = 'glib'
+
+ # First try to initialise the D-Bus connection with the given
+ # mainloop.
+ # If it fails, we'll try to initialise it without it.
+ try:
+ self.ntf2.init("opensnitch", mainloop=mloop)
+ except Exception:
+ self.DOES_SUPPORT_ACTIONS = False
+ self.ntf2.init("opensnitch")
+
+ # usually because dbus mainloop is not initiated, specially
+ # with 'qt'
+ # FIXME: figure out how to init it, or how to connect to an
+ # existing session.
+ print("DesktopNotifications(): system doesn't support actions. Available capabilities:")
+ print(self.ntf2.get_server_caps())
+
+
+ # Example: ['actions', 'action-icons', 'body', 'body-markup', 'icon-static', 'persistence', 'sound']
+ if ('actions' not in self.ntf2.get_server_caps()):
+ self.DOES_SUPPORT_ACTIONS = False
+
+ except Exception as e:
+ print("DesktopNotifications not available (install python3-notify2):", e)
+ self.IS_LIBNOTIFY_AVAILABLE = False
+
+ def is_available(self):
+ return self.IS_LIBNOTIFY_AVAILABLE
+
+ def are_enabled(self):
+ return self._cfg.getBool(Config.NOTIFICATIONS_ENABLED, True)
+
+ def support_actions(self):
+ """Returns true if the notifications daemon support actions(buttons).
+ This depends on 2 factors:
+ - If the notification server actually supports it (get_server_caps()).
+ - If there's a dbus instance running.
+ """
+ return self.DOES_SUPPORT_ACTIONS
+
+ def show(self, title, body, icon="dialog-information"):
+ try:
+ ntf = self.ntf2.Notification(title, body, icon)
+
+ # timeouts seems to be ignored (on Cinnamon at least)
+ timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15)
+ # -1 and 0 are special values
+ if timeout > 0:
+ timeout = timeout * 1000
+ ntf.set_timeout(timeout * 1000)
+ ntf.timeout = timeout * 1000
+
+ ntf.set_category(self.CATEGORY_NETWORK)
+ # used to display our app icon an name.
+ ntf.set_hint(self.HINT_DESKTOP_ENTRY, "opensnitch_ui")
+ ntf.show()
+ except Exception as e:
+ print("[notifications] show() exception:", e)
+
+ # TODO:
+ # - construct a rule with the default configured parameters.
+ # - create a common dialogs/prompt.py:_send_rule(), maybe in utils.py
+ def ask(self, connection, timeout, callback):
+ c = connection
+ title = QC.translate("popups", "New outgoing connection")
+ body = c.process_path + "\n"
+ body = body + QC.translate("popups", "is connecting to <b>%s</b> on %s port %d") % ( \
+ c.dst_host or c.dst_ip,
+ c.protocol.upper(),
+ c.dst_port )
+
+ ntf = self.ntf2.Notification(title, body, "dialog-warning")
+ timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15)
+ ntf.set_timeout(timeout * 1000)
+ ntf.timeout = timeout * 1000
+ if self.DOES_SUPPORT_ACTIONS:
+ ntf.set_urgency(self.ntf2.URGENCY_CRITICAL)
+ ntf.add_action("allow", self.ACTION_ALLOW, callback, connection)
+ ntf.add_action("deny", self.ACTION_DENY, callback, connection)
+ #ntf.add_action("open-gui", QC.translate("popups", "View"), callback, connection)
+ ntf.set_category(self.CATEGORY_NETWORK)
+ ntf.set_hint(self.HINT_DESKTOP_ENTRY, "opensnitch_ui")
+ ntf.show()
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="512"
+ height="512"
+ viewBox="0 0 135.46667 135.46667"
+ version="1.1"
+ id="svg8"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="icon-pause.svg"
+ inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/opensnitch/res/icon-pause.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.8520834"
+ inkscape:cx="92.868389"
+ inkscape:cy="235.9505"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1600"
+ inkscape:window-height="847"
+ inkscape:window-x="0"
+ inkscape:window-y="204"
+ inkscape:window-maximized="1"
+ units="px"
+ inkscape:document-rotation="0"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:pagecheckerboard="0">
+ <sodipodi:guide
+ position="45.643586,19.473337"
+ orientation="0,-1"
+ id="guide858" />
+ <sodipodi:guide
+ position="48.574825,118.50736"
+ orientation="0,-1"
+ id="guide860" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Capa 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-284.30001)">
+ <path
+ style="fill:#232629;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.88352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 24.756487,346.52813 9.319538,-24.69115 25.892395,-5.7321 24.204196,-12.72968 25.173834,11.71117 4.05241,24.92736 16.3729,12.59899 3.85152,17.78296 -6.72818,16.22266 -13.39906,8.50947 -91.980588,0.9444 -15.4585065,-11.35245 -3.13783,-17.10855 7.8853195,-15.623 z"
+ id="path1481"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.260914;stroke-opacity:1"
+ d="M 25.617431,400.30128 C 13.956756,398.62993 4.455433,390.16178 1.3179549,378.64484 c -0.61010168,-2.23995 -0.68141103,-2.97449 -0.68141103,-7.02809 0,-4.05349 0.0709898,-4.78804 0.68141103,-7.02767 1.4422687,-5.29433 4.0049287,-9.7113 7.7967237,-13.43793 3.1792184,-3.12477 7.6935334,-5.90721 11.3127154,-6.97315 l 1.011141,-0.29572 0.159592,-2.88031 c 0.699721,-12.63666 9.525535,-23.14365 22.243579,-26.48047 2.251529,-0.59144 3.06051,-0.67035 7.018915,-0.67878 2.525469,-0.006 4.987882,0.11997 5.653762,0.28414 1.179544,0.29572 1.179787,0.29572 1.94729,-0.66193 1.527585,-1.90257 4.987766,-4.99031 7.28526,-6.50045 5.401898,-3.5513 10.887485,-5.3302 17.382981,-5.63665 5.770238,-0.26941 10.984996,0.78506 16.16673,3.27379 7.128836,3.42523 12.181556,8.39142 15.666736,15.39805 2.50603,5.03807 3.59513,10.17644 3.34371,15.77554 l -0.13051,2.91272 2.13481,1.38154 c 1.17413,0.7598 3.3948,2.61049 4.93483,4.112 8.71561,8.49782 11.93955,20.33805 8.75495,32.15396 -1.29904,4.81951 -4.90315,10.89827 -8.63914,14.56995 -3.56956,3.50836 -9.80593,7.14911 -14.36425,8.38564 -4.82636,1.30967 -4.22578,1.29325 -45.398785,1.26083 -21.133453,-0.021 -39.125022,-0.13154 -39.981254,-0.25467 z m 81.947239,-8.71712 c 5.8504,-1.53751 11.05542,-5.03703 14.56544,-9.79276 1.44781,-1.96191 3.2449,-5.86701 3.9156,-8.50866 0.82755,-3.25884 0.82928,-8.34365 0.004,-11.5926 -1.99124,-7.83999 -8.14631,-14.63425 -15.64619,-17.27125 -1.49953,-0.52724 -2.28209,-0.9303 -2.20209,-1.1355 2.47536,-6.33996 2.20303,-13.3187 -0.75958,-19.46544 -2.59443,-5.38335 -6.55609,-9.27909 -12.007089,-11.8076 -3.90889,-1.81322 -6.232969,-2.31309 -10.75536,-2.31309 -3.127696,0 -4.219681,0.10629 -6.053166,0.58617 -7.241285,1.89288 -13.31075,6.68923 -16.465647,13.01161 l -0.732485,1.46794 -1.204478,-0.5988 c -2.240975,-1.11298 -5.542791,-1.97328 -8.178424,-2.13124 -9.130628,-0.54723 -18.012897,5.39587 -20.93206,14.00441 -1.653072,4.87486 -1.486231,9.52955 0.524816,14.64108 0.134869,0.34097 -0.08673,0.38411 -1.535235,0.29256 -2.040978,-0.12839 -5.359579,0.4904 -7.966056,1.4852 -5.358562,2.0439 -10.218999,7.06398 -12.038096,12.43418 -1.6865107,4.9784 -1.5773642,9.50883 0.346691,14.39252 2.080429,5.27991 7.182215,10.04153 12.692712,11.84623 1.10934,0.36201 2.648952,0.76296 3.421359,0.8924 0.794802,0.13259 18.196341,0.21573 40.092031,0.19047 l 38.687677,-0.0421 z"
+ id="path826"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsccccccccccccsccccccccccccccscccsccccsscccccccccc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.6842px;line-height:47.9278px;font-family:Korataki;-inkscape-font-specification:Korataki;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.91711px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="34.512127"
+ y="456.6312"
+ id="text841"
+ transform="scale(1.1975484,0.83503932)"><tspan
+ sodipodi:role="line"
+ id="tspan839"
+ x="34.512127"
+ y="456.6312"
+ style="fill:#ffffff;stroke-width:1.91711px">I</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:76.6842px;line-height:47.9278px;font-family:Korataki;-inkscape-font-specification:Korataki;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.91711px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="61.164921"
+ y="456.6312"
+ id="text841-7"
+ transform="scale(1.1975484,0.83503932)"><tspan
+ sodipodi:role="line"
+ id="tspan839-6"
+ x="61.164921"
+ y="456.6312"
+ style="fill:#ffffff;stroke-width:1.91711px">I</tspan></text>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="512"
+ height="512"
+ viewBox="0 0 135.46667 135.46667"
+ version="1.1"
+ id="svg8"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
+ sodipodi:docname="icon-white.svg"
+ inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.9899495"
+ inkscape:cx="288.90363"
+ inkscape:cy="223.24371"
+ inkscape:document-units="px"
+ inkscape:current-layer="g3307"
+ showgrid="false"
+ inkscape:window-width="1600"
+ inkscape:window-height="847"
+ inkscape:window-x="0"
+ inkscape:window-y="204"
+ inkscape:window-maximized="1"
+ units="px"
+ inkscape:pagecheckerboard="0"
+ showguides="true"
+ inkscape:guide-bbox="true" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Capa 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-284.30001)">
+ <g
+ id="g3307"
+ transform="matrix(10.756234,0,0,10.756234,-0.76070676,-2776.4763)">
+ <path
+ style="fill:#232629;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.268079px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 2.4759494,290.29453 0.8664313,-2.29552 2.4071989,-0.53291 2.2502482,-1.18347 2.3403932,1.08878 0.37675,2.31748 1.522178,1.17132 0.358073,1.65327 -0.625514,1.50821 -1.245702,0.79112 -8.5513735,0.0878 -1.43716703,-1.05543 -0.29172201,-1.59057 0.73309294,-1.45246 z"
+ id="path1481"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96" />
+ <path
+ style="fill:#fbffff;fill-opacity:1;stroke:none;stroke-width:0.0245664;stroke-opacity:1"
+ d="m 2.4221019,295.34968 c -1.0878659,-0.15882 -1.97427807,-0.9635 -2.26698456,-2.05789 -0.05691855,-0.21285 -0.06357126,-0.28265 -0.06357126,-0.66784 0,-0.38518 0.0066228,-0.45498 0.06357126,-0.6678 0.13455437,-0.50309 0.37363404,-0.92281 0.72738412,-1.27693 0.29660044,-0.29693 0.71775704,-0.56133 1.05540334,-0.66262 l 0.094333,-0.0281 0.014889,-0.2737 c 0.065279,-1.20079 0.888671,-2.19921 2.0751826,-2.51629 0.2100531,-0.0562 0.2855258,-0.0637 0.6548195,-0.0645 0.2356099,-5.9e-4 0.4653373,0.0114 0.5274596,0.027 0.1100438,0.0281 0.1100666,0.0281 0.1816696,-0.0629 0.1425139,-0.18079 0.4653264,-0.4742 0.6796678,-0.6177 0.5039623,-0.33746 1.0157321,-0.5065 1.6217201,-0.53562 0.538326,-0.0256 1.0248294,0.0746 1.5082517,0.31109 0.665074,0.32548 1.1364593,0.79739 1.4616053,1.46319 0.233795,0.47874 0.335401,0.96701 0.311946,1.49906 l -0.01218,0.27678 0.199164,0.13128 c 0.109539,0.0722 0.316714,0.24806 0.460388,0.39074 0.813111,0.8075 1.113884,1.93261 0.816781,3.05541 -0.121193,0.45797 -0.457432,1.0356 -0.805977,1.3845 -0.333016,0.33338 -0.91483,0.67934 -1.34009,0.79684 -0.4502688,0.12445 -0.3942394,0.12289 -4.2354147,0.11981 -1.971615,-0.002 -3.6501125,-0.0125 -3.7299933,-0.0242 z m 7.6451481,-0.82834 c 0.545805,-0.1461 1.0314,-0.47864 1.358863,-0.93055 0.13507,-0.18643 0.302726,-0.55751 0.365299,-0.80853 0.07721,-0.30967 0.07737,-0.79285 3.72e-4,-1.10158 -0.185769,-0.74499 -0.759998,-1.39061 -1.459688,-1.64119 -0.139897,-0.0501 -0.212904,-0.0884 -0.205441,-0.1079 0.230936,-0.60245 0.205529,-1.2656 -0.07086,-1.84969 -0.2420436,-0.51155 -0.6116411,-0.88174 -1.1201832,-1.12201 -0.3646743,-0.1723 -0.5814957,-0.2198 -1.0034057,-0.2198 -0.291794,0 -0.3936692,0.0101 -0.5647214,0.0557 -0.6755653,0.17987 -1.2418071,0.63564 -1.5361388,1.23642 l -0.068336,0.13949 -0.1123702,-0.0569 c -0.2090683,-0.10576 -0.5171066,-0.18751 -0.7629941,-0.20252 -0.8518289,-0.052 -1.6804874,0.51274 -1.9528265,1.33076 -0.1542208,0.46323 -0.1386557,0.90554 0.048962,1.39126 0.012582,0.0324 -0.00809,0.0365 -0.1432276,0.0278 -0.1904101,-0.0122 -0.5000142,0.0466 -0.7431816,0.14113 -0.4999194,0.19422 -0.9533668,0.67125 -1.12307682,1.18155 -0.15734055,0.47307 -0.14715788,0.90357 0.032344,1.36764 0.1940906,0.50172 0.6700544,0.95419 1.1841482,1.12568 0.1034943,0.0344 0.2471302,0.0725 0.3191908,0.0848 0.07415,0.0126 1.6976013,0.0205 3.7403281,0.0181 l 3.6093097,-0.004 z m -5.2655768,-1.2975 C 4.2750097,292.90743 3.844102,292.6375 3.844102,292.62395 c 0,-0.0137 0.4309077,-0.28347 0.9575712,-0.59989 l 0.9575723,-0.5752 0.00674,0.39089 0.00674,0.39094 h 1.5707862 1.5707918 v 0.39326 0.39327 H 7.34349 5.772697 l -0.00674,0.39094 -0.00674,0.39093 z m 2.1483996,-2.17014 v -0.39612 H 5.3786739 3.807272 v -0.39326 -0.39328 h 1.570792 1.5707932 l 0.00674,-0.39093 0.00674,-0.39093 0.9575575,0.57524 c 0.5266596,0.3164 0.9606572,0.58453 0.9644402,0.59591 0.00375,0.0115 -0.4050204,0.26764 -0.908454,0.56956 -0.5034315,0.30187 -0.940193,0.56487 -0.970577,0.58441 l -0.055248,0.0354 z"
+ id="path826"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PreferencesDialog</class>
+ <widget class="QDialog" name="PreferencesDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>626</width>
+ <height>442</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Preferences</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="tabPosition">
+ <enum>QTabWidget::North</enum>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <attribute name="title">
+ <string>Pop-ups</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="3">
+ <widget class="QCheckBox" name="popupsCheck">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="4">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Default options</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_5" rowstretch="0,0,0,0,0,0,0,0" columnstretch="1,0">
+ <item row="7" column="0" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,0,0">
+ <item>
+ <widget class="QCheckBox" name="uidCheck">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>If checked, this field will be selected when a pop-up is displayed</string>
+ </property>
+ <property name="text">
+ <string>User ID</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="dstPortCheck">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>If checked, this field will be selected when a pop-up is displayed</string>
+ </property>
+ <property name="text">
+ <string>Destination port</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="dstIPCheck">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>If checked, this field will be selected when a pop-up is displayed</string>
+ </property>
+ <property name="text">
+ <string>Destination IP</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="comboUIAction">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>deny</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-important">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>allow</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-default">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="toolTip">
+ <string><html><head/><body><p>Pop-up default action.</p><p>When a new outgoing connection is about to be established, this action will be selected by default, so if the timeout fires, this is the option that will be applied.</p><p><br/></p><p>While a pop-up is asking the user to allow or deny a connection:</p><p>1. new outgoing connections are denied.</p><p>2. known connections are allowed or denied based on the rules defined by the user.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Action</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="comboUIDialogPos">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>center</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>top right</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>bottom right</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>top left</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>bottom left</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="comboUIDuration">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>once</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>30s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>5m</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>15m</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>30m</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>1h</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>until reboot</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>forever</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_18">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>By default when a new pop-up appears, in its simplest form, you'll be able to filter connections or applications by one property of the connection (executable, port, IP, etc).</p><p>With these options, you can choose multiple fields to filter connections for.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Filter connections also by:</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="comboUITarget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>by executable</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>by command line</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>by destination port</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>by destination ip</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>by user id</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>by PID</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <widget class="Line" name="line_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Default target</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Default position on screen</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="toolTip">
+ <string>Pop-up default duration</string>
+ </property>
+ <property name="text">
+ <string>Duration</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_17">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>The advanced view allows you to easily select multiple fields to filter connections</string>
+ </property>
+ <property name="text">
+ <string>Show advanced view by default</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QCheckBox" name="showAdvancedCheck">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>If checked, the pop-ups will be displayed with the advanced view active.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="cmdTimeoutUp">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="list-add">
+ <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="spinUITimeout">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cmdTimeoutDown">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="list-remove">
+ <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0" colspan="3">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>This timeout is the countdown you see when a pop-up dialog is shown.</p><p>If the pop-up is not answered, the default options will be applied.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Default timeout</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="3">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Disable pop-ups, only display an notification</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_4">
+ <attribute name="title">
+ <string>UI</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="checkUIRules">
+ <property name="toolTip">
+ <string><html><head/><body><p>When this option is selected, the rules of the selected duration won't be added to the list of temporary rules in the GUI.</p><p><br/></p><p>Temporary rules will still be valid, and you can use them when prompted to allow/deny a new connection.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Don't save rules of duration</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QGroupBox" name="groupNotifs">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Desktop notifications</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_9">
+ <item row="0" column="0">
+ <widget class="QRadioButton" name="radioSysNotifs">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Use system notifications</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QRadioButton" name="radioQtNotifs">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Use Qt notifications</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="labelNotifsWarning">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cmdTestNotifs">
+ <property name="text">
+ <string>Test</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="comboUITheme">
+ <item>
+ <property name="text">
+ <string>System</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Events tab columns</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_7">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="checkHideTime">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Time</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QCheckBox" name="checkHideRule">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Rule</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="checkHideNode">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Node</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="checkHideProto">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Protocol</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="checkHideAction">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Action</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="checkHideDst">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Destination</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QCheckBox" name="checkHideProc">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Process</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_21">
+ <property name="text">
+ <string>Theme</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="comboUIRules">
+ <item>
+ <property name="text">
+ <string>any temporary rules</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>once</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="labelThemeError">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_3">
+ <attribute name="title">
+ <string>Nodes</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_4" rowstretch="0,0,0,0,0,0,0,0,0,0,0">
+ <item row="9" column="0">
+ <widget class="QLabel" name="label_13">
+ <property name="text">
+ <string>Process monitor method</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QComboBox" name="comboNodes">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="toolTip">
+ <string><html><head/><body><p>Log file to write logs.<br/></p><p>/dev/stdout will print logs to the standard output.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Log file</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="2">
+ <widget class="QCheckBox" name="checkInterceptUnknown">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0">
+ <widget class="QLabel" name="label_11">
+ <property name="toolTip">
+ <string><html><head/><body><p>The default duration will take place when there's no UI connected.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Default duration</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QCheckBox" name="checkApplyToNodes">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Apply configuration to all nodes</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="labelNodeVersion">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="toolTip">
+ <string><html><head/><body><p>The default action will take place when there's no UI connected.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Default action when the GUI is disconnected</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_8">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>HostName</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="labelNodeName">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ </widget>
+ </item>
+ <item row="9" column="2">
+ <widget class="QComboBox" name="comboNodeMonitorMethod">
+ <property name="accessibleDescription">
+ <string notr="true"/>
+ </property>
+ <item>
+ <property name="text">
+ <string notr="true">proc</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">ebpf</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">audit</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="7" column="2">
+ <widget class="QComboBox" name="comboNodeDuration">
+ <item>
+ <property name="text">
+ <string>once</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>until restart</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>always</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="toolTip">
+ <string><html><head/><body><p>Address of the node.</p><p>Default: unix:///tmp/osui.sock (unix:// is mandatory if it's a Unix socket)</p><p>It can also be an IP address with the port: 127.0.0.1:50051</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Address</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0">
+ <widget class="QLabel" name="label_12">
+ <property name="toolTip">
+ <string><html><head/><body><p>If checked, OpenSnitch will prompt you to allow or deny connections that don't have an associated PID, due to several reasons, mostly due to bad state connections.</p><p>The pop-up dialog will only contain information about the network connection.</p><p>There're some scenarios where these are valid connections though, like when establishing a VPN using WireGuard.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Debug invalid connections</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QComboBox" name="comboNodeAction">
+ <property name="editable">
+ <bool>false</bool>
+ </property>
+ <item>
+ <property name="text">
+ <string>deny</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-important">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>allow</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-default">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_9">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Ignored">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Version</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ </widget>
+ </item>
+ <item row="10" column="2">
+ <widget class="QComboBox" name="comboNodeLogLevel">
+ <property name="accessibleDescription">
+ <string notr="true"/>
+ </property>
+ <item>
+ <property name="text">
+ <string notr="true">DEBUG</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">INFO</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">IMPORTANT</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">WARNING</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">ERROR</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string notr="true">FATAL</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QComboBox" name="comboNodeAddress">
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <item>
+ <property name="text">
+ <string>unix:///tmp/osui.sock</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QComboBox" name="comboNodeLogFile">
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <item>
+ <property name="text">
+ <string>/var/log/opensnitchd.log</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>/dev/stdout</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Preferred</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="10" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Default log level</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="3">
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2">
+ <attribute name="title">
+ <string>Database</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_3" rowstretch="0,0,0,0,0,0">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>15</number>
+ </property>
+ <item row="1" column="1" colspan="3">
+ <widget class="QLabel" name="dbLabel">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QComboBox" name="comboDBType">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <item>
+ <property name="text">
+ <string>In memory</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>File</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Database type</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QPushButton" name="dbFileButton">
+ <property name="text">
+ <string>Select</string>
+ </property>
+ <property name="icon">
+ <iconset theme="document-open">
+ <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="0" colspan="4">
+ <layout class="QGridLayout" name="gridLayout_8">
+ <property name="verticalSpacing">
+ <number>10</number>
+ </property>
+ <item row="0" column="3">
+ <widget class="QSpinBox" name="spinDBMaxDays">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>99999</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QPushButton" name="cmdDBMaxDaysUp">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="list-add">
+ <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="5">
+ <widget class="QLabel" name="label_20">
+ <property name="text">
+ <string>minutes</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QSpinBox" name="spinDBPurgeInterval">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="buttonSymbols">
+ <enum>QAbstractSpinBox::NoButtons</enum>
+ </property>
+ <property name="minimum">
+ <number>5</number>
+ </property>
+ <property name="maximum">
+ <number>1440</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelDBPurgeInterval">
+ <property name="text">
+ <string>Minutes between events purges</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="5">
+ <widget class="QLabel" name="label_19">
+ <property name="text">
+ <string>days</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="checkDBMaxDays">
+ <property name="text">
+ <string>Maximum days of events to keep</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QPushButton" name="cmdDBMaxDaysDown">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="list-remove">
+ <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="cmdDBPurgesUp">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="list-add">
+ <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QPushButton" name="cmdDBPurgesDown">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="list-remove">
+ <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="helpButton">
+ <property name="mouseTracking">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip">
+ <string/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="help-browser">
+ <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ <property name="icon">
+ <iconset theme="window-close">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="applyButton">
+ <property name="text">
+ <string>Apply</string>
+ </property>
+ <property name="icon">
+ <iconset theme="document-save">
+ <normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="acceptButton">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-default">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="statusLabel">
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ProcessDetailsDialog</class>
+ <widget class="QDialog" name="ProcessDetailsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>731</width>
+ <height>478</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Process details</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelProcIcon">
+ <property name="minimumSize">
+ <size>
+ <width>48</width>
+ <height>48</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>64</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QLabel" name="labelProcName">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="text">
+ <string>loading...</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelProcArgs">
+ <property name="text">
+ <string>loading...</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelCwd">
+ <property name="text">
+ <string>CWD: loading...</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="labelStatm">
+ <property name="text">
+ <string>mem stats: loading...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="tabPosition">
+ <enum>QTabWidget::South</enum>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <property name="documentMode">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="tab_2">
+ <attribute name="title">
+ <string>Status</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="1">
+ <widget class="QPlainTextEdit" name="textStatus">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_3">
+ <attribute name="title">
+ <string>Open files</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="1" column="0">
+ <widget class="QPlainTextEdit" name="textOpenedFiles">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage1">
+ <attribute name="title">
+ <string>I/O Statistics</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="0">
+ <widget class="QPlainTextEdit" name="textIOStats">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage2">
+ <attribute name="title">
+ <string>Memory mapped files</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="1" column="0">
+ <widget class="QPlainTextEdit" name="textMappedFiles">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Stack</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="0" column="0">
+ <widget class="QPlainTextEdit" name="textStack">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_4">
+ <attribute name="title">
+ <string>Environment variables</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="0" column="0">
+ <widget class="QPlainTextEdit" name="textEnv">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Application pids</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPids">
+ <property name="maxVisibleItems">
+ <number>100</number>
+ </property>
+ <property name="sizeAdjustPolicy">
+ <enum>QComboBox::AdjustToContents</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cmdAction">
+ <property name="toolTip">
+ <string>Start or stop monitoring this process</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="media-playback-start"/>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cmdClose">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ <property name="icon">
+ <iconset theme="window-close"/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Dialog</class>
+ <widget class="QDialog" name="Dialog">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>520</width>
+ <height>308</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>200</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="windowTitle">
+ <string>opensnitch-qt</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/pics/icon-white.png</normaloff>
+ <normalon>:/pics/icon.png</normalon>:/pics/icon-white.png</iconset>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0,0,0,1">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinAndMaxSize</enum>
+ </property>
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
+ </property>
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::ExpandingFieldsGrow</enum>
+ </property>
+ <property name="labelAlignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="iconLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>64</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>64</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="resources.qrc">:/pics/icon.png</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="appNameLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>16</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string notr="true">Chromium Web Browser</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="appDescriptionLabel">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <weight>50</weight>
+ <italic>true</italic>
+ <bold>false</bold>
+ <underline>true</underline>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string notr="true"><html><head/><body><p>/opt/google/chrome/bin/chrome --something abc --more-long def --for-word-wrapping</p></body></html></string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="appPathLabel">
+ <property name="text">
+ <string notr="true">(/path/to/bin/chromium)</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="argsLabel">
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string notr="true">(/path/to/bin/chromium)</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="messageLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>520</width>
+ <height>40</height>
+ </size>
+ </property>
+ <property name="text">
+ <string notr="true">Chromium Web Browser wants to connect to www.evilsocket.net on tcp port 443. And maybe to www.goodsocket.net on port 344</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>3</number>
+ </property>
+ <item row="6" column="1">
+ <widget class="QLabel" name="label_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>Ubuntu</family>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>User ID</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string><html><head/><body><p><span style=" font-weight:600;">Executed from</span></p></body></html></string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QCheckBox" name="checkUserID">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="checkDstIP">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="3">
+ <widget class="QLabel" name="uidLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="sourceIPLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>Ubuntu</family>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Source IP</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QLabel" name="pidLabelWidget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>Ubuntu</family>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Process ID</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="3">
+ <widget class="QLabel" name="destPortLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLabel" name="destIPLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QComboBox" name="whatIPCombo">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumContentsLength">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="cwdLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QCheckBox" name="checkDstPort">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>Ubuntu</family>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Destination IP</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLabel" name="destPortLabel_1">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>Ubuntu</family>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Dst Port</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="3">
+ <widget class="QLabel" name="pidLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>90</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="3,2,0,0,0">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <item>
+ <widget class="QComboBox" name="whatCombo">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>97</width>
+ <height>26</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>204</width>
+ <height>30</height>
+ </size>
+ </property>
+ <item>
+ <property name="text">
+ <string>from this executable</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>from this command line</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>this destination port</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>this user</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>this destination ip</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>from this PID</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="durationCombo">
+ <property name="minimumSize">
+ <size>
+ <width>97</width>
+ <height>26</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>204</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="currentText">
+ <string>once</string>
+ </property>
+ <item>
+ <property name="text">
+ <string>once</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>30s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>5m</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>15m</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>30m</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>1h</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>until reboot</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>forever</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="denyButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>26</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Deny</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-important">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="applyButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>80</width>
+ <height>26</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>204</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Allow</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-default">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="checkAdvanced">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>24</width>
+ <height>24</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>40</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>+</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="resources.qrc"/>
+ <include location="../../../../../../../../.designer/backup/resources.qrc"/>
+ </resources>
+ <connections/>
+</ui>
--- /dev/null
+<RCC>
+ <qresource prefix="pics">
+ <file>icon-white.svg</file>
+ <file>icon-white.png</file>
+ <file>icon-red.png</file>
+ <file>icon.png</file>
+ </qresource>
+</RCC>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RulesDialog</class>
+ <widget class="QDialog" name="RulesDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>552</width>
+ <height>491</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Rule</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="5" column="1">
+ <widget class="QGroupBox" name="enableGroup">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout" columnstretch="0,0,0,0,0,0,0">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="topMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>12</number>
+ </property>
+ <item row="3" column="1">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Action</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="4">
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="3" column="4">
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Duration</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="6">
+ <widget class="QComboBox" name="durationCombo">
+ <item>
+ <property name="text">
+ <string>once</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>30s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>5m</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>15m</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>30m</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>1h</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>until reboot</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>always</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="6">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QRadioButton" name="actionDenyRadio">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Deny will just discard the connection</string>
+ </property>
+ <property name="text">
+ <string>Deny</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-important">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="actionRejectRadio">
+ <property name="toolTip">
+ <string>Reject will drop the connection, and kill the socket that initiated it</string>
+ </property>
+ <property name="text">
+ <string>Reject</string>
+ </property>
+ <property name="icon">
+ <iconset theme="window-close">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="actionAllowRadio">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Allow will allow the connection</string>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text">
+ <string>Allow</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-default">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <property name="elideMode">
+ <enum>Qt::ElideRight</enum>
+ </property>
+ <property name="documentMode">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="tabWidgetPage1">
+ <attribute name="icon">
+ <iconset theme="system-run">
+ <normaloff>.</normaloff>.</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Applications</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="1" colspan="2">
+ <widget class="QLineEdit" name="procLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>The value of this field is always the absolute path to the executable: /path/to/binary<br/></p><p>Examples:</p><p>- Simple: /path/to/binary</p><p>- Multiple paths: ^/usr/lib(64|)/firefox/firefox$</p><p>- Multiple binaries: ^(/usr/sbin/ntpd|/lib/systemd/systemd-timesyncd|/usr/bin/xbrlapi|/usr/bin/dirmngr)$ </p><p>- Deny/Allow executions from /tmp:</p><p>^/(var/|)tmp/.*$<br/></p><p>For more examples visit the <a href="https://github.com/evilsocket/opensnitch/wiki/Rules-examples">wiki page</a> or ask on the <a href="https://github.com/evilsocket/opensnitch/discussions">Discussion forums</a>.</p></body></html></string>
+ </property>
+ <property name="placeholderText">
+ <string notr="true">/path/to/executable, .*/bin/executable[0-9\.]+$, ...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="checkCmdlineRegexp">
+ <property name="text">
+ <string>Is regular expression</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="uidCheck">
+ <property name="text">
+ <string>From this user ID</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="cmdlineCheck">
+ <property name="text">
+ <string>From this command line</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="2">
+ <widget class="QLineEdit" name="cmdlineLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>This field will contain and match the command line that was executed by the user.<br/></p><p>If the user typed the command, only the command will appear:</p><p>telnet 1.2.3.4<br/></p><p>If the user typed the absolute or relative path to the command, that is what will appear:</p><p>/usr/bin/telnet 1.2.3.4</p><p>../../../usr/bin/telnet 1.2.3.4</p></body></html></string>
+ </property>
+ <property name="placeholderText">
+ <string notr="true">curl -L https://www.domain.com</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QCheckBox" name="pidCheck">
+ <property name="text">
+ <string>From this PID</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <item>
+ <spacer name="horizontalSpacer_11">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="pidLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="4" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="uidLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="procCheck">
+ <property name="text">
+ <string>From this executable</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="checkProcRegexp">
+ <property name="text">
+ <string>is regular expression</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage2">
+ <attribute name="icon">
+ <iconset theme="preferences-system-network">
+ <normaloff>.</normaloff>.</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Network</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0" colspan="2">
+ <widget class="QCheckBox" name="dstPortCheck">
+ <property name="text">
+ <string>To this port</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="dstHostCheck">
+ <property name="text">
+ <string>To this host</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" colspan="3">
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>133</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="5">
+ <widget class="QComboBox" name="protoCombo">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>Only TCP, UDP or UDPLITE are allowed</p><p>You can use regexp, i.e.: ^(TCP|UDP)$</p></body></html></string>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="currentText">
+ <string>TCP</string>
+ </property>
+ <item>
+ <property name="text">
+ <string>TCP</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>UDP</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>UDPLITE</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>TCP6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>UDP6</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>UDPLITE6</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="5">
+ <widget class="QLineEdit" name="dstPortLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>You can specify multiple ports using regular expressions:</p><p><br/></p><p>- 53, 80 or 443:</p><p>^(53|80|443)$</p><p><br/></p><p>- 53, 443 or 5551, 5552, 5553, etc:</p><p>^(53|443|555[0-9])$</p></body></html></string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="4">
+ <widget class="QCheckBox" name="protoCheck">
+ <property name="text">
+ <string>Protocol</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="dstIPCheck">
+ <property name="text">
+ <string>To this IP / Network</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <spacer name="horizontalSpacer_9">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="1">
+ <spacer name="horizontalSpacer_10">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="2" colspan="4">
+ <widget class="QLineEdit" name="dstHostLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>Commas or spaces are not allowed to specify multiple domains.
+
+Use regular expressions instead:
+.*(opensnitch|duckduckgo).com
+.*\.google.com
+
+or a single domain:
+www.gnu.org - it'll only match www.gnu.org, nor ftp.gnu.org, nor www2.gnu.org, ...
+gnu.org - it'll only match gnu.org, nor www.gnu.org, nor ftp.gnu.org, ...</string>
+ </property>
+ <property name="placeholderText">
+ <string>www.domain.org, .*\.domain.org</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2" colspan="4">
+ <widget class="QComboBox" name="dstIPCombo">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string>You can specify a single IP:
+- 192.168.1.1
+
+or a regular expression:
+- 192\.168\.1\.[0-9]+
+
+multiple IPs:
+- ^(192\.168\.1\.1|172\.16\.0\.1)$
+
+You can also specify a subnet:
+- 192.168.1.0/24
+
+Note: Commas or spaces are not allowed to separate IPs or networks.</string>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <item>
+ <property name="text">
+ <string>LAN</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>127.0.0.0/8</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192.168.0.0/24</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192.168.1.0/24</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192.168.2.0/24</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>192.168.0.0/16</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>169.254.0.0/16</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>172.16.0.0/12</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>10.0.0.0/8</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>::1/128</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>fc00::/7</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>ff00::/8</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>fe80::/10</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>fd00::/8</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tabWidgetPage3">
+ <attribute name="icon">
+ <iconset theme="document-properties">
+ <normaloff>.</normaloff>.</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>List of domains/IPs</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_5">
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="dstListNetsCheck">
+ <property name="text">
+ <string>To this list of network ranges</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="dstListIPsCheck">
+ <property name="text">
+ <string>To this list of IPs</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <item>
+ <widget class="QPushButton" name="selectIPsListButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="document-open">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="dstListIPsLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>Select a directory with files containing list of IPs to block or allow:</p><p>1.2.3.4.5</p><p>1.2.3.4.6</p><p>.</p><p>etc.</p><p>One IP per line. Empty lines or started with # are ignored.</p></body></html></string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="dstListsCheck">
+ <property name="text">
+ <string>To this list of domains</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <item>
+ <widget class="QPushButton" name="selectNetsListButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="document-open">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="dstListNetsLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>Select a directory with files containing list of network ranges to block or allow:</p><p>1.2.3.0/24</p><p>80.34.56.0/20</p><p>.</p><p>etc.<br/></p><p>One Network Range per line. Empty lines or started with # are ignored.</p></body></html></string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="QPushButton" name="selectListButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="document-open">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="dstListsLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>Select a directory with lists of domains to block or allow.</p><p>Put inside that directory files with any extension containing lists of domains.</p><p><br/>The format of each entry of a list is as follow (hosts format):</p><p>127.0.0.1 www.domain.com</p><p>or </p><p>0.0.0.0 www.domain.com</p><p>Empty lines or started with # are ignored.</p></body></html></string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="dstListRegexpCheck">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>To this list of domains
+(regular expressions)</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <item>
+ <widget class="QPushButton" name="selectListRegexpButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="document-open">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="dstRegexpListsLine">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip">
+ <string><html><head/><body><p>Select a directory with files containing regular expressions of domains to block or allow:</p><p>.*\.example\.com</p><p>You can also use a domain as is: &quot;example.com&quot; , and it'll match whatever.example.com, whatever.example.com.localdomain, etc.</p><p>One domain per line. Empty lines or started with # are ignored.</p></body></html></string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>More</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_6">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="sensitiveCheck">
+ <property name="toolTip">
+ <string><html><head/><body><p>By default, the field of the rules are case-insensitive, i.e., if a process tries to access gOOgle.CoM and you have a rule to Deny .*google.com, the connection will be blocked.<br/></p><p>If you check this box, you have to specify the exact string (domain, executable, command line) that you want to filter.</p></body></html></string>
+ </property>
+ <property name="text">
+ <string>Case-sensitive</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Maximum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item row="9" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <spacer name="horizontalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Apply|QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Reset</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="8" column="1">
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="1">
+ <widget class="QLabel" name="statusLabel">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Node</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="nodesCombo"/>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="nodeApplyAllCheck">
+ <property name="text">
+ <string>Apply rule to all nodes</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="3" column="1">
+ <widget class="QCheckBox" name="enableCheck">
+ <property name="text">
+ <string>Enable</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QCheckBox" name="precedenceCheck">
+ <property name="toolTip">
+ <string>If checked, this rule will take precedence over the rest of the rules. No others rules will be checked after this one.
+
+You must name the rule in such manner that it'll be checked first, because they're checked in alphabetical order. For example:
+
+[x] Priority - 000-priority-rule
+[ ] Priority - 001-less-priority-rule</string>
+ </property>
+ <property name="text">
+ <string>Priority rule</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="ruleNameEdit">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>The rules are checked in alphabetical order, so you can name them accordingly to prioritize them.
+
+000-allow-localhost
+001-deny-broadcast
+...</string>
+ </property>
+ <property name="placeholderText">
+ <string>leave blank to autocreate</string>
+ </property>
+ <property name="clearButtonEnabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>StatsDialog</class>
+ <widget class="QDialog" name="StatsDialog">
+ <property name="windowModality">
+ <enum>Qt::NonModal</enum>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>863</width>
+ <height>600</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>220</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="windowTitle">
+ <string>OpenSnitch Network Statistics</string>
+ </property>
+ <property name="windowIcon">
+ <iconset resource="resources.qrc">
+ <normaloff>:/pics/icon-white.svg</normaloff>
+ <normalon>:/pics/icon-white.svg</normalon>
+ <disabledoff>:/pics/icon-white.svg</disabledoff>
+ <disabledon>:/pics/icon-white.svg</disabledon>
+ <activeoff>:/pics/icon-white.svg</activeoff>
+ <activeon>:/pics/icon-white.svg</activeon>:/pics/icon-white.svg</iconset>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="modal">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>2</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>4</number>
+ </property>
+ <item row="3" column="0">
+ <widget class="QFrame" name="navToolBar">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_17">
+ <property name="leftMargin">
+ <number>4</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>4</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Filter</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboAction">
+ <item>
+ <property name="text">
+ <string>-</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Allow</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-default">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Deny</string>
+ </property>
+ <property name="icon">
+ <iconset theme="emblem-important">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Reject</string>
+ </property>
+ <property name="icon">
+ <iconset theme="window-close">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="filterLine">
+ <property name="text">
+ <string notr="true"/>
+ </property>
+ <property name="frame">
+ <bool>true</bool>
+ </property>
+ <property name="placeholderText">
+ <string>Ex.: firefox</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Minimum</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="prevButton">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="go-previous">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelRowsCount">
+ <property name="text">
+ <string>0</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="nextButton">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="go-next">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="limitCombo">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>50</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>100</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>200</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>300</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string/>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cmdCleanSql">
+ <property name="toolTip">
+ <string>Delete all intercepted events</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="edit-clear-all">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="helpButton">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="help-browser">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item row="0" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <item>
+ <widget class="QPushButton" name="saveButton">
+ <property name="toolTip">
+ <string>Save to CSV.</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="document-save">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+S</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="prefsButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="preferences-system">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="newRuleButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>32</horstretch>
+ <verstretch>32</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip">
+ <string>Create a new rule</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="document-new">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_9">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>60</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="nodeLabel">
+ <property name="text">
+ <string><html><head/><body><p><span style=" font-size:11pt; font-weight:600;">hostname - 192.168.1.1</span></p></body></html></string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>Ubuntu</family>
+ <pointsize>11</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="statusLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>11</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>-</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="startButton">
+ <property name="toolTip">
+ <string>Start or Stop interception</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="media-playback-start">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="tabPosition">
+ <enum>QTabWidget::North</enum>
+ </property>
+ <property name="tabShape">
+ <enum>QTabWidget::Rounded</enum>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <property name="documentMode">
+ <bool>true</bool>
+ </property>
+ <widget class="QWidget" name="tab">
+ <attribute name="icon">
+ <iconset theme="view-sort-ascending">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Events</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_8">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>4</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_16">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="GenericTableView" name="eventsTable">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="autoScroll">
+ <bool>false</bool>
+ </property>
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="horizontalScrollMode">
+ <enum>QAbstractItemView::ScrollPerPixel</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollBar" name="connectionsTableScrollBar">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_8">
+ <attribute name="icon">
+ <iconset theme="network-workgroup">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Nodes</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <item>
+ <widget class="QPushButton" name="cmdNodesBack">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="go-previous">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="nodesLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="GenericTableView" name="nodesTable">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="autoScroll">
+ <bool>false</bool>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="verticalHeaderStretchLastSection">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollBar" name="verticalScrollBar">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_3">
+ <attribute name="icon">
+ <iconset theme="address-book-new">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Rules</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="2" column="0">
+ <widget class="QSplitter" name="rulesSplitter">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="QTreeWidget" name="rulesTreePanel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <attribute name="headerVisible">
+ <bool>false</bool>
+ </attribute>
+ <column>
+ <property name="text">
+ <string notr="true">1</string>
+ </property>
+ </column>
+ <item>
+ <property name="text">
+ <string>Application rules</string>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="icon">
+ <iconset theme="system-run">
+ <normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset>
+ </property>
+ <item>
+ <property name="text">
+ <string>Permanent</string>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="icon">
+ <iconset theme="security-medium">
+ <normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Temporary</string>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ </font>
+ </property>
+ <property name="icon">
+ <iconset theme="edit-clear">
+ <normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset>
+ </property>
+ </item>
+ </item>
+ <item>
+ <property name="text">
+ <string>Nodes</string>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>10</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="icon">
+ <iconset theme="system">
+ <normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</normaloff>../../../../../../../../../../../../../../gustavo-iniguez-goya/opensnitch/ui/opensnitch/res</iconset>
+ </property>
+ </item>
+ </widget>
+ <widget class="QWidget" name="horizontalLayoutWidget">
+ <layout class="QHBoxLayout" name="horizontalLayout_19">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="GenericTableView" name="rulesTable">
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollBar" name="rulesScrollBar">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <item>
+ <widget class="QComboBox" name="comboRulesFilter">
+ <item>
+ <property name="text">
+ <string>All applications</string>
+ </property>
+ <property name="icon">
+ <iconset theme="system-run">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Permanent</string>
+ </property>
+ <property name="icon">
+ <iconset theme="security-medium">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Temporary</string>
+ </property>
+ <property name="icon">
+ <iconset theme="edit-clear">
+ <normaloff>.</normaloff>.</iconset>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_11">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>30</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <layout class="QHBoxLayout" name="rulesToolbarLayout">
+ <item>
+ <widget class="QPushButton" name="cmdRulesBack">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="go-previous">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="enableRuleCheck">
+ <property name="text">
+ <string>enable</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="editRuleButton">
+ <property name="toolTip">
+ <string>Edit rule</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="accessories-text-editor">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="delRuleButton">
+ <property name="toolTip">
+ <string>Delete rule</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="edit-delete">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="nodeRuleLabel">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="ruleLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_4">
+ <attribute name="icon">
+ <iconset theme="computer">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Hosts</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QPushButton" name="cmdHostsBack">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="go-previous">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="hostsLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="GenericTableView" name="hostsTable">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderStretchLastSection">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollBar" name="hostsScrollBar">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_7">
+ <attribute name="icon">
+ <iconset theme="system-run">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Applications</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QPushButton" name="cmdProcsBack">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="go-previous">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cmdProcDetails">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="system-search">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="procsLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="GenericTableView" name="procsTable">
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>true</bool>
+ </property>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderStretchLastSection">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollBar" name="procsScrollBar">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2">
+ <attribute name="icon">
+ <iconset theme="emblem-web">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Addresses</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QPushButton" name="cmdAddrsBack">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="go-previous">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="addrsLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="GenericTableView" name="addrTable">
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderStretchLastSection">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollBar" name="addrsScrollBar">
+ <property name="maximum">
+ <number>50</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_5">
+ <attribute name="icon">
+ <iconset theme="network-wired">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Ports</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_6">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <widget class="QPushButton" name="cmdPortsBack">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="go-previous">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="portsLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_14">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="GenericTableView" name="portsTable">
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderStretchLastSection">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollBar" name="portsScrollBar">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_6">
+ <attribute name="icon">
+ <iconset theme="system-users">
+ <normaloff>../../../../../../../../.designer/backup</normaloff>../../../../../../../../.designer/backup</iconset>
+ </attribute>
+ <attribute name="title">
+ <string>Users</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="QPushButton" name="cmdUsersBack">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="icon">
+ <iconset theme="go-previous">
+ <normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../../../../../../../../../../../../../../../../../.designer/backup</iconset>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="usersLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="GenericTableView" name="usersTable">
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderStretchLastSection">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QScrollBar" name="usersScrollBar">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="statsLayout">
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Connections</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="consLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>-</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_7">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Dropped</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="droppedLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>-</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Uptime</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="uptimeLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>-</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Rules</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="rulesLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>-</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_10">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_6">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <weight>75</weight>
+ <italic>false</italic>
+ <bold>true</bold>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>Version</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="daemonVerLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <family>DejaVu Sans</family>
+ <pointsize>8</pointsize>
+ <kerning>true</kerning>
+ </font>
+ </property>
+ <property name="text">
+ <string>-</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>GenericTableView</class>
+ <extends>QTableView</extends>
+ <header>customwidgets.generictableview</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="resources.qrc"/>
+ </resources>
+ <connections/>
+</ui>
--- /dev/null
+from PyQt5 import QtWidgets, QtGui, QtCore
+
+from datetime import datetime, timedelta
+from threading import Thread, Lock, Event
+import grpc
+import os
+import sys
+import json
+
+path = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(path)
+
+from opensnitch import ui_pb2
+from opensnitch import ui_pb2_grpc
+
+from opensnitch.dialogs.prompt import PromptDialog
+from opensnitch.dialogs.stats import StatsDialog
+
+from opensnitch.notifications import DesktopNotifications
+from opensnitch.nodes import Nodes
+from opensnitch.config import Config
+from opensnitch.version import version
+from opensnitch.database import Database
+from opensnitch.utils import Utils, CleanerTask
+from opensnitch.utils import Message
+
+class UIService(ui_pb2_grpc.UIServicer, QtWidgets.QGraphicsObject):
+ _new_remote_trigger = QtCore.pyqtSignal(str, ui_pb2.PingRequest)
+ _node_actions_trigger = QtCore.pyqtSignal(dict)
+ _update_stats_trigger = QtCore.pyqtSignal(str, str, ui_pb2.PingRequest)
+ _version_warning_trigger = QtCore.pyqtSignal(str, str)
+ _status_change_trigger = QtCore.pyqtSignal(bool)
+ _notification_callback = QtCore.pyqtSignal(ui_pb2.NotificationReply)
+ _show_message_trigger = QtCore.pyqtSignal(str, str, int)
+
+ # .desktop filename located under /usr/share/applications/
+ DESKTOP_FILENAME = "opensnitch_ui.desktop"
+
+ def __init__(self, app, on_exit):
+ super(UIService, self).__init__()
+
+
+ self.MENU_ENTRY_STATS = QtCore.QCoreApplication.translate("contextual_menu", "Statistics")
+ self.MENU_ENTRY_FW_ENABLE = QtCore.QCoreApplication.translate("contextual_menu", "Enable")
+ self.MENU_ENTRY_FW_DISABLE = QtCore.QCoreApplication.translate("contextual_menu", "Disable")
+ self.MENU_ENTRY_HELP = QtCore.QCoreApplication.translate("contextual_menu", "Help")
+ self.MENU_ENTRY_CLOSE = QtCore.QCoreApplication.translate("contextual_menu", "Close")
+
+ # set of actions that must be performed on the main thread
+ self.NODE_ADD = 0
+ self.NODE_UPDATE = 1
+ self.NODE_DELETE = 2
+ self.ADD_RULE = 3
+ self.DELETE_RULE = 4
+
+ self._cfg = Config.init()
+ self._db = Database.instance()
+ db_file=self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY)
+ db_status, db_error = self._db.initialize(
+ dbtype=self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY),
+ dbfile=db_file
+ )
+ if db_status is False:
+ Message.ok(
+ QtCore.QCoreApplication.translate("preferences", "Warning"),
+ QtCore.QCoreApplication.translate("preferences",
+ "The DB is corrupted and it's not safe to continue.<br>\
+ Remove, backup or recover the file before continuing.<br><br>\
+ Corrupted database file: {0}".format(db_file)),
+ QtWidgets.QMessageBox.Warning)
+ sys.exit(-1)
+
+ self._db_sqlite = self._db.get_db()
+ self._last_ping = None
+ self._version_warning_shown = False
+ self._asking = False
+ self._connected = False
+ self._fw_enabled = False
+ self._path = os.path.abspath(os.path.dirname(__file__))
+ self._app = app
+ self._on_exit = on_exit
+ self._exit = False
+ self._msg = QtWidgets.QMessageBox()
+ self._remote_lock = Lock()
+ self._remote_stats = {}
+
+ self._desktop_notifications = DesktopNotifications()
+ self._setup_interfaces()
+ self._setup_icons()
+ self._prompt_dialog = PromptDialog(appicon=self.white_icon)
+ self._stats_dialog = StatsDialog(dbname="general", db=self._db, appicon=self.white_icon)
+ self._setup_tray()
+ self._setup_slots()
+
+ self._nodes = Nodes.instance()
+
+ self._last_stats = {}
+ self._last_items = {
+ 'hosts':{},
+ 'procs':{},
+ 'addrs':{},
+ 'ports':{},
+ 'users':{}
+ }
+
+ self._show_gui_if_tray_not_available()
+
+ self._cleaner = None
+ if self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST):
+ self._start_db_cleaner()
+
+ # https://gist.github.com/pklaus/289646
+ def _setup_interfaces(self):
+ namestr, outbytes = Utils.get_interfaces()
+ self._interfaces = {}
+ for i in range(0, outbytes, 40):
+ name = namestr[i:i+16].split(b'\0', 1)[0]
+ addr = namestr[i+20:i+24]
+ self._interfaces[name] = "%d.%d.%d.%d" % (int(addr[0]), int(addr[1]), int(addr[2]), int(addr[3]))
+
+ def _setup_slots(self):
+ # https://stackoverflow.com/questions/40288921/pyqt-after-messagebox-application-quits-why
+ self._app.setQuitOnLastWindowClosed(False)
+ self._version_warning_trigger.connect(self._on_diff_versions)
+ self._new_remote_trigger.connect(self._on_new_remote)
+ self._node_actions_trigger.connect(self._on_node_actions)
+ self._update_stats_trigger.connect(self._on_update_stats)
+ self._status_change_trigger.connect(self._on_status_changed)
+ self._stats_dialog._shown_trigger.connect(self._on_stats_dialog_shown)
+ self._stats_dialog._status_changed_trigger.connect(self._on_stats_status_changed)
+ self._stats_dialog.settings_saved.connect(self._on_settings_saved)
+ self._show_message_trigger.connect(self._show_systray_message)
+
+ def _setup_icons(self):
+ self.off_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-off.png"))
+ self.off_icon = QtGui.QIcon()
+ self.off_icon.addPixmap(self.off_image, QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.white_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-white.svg"))
+ self.white_icon = QtGui.QIcon()
+ self.white_icon.addPixmap(self.white_image, QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.red_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-red.png"))
+ self.red_icon = QtGui.QIcon()
+ self.red_icon.addPixmap(self.red_image, QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.pause_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-pause.png"))
+ self.pause_icon = QtGui.QIcon()
+ self.pause_icon.addPixmap(self.pause_image, QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.alert_image = QtGui.QPixmap(os.path.join(self._path, "res/icon-alert.png"))
+ self.alert_icon = QtGui.QIcon()
+ self.alert_icon.addPixmap(self.alert_image, QtGui.QIcon.Normal, QtGui.QIcon.Off)
+
+ self._app.setWindowIcon(self.white_icon)
+ # NOTE: only available since pyqt 5.7
+ if hasattr(self._app, "setDesktopFileName"):
+ self._app.setDesktopFileName(self.DESKTOP_FILENAME)
+
+ def _setup_tray(self):
+ self._tray = QtWidgets.QSystemTrayIcon(self.off_icon)
+ self._tray.show()
+
+ self._menu = QtWidgets.QMenu()
+ self._tray.setContextMenu(self._menu)
+ self._tray.activated.connect(self._on_tray_icon_activated)
+
+ self._menu.addAction(self.MENU_ENTRY_STATS).triggered.connect(self._show_stats_dialog)
+ self._menu_enable_fw = self._menu.addAction(self.MENU_ENTRY_FW_DISABLE)
+ self._menu_enable_fw.setEnabled(False)
+ self._menu_enable_fw.triggered.connect(self._on_enable_interception_clicked)
+ self._menu.addAction(self.MENU_ENTRY_HELP).triggered.connect(
+ lambda: QtGui.QDesktopServices.openUrl(QtCore.QUrl(Config.HELP_CONFIG_URL))
+ )
+ self._menu.addAction(self.MENU_ENTRY_CLOSE).triggered.connect(self._on_close)
+
+ def _show_gui_if_tray_not_available(self):
+ """If the system tray is not available or ready, show the GUI after
+ 10s. This delay helps to skip showing up the GUI when DEs' autologin is on.
+ """
+ tray = self._tray
+ gui = self._stats_dialog
+ def __show_gui():
+ if not tray.isSystemTrayAvailable():
+ gui.show()
+
+ QtCore.QTimer.singleShot(10000, __show_gui)
+
+ def _on_tray_icon_activated(self, reason):
+ if reason == QtWidgets.QSystemTrayIcon.Trigger or reason == QtWidgets.QSystemTrayIcon.MiddleClick:
+ if self._stats_dialog.isVisible() and not self._stats_dialog.isMinimized():
+ self._stats_dialog.hide()
+ elif self._stats_dialog.isVisible() and self._stats_dialog.isMinimized() and not self._stats_dialog.isMaximized():
+ self._stats_dialog.hide()
+ self._stats_dialog.showNormal()
+ elif self._stats_dialog.isVisible() and self._stats_dialog.isMinimized() and self._stats_dialog.isMaximized():
+ self._stats_dialog.hide()
+ self._stats_dialog.showMaximized()
+ else:
+ self._stats_dialog.show()
+
+ def _on_close(self):
+ self._exit = True
+ self._tray.setIcon(self.off_icon)
+ self._app.processEvents()
+ self._nodes.stop_notifications()
+ self._nodes.update_all(Nodes.OFFLINE)
+ self._db.vacuum()
+ self._db.optimize()
+ self._db.close()
+ self._stop_db_cleaner()
+ self._on_exit()
+
+ def _show_stats_dialog(self):
+ if self._connected and self._fw_enabled:
+ self._tray.setIcon(self.white_icon)
+ self._stats_dialog.show()
+
+ @QtCore.pyqtSlot(bool)
+ def _on_stats_status_changed(self, enabled):
+ self._update_fw_status(enabled)
+
+ @QtCore.pyqtSlot(bool)
+ def _on_status_changed(self, enabled):
+ self._set_daemon_connected(enabled)
+
+ @QtCore.pyqtSlot(str, str)
+ def _on_diff_versions(self, daemon_ver, ui_ver):
+ if self._version_warning_shown == False:
+ self._msg.setIcon(QtWidgets.QMessageBox.Warning)
+ self._msg.setWindowTitle("OpenSnitch version mismatch!")
+ self._msg.setText(("You are running version <b>%s</b> of the daemon, while the UI is at version " + \
+ "<b>%s</b>, they might not be fully compatible.") % (daemon_ver, ui_ver))
+ self._msg.setStandardButtons(QtWidgets.QMessageBox.Ok)
+ self._msg.show()
+ self._version_warning_shown = True
+
+ @QtCore.pyqtSlot(str, str, ui_pb2.PingRequest)
+ def _on_update_stats(self, proto, addr, request):
+ main_need_refresh, details_need_refresh = self._populate_stats(self._db, proto, addr, request.stats)
+ is_local_request = self._is_local_request(proto, addr)
+ self._stats_dialog.update(is_local_request, request.stats, main_need_refresh or details_need_refresh)
+
+ @QtCore.pyqtSlot(str, ui_pb2.PingRequest)
+ def _on_new_remote(self, addr, request):
+ self._remote_stats[addr] = {
+ 'last_ping': datetime.now(),
+ 'dialog': StatsDialog(address=addr, dbname=addr, db=self._db)
+ }
+ self._remote_stats[addr]['dialog'].daemon_connected = True
+ self._remote_stats[addr]['dialog'].update(addr, request.stats)
+ self._remote_stats[addr]['dialog'].show()
+
+ @QtCore.pyqtSlot()
+ def _on_stats_dialog_shown(self):
+ if self._connected:
+ if self._fw_enabled:
+ self._tray.setIcon(self.white_icon)
+ else:
+ self._tray.setIcon(self.pause_icon)
+ else:
+ self._tray.setIcon(self.off_icon)
+
+ @QtCore.pyqtSlot(ui_pb2.NotificationReply)
+ def _on_notification_reply(self, reply):
+ if reply.code == ui_pb2.ERROR:
+ self._tray.showMessage("Error",
+ reply.data,
+ QtWidgets.QSystemTrayIcon.Information,
+ 5000)
+
+ def _on_remote_stats_menu(self, address):
+ self._remote_stats[address]['dialog'].show()
+
+ @QtCore.pyqtSlot(str, str, int)
+ def _show_systray_message(self, title, body, icon):
+ if self._desktop_notifications.are_enabled():
+ timeout = self._cfg.getInt(Config.DEFAULT_TIMEOUT_KEY, 15)
+
+ if self._desktop_notifications.is_available() and self._cfg.getInt(Config.NOTIFICATIONS_TYPE, 1) == Config.NOTIFICATION_TYPE_SYSTEM:
+ self._desktop_notifications.show(title, body, os.path.join(self._path, "res/icon-white.svg"))
+ else:
+ self._tray.showMessage(title, body, icon, timeout * 1000)
+
+ if icon == QtWidgets.QSystemTrayIcon.NoIcon:
+ self._tray.setIcon(self.alert_icon)
+
+ def _on_enable_interception_clicked(self):
+ self._enable_interception(self._fw_enabled)
+
+ @QtCore.pyqtSlot()
+ def _on_settings_saved(self):
+ if self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST):
+ if self._cleaner != None:
+ self._stop_db_cleaner()
+ self._start_db_cleaner()
+ elif self._cfg.getBool(Config.DEFAULT_DB_PURGE_OLDEST) == False and self._cleaner != None:
+ self._stop_db_cleaner()
+
+ def _stop_db_cleaner(self):
+ if self._cleaner != None:
+ self._cleaner.stop()
+ self._cleaner = None
+
+ def _start_db_cleaner(self):
+ def _cleaner_task(db):
+ oldest = self._cfg.getInt(self._cfg.DEFAULT_DB_MAX_DAYS, 1)
+ db.purge_oldest(oldest)
+
+ interval = self._cfg.getInt(self._cfg.DEFAULT_DB_PURGE_INTERVAL, 5)
+ self._cleaner = CleanerTask(interval, _cleaner_task)
+ self._cleaner.start()
+
+ def _update_fw_status(self, enabled):
+ """_update_fw_status updates the status of the menu entry
+ to disable or enable the firewall of the daemon.
+ """
+ self._fw_enabled = enabled
+ if self._connected == False:
+ return
+
+ self._stats_dialog.update_interception_status(enabled)
+ if enabled:
+ self._tray.setIcon(self.white_icon)
+ self._menu_enable_fw.setText(self.MENU_ENTRY_FW_DISABLE)
+ else:
+ self._tray.setIcon(self.pause_icon)
+ self._menu_enable_fw.setText(self.MENU_ENTRY_FW_ENABLE)
+
+ def _set_daemon_connected(self, connected):
+ """_set_daemon_connected only updates the connection status of the daemon(s),
+ regardless if the fw is enabled or not.
+ There're 3 states:
+ - daemon connected
+ - daemon not connected
+ - daemon connected and firewall enabled/disabled
+ """
+ self._stats_dialog.daemon_connected = connected
+ self._connected = connected
+
+ # if there're more than 1 node, override connection status
+ if self._nodes.count() >= 1:
+ self._connected = True
+ self._stats_dialog.daemon_connected = True
+
+ if self._nodes.count() == 1:
+ self._menu_enable_fw.setEnabled(True)
+
+ if self._nodes.count() == 0 or self._nodes.count() > 1:
+ self._menu_enable_fw.setEnabled(False)
+
+ self._stats_dialog.update_status()
+
+ if self._connected:
+ self._tray.setIcon(self.white_icon)
+ else:
+ self._fw_enabled = False
+ self._tray.setIcon(self.off_icon)
+
+ def _enable_interception(self, enable):
+ if self._connected == False:
+ return
+ if self._nodes.count() == 0:
+ self._tray.showMessage("No nodes connected",
+ "",
+ QtWidgets.QSystemTrayIcon.Information,
+ 5000)
+ return
+ if self._nodes.count() > 1:
+ print("enable interception for all nodes not supported yet")
+ return
+
+ if enable:
+ nid, noti = self._nodes.stop_interception(_callback=self._notification_callback)
+ else:
+ nid, noti = self._nodes.start_interception(_callback=self._notification_callback)
+
+ self._fw_enabled = not enable
+
+ self._stats_dialog._status_changed_trigger.emit(not enable)
+
+ def _is_local_request(self, proto, addr):
+ if proto == "unix":
+ return True
+
+ elif proto == "ipv4" or proto == "ipv6":
+ for name, ip in self._interfaces.items():
+ if addr == ip:
+ return True
+
+ return False
+
+ def _get_peer(self, peer):
+ """
+ server -> client
+ 127.0.0.1:50051 -> ipv4:127.0.0.1:52032
+ [::]:50051 -> ipv6:[::1]:59680
+ 0.0.0.0:50051 -> ipv6:[::1]:59654
+ """
+ return self._nodes.get_addr(peer)
+
+ def _delete_node(self, peer):
+ try:
+ proto, addr = self._get_peer(peer)
+ if addr in self._last_stats:
+ del self._last_stats[addr]
+ for table in self._last_items:
+ if addr in self._last_items[table]:
+ del self._last_items[table][addr]
+
+ self._nodes.update(proto, addr, Nodes.OFFLINE)
+ self._nodes.delete(peer)
+ self._stats_dialog.update(True, None, True)
+ except Exception as e:
+ print("_delete_node() exception:", e)
+
+ def _populate_stats(self, db, proto, addr, stats):
+ main_need_refresh = False
+ details_need_refresh = False
+ try:
+ if db == None:
+ print("populate_stats() db None")
+ return main_need_refresh, details_need_refresh
+
+ _node = self._nodes.get_node(proto+":"+addr)
+ if _node == None:
+ return main_need_refresh, details_need_refresh
+
+ # TODO: move to nodes.add_node()
+ version = _node['data'].version if _node != None else ""
+ hostname = _node['data'].name if _node != None else ""
+ db.insert("nodes",
+ "(addr, status, hostname, daemon_version, daemon_uptime, " \
+ "daemon_rules, cons, cons_dropped, version, last_connection)",
+ (addr, Nodes.ONLINE, hostname, stats.daemon_version, str(timedelta(seconds=stats.uptime)),
+ stats.rules, stats.connections, stats.dropped,
+ version, datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
+
+ if addr not in self._last_stats:
+ self._last_stats[addr] = []
+
+ db.transaction()
+ for event in stats.events:
+ if event.unixnano in self._last_stats[addr]:
+ continue
+ main_need_refresh=True
+ db.insert("connections",
+ "(time, node, action, protocol, src_ip, src_port, dst_ip, dst_host, dst_port, uid, pid, process, process_args, process_cwd, rule)",
+ (str(datetime.fromtimestamp(event.unixnano/1000000000)), "%s:%s" % (proto, addr), event.rule.action,
+ event.connection.protocol, event.connection.src_ip, str(event.connection.src_port),
+ event.connection.dst_ip, event.connection.dst_host, str(event.connection.dst_port),
+ str(event.connection.user_id), str(event.connection.process_id),
+ event.connection.process_path, " ".join(event.connection.process_args),
+ event.connection.process_cwd, event.rule.name),
+ action_on_conflict="IGNORE"
+ )
+ self._nodes.update_rule_time(
+ str(datetime.fromtimestamp(event.unixnano/1000000000)),
+ event.rule.name,
+ "%s:%s" % (proto, addr)
+ )
+ db.commit()
+
+ details_need_refresh = self._populate_stats_details(db, addr, stats)
+ self._last_stats[addr] = []
+ for event in stats.events:
+ self._last_stats[addr].append(event.unixnano)
+ except Exception as e:
+ print("_populate_stats() exception: ", e)
+
+ return main_need_refresh, details_need_refresh
+
+ def _populate_stats_details(self, db, addr, stats):
+ need_refresh = False
+ changed = self._populate_stats_events(db, addr, stats, "hosts", ("what", "hits"), (1,2), stats.by_host.items())
+ if changed: need_refresh = True
+ changed = self._populate_stats_events(db, addr, stats, "procs", ("what", "hits"), (1,2), stats.by_executable.items())
+ if changed: need_refresh = True
+ changed = self._populate_stats_events(db, addr, stats, "addrs", ("what", "hits"), (1,2), stats.by_address.items())
+ if changed: need_refresh = True
+ changed = self._populate_stats_events(db, addr, stats, "ports", ("what", "hits"), (1,2), stats.by_port.items())
+ if changed: need_refresh = True
+ changed = self._populate_stats_events(db, addr, stats, "users", ("what", "hits"), (1,2), stats.by_uid.items())
+ if changed: need_refresh = True
+
+ return need_refresh
+
+ def _populate_stats_events(self, db, addr, stats, table, colnames, cols, items):
+ fields = []
+ values = []
+ need_refresh = False
+ try:
+ if addr not in self._last_items[table].keys():
+ self._last_items[table][addr] = {}
+ if items == self._last_items[table][addr]:
+ return need_refresh
+
+ for row, event in enumerate(items):
+ if event in self._last_items[table][addr]:
+ continue
+ need_refresh = True
+ what, hits = event
+ # FIXME: this is suboptimal
+ # BUG: there can be users with same id on different machines but with different names
+ if table == "users":
+ what = Utils.get_user_id(what)
+ fields.append(what)
+ values.append(int(hits))
+ # FIXME: default action on conflict is to replace. If there're multiple nodes connected,
+ # stats are painted once per node on each update.
+ if need_refresh:
+ db.insert_batch(table, colnames, cols, fields, values)
+
+ self._last_items[table][addr] = items
+ except Exception as e:
+ print("details exception: ", e)
+
+ return need_refresh
+
+ def _overwrite_nodes_config(self, node_config):
+ _default_action = self._cfg.getInt(self._cfg.DEFAULT_ACTION_KEY)
+ temp_cfg = json.loads(node_config)
+ try:
+ if _default_action == Config.ACTION_DENY_IDX:
+ temp_cfg['DefaultAction'] = Config.ACTION_DENY
+ else:
+ temp_cfg['DefaultAction'] = Config.ACTION_ALLOW
+
+ node_config = json.dumps(temp_cfg)
+ except Exception as e:
+ print("error parsing node's configuration:", e)
+
+ return node_config
+
+ @QtCore.pyqtSlot(dict)
+ def _on_node_actions(self, kwargs):
+ if kwargs['action'] == self.NODE_ADD:
+ n = self._nodes.add(kwargs['peer'], kwargs['node_config'])
+ if n != None:
+ self._status_change_trigger.emit(True)
+ # if there're more than one node, we can't update the status
+ # based on the fw status, only if the daemon is running or not
+ if self._nodes.count() <= 1:
+ self._update_fw_status(kwargs['node_config'].isFirewallRunning)
+ else:
+ self._update_fw_status(True)
+ elif kwargs['action'] == self.ADD_RULE:
+ rule = kwargs['rule']
+ proto, addr = self._get_peer(kwargs['peer'])
+ self._nodes.add_rule((datetime.now().strftime("%Y-%m-%d %H:%M:%S")),
+ "{0}:{1}".format(proto, addr),
+ rule.name, str(rule.enabled), str(rule.precedence), rule.action, rule.duration,
+ rule.operator.type, str(rule.operator.sensitive), rule.operator.operand,
+ rule.operator.data)
+ elif kwargs['action'] == self.DELETE_RULE:
+ self._db.delete_rule(kwargs['name'], kwargs['addr'])
+
+ elif kwargs['action'] == self.NODE_DELETE:
+ self._delete_node(kwargs['peer'])
+
+ def Ping(self, request, context):
+ try:
+ self._last_ping = datetime.now()
+ if Utils.check_versions(request.stats.daemon_version):
+ self._version_warning_trigger.emit(request.stats.daemon_version, version)
+
+ proto, addr = self._get_peer(context.peer())
+ # do not update db here, do it on the main thread
+ self._update_stats_trigger.emit(proto, addr, request)
+ #else:
+ # with self._remote_lock:
+ # # XXX: disable this option for now
+ # # opening several dialogs only updates one of them.
+ # if addr not in self._remote_stats:
+ # self._new_remote_trigger.emit(addr, request)
+ # else:
+ # self._populate_stats(self._remote_stats[addr]['dialog'].get_db(), proto, addr, request.stats)
+ # self._remote_stats[addr]['dialog'].update(addr, request.stats)
+
+ except Exception as e:
+ print("Ping exception: ", e)
+
+ return ui_pb2.PingReply(id=request.id)
+
+ def AskRule(self, request, context):
+ #def callback(ntf, action, connection):
+ # TODO
+
+ #if self._desktop_notifications.support_actions():
+ # self._desktop_notifications.ask(request, callback)
+
+ # TODO: allow connections originated from ourselves: os.getpid() == request.pid)
+ self._asking = True
+ proto, addr = self._get_peer(context.peer())
+ rule, timeout_triggered = self._prompt_dialog.promptUser(request, self._is_local_request(proto, addr), context.peer())
+ self._last_ping = datetime.now()
+ self._asking = False
+ if rule == None:
+ return None
+
+ if timeout_triggered:
+ _title = request.process_path
+ if _title == "":
+ _title = "%s:%d (%s)" % (request.dst_host if request.dst_host != "" else request.dst_ip, request.dst_port, request.protocol)
+
+
+ node_text = "" if self._is_local_request(proto, addr) else "on node {0}:{1}".format(proto, addr)
+ self._show_message_trigger.emit(_title,
+ "{0} action applied {1}\nCommand line: {2}"
+ .format(rule.action, node_text, " ".join(request.process_args)),
+ QtWidgets.QSystemTrayIcon.NoIcon)
+
+
+ if rule.duration in Config.RULES_DURATION_FILTER:
+ self._node_actions_trigger.emit(
+ {
+ 'action': self.DELETE_RULE,
+ 'name': rule.name,
+ 'addr': context.peer()
+ }
+ )
+ else:
+ self._node_actions_trigger.emit(
+ {
+ 'action': self.ADD_RULE,
+ 'peer': context.peer(),
+ 'rule': rule
+ }
+ )
+
+ return rule
+
+ def Subscribe(self, node_config, context):
+ """
+ Accept and collect nodes. It keeps a connection open with each
+ client, in order to send them notifications.
+
+ @doc: https://grpc.github.io/grpc/python/grpc.html#service-side-context
+ """
+ # if the exit mark is set, don't accept new connections.
+ # db vacuum operation may take a lot of time to complete.
+ if self._exit:
+ return
+ try:
+ self._node_actions_trigger.emit({
+ 'action': self.NODE_ADD,
+ 'peer': context.peer(),
+ 'node_config': node_config
+ })
+ # force events processing, to add the node ^ before the
+ # Notifications() call arrives.
+ self._app.processEvents()
+
+ proto, addr = self._get_peer(context.peer())
+ if self._is_local_request(proto, addr) == False:
+ self._show_message_trigger.emit(
+ QtCore.QCoreApplication.translate("stats", "New node connected"),
+ "({0})".format(context.peer()),
+ QtWidgets.QSystemTrayIcon.Information)
+ except Exception as e:
+ print("[Notifications] exception adding new node:", e)
+ context.cancel()
+
+ node_config.config = self._overwrite_nodes_config(node_config.config)
+
+ return node_config
+
+ def Notifications(self, node_iter, context):
+ """
+ Accept and collect nodes. It keeps a connection open with each
+ client, in order to send them notifications.
+
+ @doc: https://grpc.github.io/grpc/python/grpc.html#service-side-context
+ @doc: https://grpc.io/docs/what-is-grpc/core-concepts/
+ """
+ proto, addr = self._get_peer(context.peer())
+ _node = self._nodes.get_node("%s:%s" % (proto, addr))
+ if _node == None:
+ return
+
+ stop_event = Event()
+ def _on_client_closed():
+ stop_event.set()
+ self._node_actions_trigger.emit(
+ {'action': self.NODE_DELETE,
+ 'peer': context.peer(),
+ })
+
+ self._status_change_trigger.emit(False)
+ # TODO: handle the situation when a node disconnects, and the
+ # remaining node has the fw disabled.
+ #if self._nodes.count() == 1:
+ # nd = self._nodes.get_nodes()
+ # if nd[0].get_config().isFirewallRunning:
+
+ if self._is_local_request(proto, addr) == False:
+ self._show_message_trigger.emit("node exited",
+ "({0})".format(context.peer()),
+ QtWidgets.QSystemTrayIcon.Information)
+
+ context.add_callback(_on_client_closed)
+
+ # TODO: move to notifications.py
+ def new_node_message():
+ print("new node connected, listening for client responses...", addr)
+
+ while self._exit == False:
+ try:
+ if stop_event.is_set():
+ break
+ in_message = next(node_iter)
+ if in_message == None:
+ continue
+
+ self._nodes.reply_notification(addr, in_message)
+ except StopIteration:
+ print("[Notifications] Node {0} exited".format(addr))
+ break
+ except grpc.RpcError as e:
+ print("[Notifications] grpc exception new_node_message(): ", addr, in_message)
+ except Exception as e:
+ print("[Notifications] unexpected exception new_node_message(): ", addr, e, in_message)
+
+ read_thread = Thread(target=new_node_message)
+ read_thread.daemon = True
+ read_thread.start()
+
+ while self._exit == False:
+ if stop_event.is_set():
+ break
+
+ try:
+ noti = _node['notifications'].get()
+ if noti != None:
+ _node['notifications'].task_done()
+ yield noti
+ except Exception as e:
+ print("[Notifications] exception getting notification from queue:", addr, e)
+ context.cancel()
+
+ return node_iter
+
--- /dev/null
+
+from PyQt5 import QtCore, QtWidgets, QtGui
+from opensnitch.version import version
+from opensnitch.database import Database
+from opensnitch.config import Config
+from threading import Thread, Event
+import pwd
+import socket
+import fcntl
+import struct
+import array
+import os, sys, glob
+
+class AsnDB():
+ __instance = None
+ asndb = None
+
+ @staticmethod
+ def instance():
+ if AsnDB.__instance == None:
+ AsnDB.__instance = AsnDB()
+ return AsnDB.__instance
+
+ def __init__(self):
+ self.ASN_AVAILABLE = True
+ self.load()
+
+ def is_available(self):
+ return self.ASN_AVAILABLE
+
+ def load(self):
+ """Load the ASN DB from disk.
+
+ It'll try to load it from user's opensnitch directory if these file exist:
+ - ~/.config/opensnitch/ipasn_db.dat.gz
+ - ~/.config/opensnitch/asnames.json
+ Otherwise it'll try to load it from python3-pyasn package.
+ """
+ try:
+ if self.asndb != None:
+ return
+
+ import pyasn
+
+ IPASN_DB_PATH = os.path.expanduser('~/.config/opensnitch/ipasn_db.dat.gz')
+ # .gz not supported for asnames
+ AS_NAMES_FILE_PATH = os.path.expanduser('~/.config/opensnitch/asnames.json')
+
+ # if the user hasn't downloaded an updated ipasn db, use the one
+ # shipped with the python3-pyasn package
+ if os.path.isfile(IPASN_DB_PATH) == False:
+ IPASN_DB_PATH = '/usr/lib/python3/dist-packages/data/ipasn_20140513_v12.dat.gz'
+ if os.path.isfile(AS_NAMES_FILE_PATH) == False:
+ AS_NAMES_FILE_PATH = '/usr/lib/python3/dist-packages/data/asnames.json'
+
+ print("using IPASN DB:", IPASN_DB_PATH)
+ self.asndb = pyasn.pyasn(IPASN_DB_PATH, as_names_file=AS_NAMES_FILE_PATH)
+ except Exception as e:
+ self.ASN_AVAILABLE = False
+ print("exception loading ipasn db:", e)
+ print("Install python3-pyasn to display IP's network name.")
+
+
+ def lookup(self, ip):
+ """Lookup the IP in the ASN DB.
+
+ Return the net range and the prefix if found, otherwise nothing.
+ """
+ try:
+ return self.asndb.lookup(ip)
+ except Exception:
+ return "", ""
+
+ def get_as_name(self, asn):
+ """Get the ASN name given a network range.
+
+ Return the name of the network if found, otherwise nothing.
+ """
+ try:
+ asname = self.asndb.get_as_name(asn)
+ if asname == None:
+ asname = ""
+ return asname
+ except Exception:
+ return ""
+
+ def get_asn(self, ip):
+ try:
+ asn, prefix = self.lookup(ip)
+ return self.get_as_name(asn)
+ except Exception:
+ return ""
+
+class Themes():
+ """Change GUI's appearance using qt-material lib.
+ https://github.com/UN-GCPDS/qt-material
+ """
+ THEMES_PATH = [
+ os.path.expanduser("~/.config/opensnitch/"),
+ os.path.dirname(sys.modules[__name__].__file__)
+ ]
+ __instance = None
+
+ AVAILABLE = False
+ try:
+ from qt_material import apply_stylesheet as qtmaterial_apply_stylesheet
+ from qt_material import list_themes as qtmaterial_themes
+ AVAILABLE = True
+ except Exception:
+ print("Themes not available. Install qt-material if you want to change GUI's appearance: pip3 install qt-material.")
+
+ @staticmethod
+ def instance():
+ if Themes.__instance == None:
+ Themes.__instance = Themes()
+ return Themes.__instance
+
+ def __init__(self):
+ self._cfg = Config.get()
+ theme = self._cfg.getInt(self._cfg.DEFAULT_THEME, 0)
+
+ def available(self):
+ return Themes.AVAILABLE
+
+ def get_saved_theme(self):
+ if not Themes.AVAILABLE:
+ return 0, ""
+
+ theme = self._cfg.getSettings(self._cfg.DEFAULT_THEME)
+ if theme != "" and theme != None:
+ # 0 == System
+ return self.list_themes().index(theme)+1, theme
+ return 0, ""
+
+ def save_theme(self, theme_idx, theme):
+ if not Themes.AVAILABLE:
+ return
+
+ if theme_idx == 0:
+ self._cfg.setSettings(self._cfg.DEFAULT_THEME, "")
+ else:
+ self._cfg.setSettings(self._cfg.DEFAULT_THEME, theme)
+
+ def load_theme(self, app):
+ if not Themes.AVAILABLE:
+ return
+
+ try:
+ theme_idx, theme_name = self.get_saved_theme()
+ if theme_name != "":
+ invert = "light" in theme_name
+ print("Using theme:", theme_idx, theme_name, "inverted:", invert)
+ # TODO: load {theme}.xml.extra and .xml.css for further
+ # customizations.
+ Themes.qtmaterial_apply_stylesheet(app, theme=theme_name, invert_secondary=invert)
+ except Exception as e:
+ print("Themes.load_theme() exception:", e)
+
+ def list_local_themes(self):
+ themes = []
+ if not Themes.AVAILABLE:
+ return themes
+
+ try:
+ for tdir in self.THEMES_PATH:
+ themes += glob.glob(tdir + "/themes/*.xml")
+ except Exception:
+ pass
+ finally:
+ return themes
+
+ def list_themes(self):
+ themes = self.list_local_themes()
+ if not Themes.AVAILABLE:
+ return themes
+
+ themes += Themes.qtmaterial_themes()
+ return themes
+
+class CleanerTask(Thread):
+ interval = 1
+ stop_flag = None
+ callback = None
+
+ def __init__(self, _interval, _callback):
+ Thread.__init__(self, name="cleaner_db_thread")
+ self.interval = _interval * 60
+ self.stop_flag = Event()
+ self.callback = _callback
+ self._cfg = Config.init()
+
+ # We need to instantiate a new QsqlDatabase object with a unique name,
+ # because it's not thread safe:
+ # "A connection can only be used from within the thread that created it."
+ # https://doc.qt.io/qt-5/threads-modules.html#threads-and-the-sql-module
+ # The filename and type is the same, the one chosen by the user.
+ self.db = Database("db-cleaner-connection")
+ self.db_status, db_error = self.db.initialize(
+ dbtype=self._cfg.getInt(self._cfg.DEFAULT_DB_TYPE_KEY),
+ dbfile=self._cfg.getSettings(self._cfg.DEFAULT_DB_FILE_KEY)
+ )
+
+ def run(self):
+ if self.db_status == False:
+ return
+ while not self.stop_flag.is_set():
+ self.stop_flag.wait(self.interval)
+ self.callback(self.db)
+
+ def stop(self):
+ self.stop_flag.set()
+ self.db.close()
+
+class QuickHelp():
+ @staticmethod
+ def show(help_str):
+ QtWidgets.QToolTip.showText(QtGui.QCursor.pos(), help_str)
+
+class Utils():
+ @staticmethod
+ def check_versions(daemon_version):
+ lMayor, lMinor, lPatch = version.split(".")
+ rMayor, rMinor, rPatch = daemon_version.split(".")
+ return lMayor != rMayor or (lMayor == rMayor and lMinor != rMinor)
+
+ @staticmethod
+ def get_user_id(uid):
+ pw_name = uid
+ try:
+ pw_name = pwd.getpwuid(int(uid)).pw_name + " (" + uid + ")"
+ except Exception:
+ #pw_name += " (error)"
+ pass
+
+ return pw_name
+
+ @staticmethod
+ def get_interfaces():
+ max_possible = 128 # arbitrary. raise if needed.
+ bytes = max_possible * 32
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ names = array.array('B', b'\0' * bytes)
+ outbytes = struct.unpack('iL', fcntl.ioctl(
+ s.fileno(),
+ 0x8912, # SIOCGIFCONF
+ struct.pack('iL', bytes, names.buffer_info()[0])
+ ))[0]
+ return names.tobytes(), outbytes
+
+class Message():
+
+ @staticmethod
+ def ok(title, message, icon):
+ msgBox = QtWidgets.QMessageBox()
+ msgBox.setWindowFlags(msgBox.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
+ msgBox.setText("<b>{0}</b><br><br>{1}".format(title, message))
+ msgBox.setIcon(icon)
+ msgBox.setModal(True)
+ msgBox.setStandardButtons(QtWidgets.QMessageBox.Ok)
+ msgBox.exec_()
+
+ @staticmethod
+ def yes_no(title, message, icon):
+ msgBox = QtWidgets.QMessageBox()
+ msgBox.setWindowFlags(msgBox.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
+ msgBox.setText(title)
+ msgBox.setIcon(icon)
+ msgBox.setModal(True)
+ msgBox.setInformativeText(message)
+ msgBox.setStandardButtons(QtWidgets.QMessageBox.Cancel | QtWidgets.QMessageBox.Yes)
+ msgBox.setDefaultButton(QtWidgets.QMessageBox.Cancel)
+ return msgBox.exec_()
+
+class FileDialog():
+
+ @staticmethod
+ def save(parent):
+ options = QtWidgets.QFileDialog.Options()
+ fileName, _ = QtWidgets.QFileDialog.getSaveFileName(parent, "", "","All Files (*)", options=options)
+ return fileName
+
+ @staticmethod
+ def select(parent):
+ options = QtWidgets.QFileDialog.Options()
+ fileName, _ = QtWidgets.QFileDialog.getOpenFileName(parent, "", "","All Files (*)", options=options)
+ return fileName
+
+ @staticmethod
+ def select_dir(parent, current_dir):
+ options = QtWidgets.QFileDialog.Options()
+ fileName = QtWidgets.QFileDialog.getExistingDirectory(parent, "", current_dir, options)
+ return fileName
+
--- /dev/null
+version = '1.5.8'
--- /dev/null
+grpcio-tools>=1.10.1
+pyinotify==0.9.6
+unicode_slugify==0.1.3
+pyqt5>=5.6
+protobuf
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ width="64"
+ height="64"
+ viewBox="0 0 12.698413 12.698413"
+ version="1.1"
+ id="svg8"
+ inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+ sodipodi:docname="opensnitch-ui.svg"
+ inkscape:export-filename="64x64/opensnitch-ui.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="9.75"
+ inkscape:cx="21.74359"
+ inkscape:cy="31.794872"
+ inkscape:document-units="px"
+ inkscape:current-layer="g4133"
+ showgrid="true"
+ inkscape:window-width="1600"
+ inkscape:window-height="839"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ units="px"
+ inkscape:pagecheckerboard="0"
+ inkscape:showpageshadow="2"
+ inkscape:deskcolor="#d1d1d1"
+ showguides="true">
+ <sodipodi:guide
+ position="13.436869,10.958536"
+ orientation="0,-1"
+ id="guide291"
+ inkscape:locked="false" />
+ <sodipodi:guide
+ position="13.233163,1.6160318"
+ orientation="0,-1"
+ id="guide293"
+ inkscape:locked="false" />
+ <inkscape:grid
+ type="xygrid"
+ id="grid295" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Capa 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-284.30001)">
+ <g
+ id="g4133"
+ transform="matrix(0.9182611,0,0,0.9182611,-19.582232,24.4642)">
+ <path
+ style="fill:#232629;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.287462px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 23.93778,289.55608 0.922529,-2.47895 2.563056,-0.57549 2.395943,-1.27803 2.491924,1.17578 0.401143,2.50266 1.620734,1.26491 0.381256,1.78538 -0.666013,1.62872 -1.326357,0.85434 -9.105041,0.0948 -1.530218,-1.13976 -0.31061,-1.71767 0.780558,-1.56852 z"
+ id="path1481"
+ sodipodi:nodetypes="ccccccccccccccc"
+ inkscape:export-filename="/home/ga/Proiektuak/opensnitch/gui/opensnitch/ui/lib/python3.9/site-packages/opensnitch/res/icon-white.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96" />
+ <path
+ style="fill:#fbffff;fill-opacity:1;stroke:none;stroke-width:0.0267051;stroke-opacity:1"
+ d="m 23.924347,295.01517 c -1.190403,-0.17151 -2.160364,-1.04049 -2.48066,-2.22233 -0.06228,-0.22986 -0.06956,-0.30524 -0.06956,-0.7212 0,-0.41596 0.0072,-0.49134 0.06956,-0.72117 0.147237,-0.54329 0.408851,-0.99654 0.795944,-1.37896 0.324557,-0.32066 0.785409,-0.60618 1.154881,-0.71557 l 0.103224,-0.0303 0.01629,-0.29557 c 0.07143,-1.29674 0.972433,-2.37494 2.27078,-2.71736 0.229851,-0.0607 0.312438,-0.0688 0.716539,-0.0696 0.257818,-6.1e-4 0.509198,0.0123 0.577176,0.0292 0.120416,0.0303 0.120441,0.0303 0.198793,-0.0679 0.155946,-0.19524 0.509186,-0.51209 0.74373,-0.66706 0.551463,-0.36443 1.11147,-0.54697 1.774575,-0.57842 0.589067,-0.0277 1.121425,0.0806 1.650413,0.33595 0.72776,0.35149 1.243577,0.8611 1.599369,1.58011 0.255831,0.51699 0.367014,1.04428 0.341349,1.61884 l -0.01332,0.2989 0.217937,0.14177 c 0.119863,0.078 0.346565,0.26788 0.503782,0.42196 0.88975,0.87202 1.218873,2.08704 0.893766,3.29955 -0.132616,0.49457 -0.500547,1.11835 -0.881945,1.49513 -0.364404,0.36002 -1.001057,0.73363 -1.4664,0.86052 -0.492709,0.13439 -0.431399,0.13271 -4.634625,0.12938 -2.15745,-0.002 -3.994154,-0.0135 -4.081564,-0.0261 z m 8.365743,-0.89453 c 0.59725,-0.15778 1.128615,-0.51689 1.486943,-1.00491 0.147801,-0.20133 0.33126,-0.60206 0.39973,-0.87313 0.08448,-0.33442 0.08466,-0.85621 4.07e-4,-1.18961 -0.203279,-0.80452 -0.831632,-1.50173 -1.597272,-1.77233 -0.153083,-0.0541 -0.232971,-0.0955 -0.224805,-0.11652 0.252703,-0.65059 0.224901,-1.36673 -0.07754,-1.99749 -0.264858,-0.55243 -0.669292,-0.9522 -1.225766,-1.21167 -0.399047,-0.18607 -0.636305,-0.23736 -1.097982,-0.23736 -0.319298,0 -0.430775,0.0109 -0.61795,0.0601 -0.739241,0.19424 -1.358854,0.68643 -1.680928,1.33522 l -0.07478,0.15063 -0.122961,-0.0614 c -0.228775,-0.11421 -0.565847,-0.2025 -0.834911,-0.21871 -0.932118,-0.0562 -1.838882,0.55372 -2.13689,1.4371 -0.168757,0.50024 -0.151725,0.9779 0.05358,1.50243 0.01377,0.035 -0.0089,0.0394 -0.156728,0.03 -0.208357,-0.0132 -0.547143,0.0503 -0.81323,0.15241 -0.54704,0.20974 -1.043227,0.72489 -1.228933,1.27596 -0.172171,0.51087 -0.161028,0.97577 0.03539,1.47692 0.212384,0.54181 0.73321,1.03044 1.29576,1.21563 0.113249,0.0371 0.270424,0.0783 0.349276,0.0916 0.08114,0.0136 1.857609,0.0221 4.092874,0.0195 l 3.949506,-0.004 z m -5.761885,-1.40118 c -0.576304,-0.34169 -1.047827,-0.63319 -1.047827,-0.64782 0,-0.0148 0.471523,-0.30613 1.047827,-0.64783 l 1.047829,-0.62116 0.0074,0.42212 0.0074,0.42218 h 1.718841 1.718847 v 0.42469 0.42469 h -1.718876 -1.718849 l -0.0074,0.42218 -0.0074,0.42217 z m 2.350898,-2.34355 v -0.42777 h -1.719512 -1.719514 v -0.42468 -0.42471 h 1.718847 1.718849 l 0.0074,-0.42217 0.0074,-0.42216 1.047812,0.6212 c 0.5763,0.34168 1.051205,0.63124 1.055344,0.64353 0.0041,0.0124 -0.443196,0.28902 -0.99408,0.61507 -0.550883,0.32599 -1.028812,0.61 -1.062059,0.63111 l -0.06046,0.0382 z"
+ id="path826-6"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+</svg>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<component type="desktop-application">
+ <id>io.github.evilsocket.opensnitch</id>
+
+ <name>OpenSnitch</name>
+ <summary>GNU/Linux interactive application firewall</summary>
+
+ <metadata_license>FTL</metadata_license>
+ <project_license>GPL-3.0-or-later</project_license>
+
+ <supports>
+ <control>pointing</control>
+ <control>keyboard</control>
+ <control>touch</control>
+ </supports>
+
+ <description>
+ <p>
+ Whenever a program tries to establish a new connection, it'll prompt the user to allow or deny it.
+ </p>
+ <p>
+ The user can decide if block the outgoing connection based on properties of the connection: by port, by uid, by dst ip, by program or a combination of them. These rules can last forever, until the app restart or just one time.
+ </p>
+ <p>
+ The GUI allows the user to view live outgoing connections, as well as search by process, user, host or port.
+ </p>
+ <p>
+ OpenSnitch can also work as a system-wide domains blocker, by using lists of domains, list of IPs or list of regular expressions.
+ </p>
+ </description>
+
+ <categories>
+ <category>System</category>
+ <category>Security</category>
+ <category>Monitor</category>
+ <category>Network</category>
+ </categories>
+
+ <icon type="stock">opensnitch-ui</icon>
+ <url type="homepage">https://github.com/evilsocket/opensnitch</url>
+ <url type="bugtracker">https://github.com/evilsocket/opensnitch/issues</url>
+ <url type="help">https://github.com/evilsocket/opensnitch/wiki</url>
+ <launchable type="desktop-id">opensnitch_ui.desktop</launchable>
+ <screenshots>
+ <screenshot type="default">
+ <image>https://user-images.githubusercontent.com/2742953/85205382-6ba9cb00-b31b-11ea-8e9a-bd4b8b05a236.png</image>
+ </screenshot>
+ <screenshot>
+ <image>https://user-images.githubusercontent.com/2742953/217039798-3477c6c2-d64f-4eea-89af-cd94ee77cff4.png</image>
+ </screenshot>
+ <screenshot>
+ <image>https://user-images.githubusercontent.com/2742953/99863173-3987e800-2b9d-11eb-93f2-fe3121b18c51.png</image>
+ </screenshot>
+ </screenshots>
+ <content_rating type="oars-1.0" />
+</component>
--- /dev/null
+[Desktop Entry]
+Exec=opensnitch-ui
+Icon=opensnitch-ui
+Type=Service
+X-KDE-ServiceTypes=SystemSettingsExternalApp
+Name=OpenSnitch Firewall
+Comment=OpenSnitch Firewall Graphical Interface
+X-KDE-Keywords=system,firewall,policies,security,polkit,policykit,douane
+X-KDE-Autostart-after=panel
--- /dev/null
+[Desktop Entry]
+Type=Application
+Name=OpenSnitch
+Exec=/bin/sh -c "pkill -15 opensnitch-ui; opensnitch-ui"
+Icon=opensnitch-ui
+GenericName=OpenSnitch Firewall
+GenericName[hu]=OpenSnitch-tűzfal
+GenericName[nb]=OpenSnitch brannmur
+Comment=Interactive application firewall
+Comment[es]=Firewall de aplicaciones
+Comment[hu]=Alkalmazási tűzfal
+Comment[nb]=Interaktiv programbrannmur
+Terminal=false
+NoDisplay=false
+Categories=System;Security;Monitor;Network;
+Keywords=system;firewall;policies;security;polkit;policykit;
+X-GNOME-Autostart-Delay=3
+X-GNOME-Autostart-enabled=true
--- /dev/null
+from setuptools import setup, find_packages
+
+import os
+import sys
+
+path = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(path)
+
+from opensnitch.version import version
+
+setup(name='opensnitch-ui',
+ version=version,
+ description='Prompt service and UI for the opensnitch interactive firewall application.',
+ long_description='GUI for the opensnitch interactive firewall application\n\
+opensnitch-ui is a GUI for opensnitch written in Python.\n\
+It allows the user to view live outgoing connections, as well as search\n\
+to make connections.\n\
+.\n\
+The user can decide if block the outgoing connection based on properties of\n\
+the connection: by port, by uid, by dst ip, by program or a combination\n\
+of them.\n\
+.\n\
+These rules can last forever, until the app restart or just one time.',
+ url='https://github.com/evilsocket/opensnitch',
+ author='Simone "evilsocket" Margaritelli',
+ author_email='evilsocket@protonmail.com',
+ license='GPL-3.0',
+ packages=find_packages(),
+ include_package_data = True,
+ package_data={'': ['*.*']},
+ data_files=[('/usr/share/applications', ['resources/opensnitch_ui.desktop']),
+ ('/usr/share/kservices5', ['resources/kcm_opensnitch.desktop']),
+ ('/usr/share/icons/hicolor/scalable/apps', ['resources/icons/opensnitch-ui.svg']),
+ ('/usr/share/icons/hicolor/48x48/apps', ['resources/icons/48x48/opensnitch-ui.png']),
+ ('/usr/share/icons/hicolor/64x64/apps', ['resources/icons/64x64/opensnitch-ui.png']),
+ ('/usr/share/metainfo', ['resources/io.github.evilsocket.opensnitch.appdata.xml'])],
+ scripts = [ 'bin/opensnitch-ui' ],
+ zip_safe=False)
--- /dev/null
+GUI unit tests.
+
+We use pytest [0] to pytest-qt [1] to test GUI code.
+
+To run the tests: `cd tests; pytest -v`
+
+TODO:
+ - test service class (Service.py)
+ - test events window (stats.py):
+ - The size of the window must be saved on close, and restored when opening it again.
+ - Columns width of every view must be saved and restored properly.
+ - On the Events tab, clicking on the Node, Process or Rule column must jump to the detailed view of the selected item.
+ - When entering into a detail view:
+ - the results limit configured must be respected (that little button on the bottom right of every tab).
+ - must apply the proper SQL query for every detailed view.
+ - When going back from a detail view:
+ - The SQL query must be restored.
+ - Test rules context menu actions.
+ - Test select rows and copy them to the clipboard (ctrl+c).
+
+
+0. https://docs.pytest.org/en/6.2.x/
+1. https://pytest-qt.readthedocs.io/en/latest/intro.html
--- /dev/null
+
+from opensnitch.database import Database
+from opensnitch.config import Config
+from opensnitch.nodes import Nodes
+
+# grpc object
+class ClientConfig:
+ version = "1.2.3"
+ name = "bla"
+ logLevel = 0
+ isFirewallRunning = False
+ rules = []
+ config = '''{
+ "Server":{
+ "Address": "unix:///tmp/osui.sock",
+ "LogFile": "/var/log/opensnitchd.log"
+ },
+ "DefaultAction": "deny",
+ "DefaultDuration": "once",
+ "InterceptUnknown": false,
+ "ProcMonitorMethod": "ebpf",
+ "LogLevel": 0,
+ "Firewall": "iptables",
+ "Stats": {
+ "MaxEvents": 150,
+ "MaxStats": 50
+ }
+ }
+ '''
+
+class Connection:
+ protocol = "tcp"
+ src_ip = "127.0.0.1"
+ src_port = "12345"
+ dst_ip = "127.0.0.1"
+ dst_host = "localhost"
+ dst_port = "54321"
+ user_id = 1000
+ process_id = 9876
+ process_path = "/bin/cmd"
+ process_cwd = "/tmp"
+ process_args = "/bin/cmd --parm1 test"
+ process_env = []
+
+db = Database.instance()
+db.initialize()
+Config.init()
+
+nodes = Nodes.instance()
+nodes._nodes["unix:/tmp/osui.sock"] = {
+ 'data': ClientConfig
+}
--- /dev/null
+#
+# pytest -v tests/dialogs/test_ruleseditor.py
+#
+import os
+import time
+import json
+from PyQt5 import QtCore, QtWidgets, QtGui
+
+from opensnitch.config import Config
+from opensnitch.dialogs.preferences import PreferencesDialog
+
+class TestPreferences():
+
+ @classmethod
+ def reset_settings(self):
+ try:
+ os.remove(os.environ['HOME'] + "/.config/opensnitch/settings.conf")
+ except Exception:
+ pass
+
+ @classmethod
+ def setup_method(self):
+ white_icon = QtGui.QIcon("../res/icon-white.svg")
+ self.reset_settings()
+ self.prefs = PreferencesDialog(appicon=white_icon)
+ self.prefs.show()
+
+ def run(self, qtbot):
+ def handle_dialog():
+ qtbot.mouseClick(self.prefs.applyButton, QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.prefs.acceptButton, QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(500, handle_dialog)
+ self.prefs.exec_()
+
+ def test_save_popups_settings(self, qtbot):
+ """ Test saving UI related settings.
+ """
+ qtbot.addWidget(self.prefs)
+
+ self.prefs.comboUIAction.setCurrentIndex(Config.ACTION_ALLOW_IDX)
+ self.prefs.comboUITarget.setCurrentIndex(2)
+ self.prefs.comboUIDuration.setCurrentIndex(4)
+ self.prefs.comboUIDialogPos.setCurrentIndex(2)
+ self.prefs.spinUITimeout.setValue(30)
+ self.prefs.showAdvancedCheck.setChecked(True)
+ self.prefs.uidCheck.setChecked(True)
+
+ self.run(qtbot)
+
+ assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_ACTION_KEY) == Config.ACTION_ALLOW_IDX and self.prefs.comboUIAction.currentText() == Config.ACTION_ALLOW
+ assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_TARGET_KEY) == 2
+ assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_DURATION_KEY) == 4
+ assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_TIMEOUT_KEY) == 30
+ assert self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_POPUP_POSITION) == 2
+ assert self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_POPUP_ADVANCED) == True
+ assert self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_POPUP_ADVANCED_UID) == True
+
+ def test_save_ui_settings(self, qtbot):
+ self.prefs.checkUIRules.setChecked(True)
+ self.prefs.comboUIRules.setCurrentIndex(1)
+ self.prefs.checkHideNode.setChecked(False)
+ self.prefs.checkHideProto.setChecked(False)
+
+ self.run(qtbot)
+
+ assert self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_IGNORE_RULES) == True and self.prefs._cfg.getInt(self.prefs._cfg.DEFAULT_IGNORE_TEMPORARY_RULES) == 1
+ cols = self.prefs._cfg.getSettings(Config.STATS_SHOW_COLUMNS)
+ assert cols == ['0','2','3','5','6']
+
+ def test_save_node_settings(self, qtbot, capsys):
+ self.prefs.comboNodeAction.setCurrentIndex(Config.ACTION_ALLOW_IDX)
+ self.prefs.comboNodeMonitorMethod.setCurrentIndex(2)
+ self.prefs.comboNodeLogLevel.setCurrentIndex(5)
+ self.prefs.checkInterceptUnknown.setChecked(True)
+ self.prefs.tabWidget.setCurrentIndex(self.prefs.TAB_NODES)
+ self.prefs._node_needs_update = True
+
+ self.run(qtbot)
+
+ assert len(self.prefs._notifications_sent) == 1
+ for n in self.prefs._notifications_sent:
+ conf = json.loads(self.prefs._notifications_sent[n].data)
+ assert conf['InterceptUnknown'] == True
+ assert conf['ProcMonitorMethod'] == "audit"
+ assert conf['LogLevel'] == 5
+ assert conf['DefaultAction'] == "allow"
+
+# TODO: click on the QMessageDialog
+#
+# def test_save_db_settings(self, qtbot, monkeypatch, capsys):
+# self.prefs.comboDBType.setCurrentIndex(1)
+# self.prefs.dbLabel.setText('/tmp/test.db')
+#
+# def handle_dialog():
+# qtbot.mouseClick(self.prefs.applyButton, QtCore.Qt.LeftButton)
+# # after saving the settings, a warning dialog must appear, informing
+# # the user to restart the GUI
+# time.sleep(.5)
+# msgbox = QtWidgets.QApplication.activeModalWidget()
+# try:
+# assert msgbox != None
+# okBtn = msgbox.button(QtWidgets.QMessageBox.Ok)
+# qtbot.mouseClick(okBtn, QtCore.Qt.LeftButton)
+# except Exception as e:
+# print("test_save_db_Settings() exception:", e)
+# qtbot.mouseClick(self.prefs.acceptButton, QtCore.Qt.LeftButton)
+#
+# QtCore.QTimer.singleShot(500, handle_dialog)
+# self.prefs.exec_()
+
+# assert self.prefs._cfg.getInt(Config.DEFAULT_DB_TYPE_KEY) == 1
+# assert self.prefs._cfg.getSettings(Config.DEFAULT_DB_FILE_KEY) == '/tmp/test.db'
+
+ def test_load_ui_settings(self, qtbot, capsys):
+ """ reTest saved settings (load_settings()).
+ On dialog show up the widgets must be configured properly, with the settings
+ configured in previous tests.
+ """
+ self.prefs.checkUIRules.setChecked(False)
+ self.prefs.comboUIRules.setCurrentIndex(0)
+ self.prefs.comboUITarget.setCurrentIndex(0)
+ self.prefs.comboUIDuration.setCurrentIndex(0)
+ self.prefs.checkHideNode.setChecked(True)
+ self.prefs.checkHideProto.setChecked(True)
+
+ def handle_dialog():
+ qtbot.mouseClick(self.prefs.cancelButton, QtCore.Qt.LeftButton)
+ QtCore.QTimer.singleShot(500, handle_dialog)
+
+ self.prefs.exec_()
+ self.prefs.show()
+
+ print(self.prefs._cfg.getBool(self.prefs._cfg.DEFAULT_IGNORE_RULES))
+
+ assert self.prefs.comboUIAction.currentIndex() == Config.ACTION_ALLOW_IDX and self.prefs.comboUIAction.currentText() == Config.ACTION_ALLOW
+ assert self.prefs.checkUIRules.isChecked() == True
+ assert self.prefs.comboUIRules.currentIndex() == 1
+ assert self.prefs.comboUITarget.currentIndex() == 2
+ assert self.prefs.comboUIDuration.currentIndex() == 4 and self.prefs.comboUIDuration.currentText() == Config.DURATION_30m
+ assert self.prefs.comboUIDialogPos.currentIndex() == 2
+ assert self.prefs.spinUITimeout.value() == 30
--- /dev/null
+#
+# pytest -v tests/dialogs/test_ruleseditor.py
+#
+
+import json
+from PyQt5 import QtCore, QtWidgets, QtGui
+
+from opensnitch.config import Config
+from opensnitch.dialogs.ruleseditor import RulesEditorDialog
+
+class TestRulesEditor():
+
+ @classmethod
+ def setup_method(self):
+ white_icon = QtGui.QIcon("../res/icon-white.svg")
+ self.rd = RulesEditorDialog(appicon=white_icon)
+ self.rd.show()
+ self.rd.ruleNameEdit.setText("xxx")
+ self.rd.nodesCombo.addItem("unix:/tmp/osui.sock")
+ self.rd.nodesCombo.setCurrentText("unix:/tmp/osui.sock")
+ self.rd._nodes._nodes["unix:/tmp/osui.sock"] = {}
+
+ def test_rule_no_fields(self, qtbot):
+ """ Test that rules without fields selected cannot be created.
+ """
+ qtbot.addWidget(self.rd)
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+ assert self.rd.statusLabel.text() != ""
+
+ def test_fields_empty(self, qtbot):
+ """ Test that fields cannot be empty.
+ """
+
+ self.rd.pidCheck.setChecked(True)
+ self.rd.pidLine.setText("")
+ result, error = self.rd._save_rule()
+ assert error != None
+
+ self.rd.pidCheck.setChecked(False)
+ self.rd.uidCheck.setChecked(True)
+ self.rd.uidLine.setText("")
+ result, error = self.rd._save_rule()
+ assert error != None
+
+ self.rd.uidCheck.setChecked(False)
+ self.rd.procCheck.setChecked(True)
+ self.rd.procLine.setText("")
+ result, error = self.rd._save_rule()
+ assert error != None
+
+ self.rd.procCheck.setChecked(False)
+ self.rd.cmdlineCheck.setChecked(True)
+ self.rd.cmdlineLine.setText("")
+ result, error = self.rd._save_rule()
+ assert error != None
+
+ self.rd.cmdlineCheck.setChecked(False)
+ self.rd.dstPortCheck.setChecked(True)
+ self.rd.dstPortLine.setText("")
+ result, error = self.rd._save_rule()
+ assert error != None
+
+ self.rd.dstPortCheck.setChecked(False)
+ self.rd.dstHostCheck.setChecked(True)
+ self.rd.dstHostLine.setText("")
+ result, error = self.rd._save_rule()
+ assert error != None
+
+ self.rd.dstHostCheck.setChecked(False)
+ self.rd.dstListsCheck.setChecked(True)
+ self.rd.dstListsLine.setText("")
+ result, error = self.rd._save_rule()
+ assert error != None
+
+ def test_add_basic_rule(self, qtbot):
+ """ Test adding a basic rule.
+ """
+
+ self.rd.statusLabel.setText("")
+ self.rd.ruleNameEdit.setText("www.test.com")
+ self.rd.dstHostCheck.setChecked(True)
+ self.rd.dstHostLine.setText("www.test.com")
+ self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_UNTIL_RESTART))
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ assert self.rd.statusLabel.text() == ""
+ assert self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()).next() == True
+ assert self.rd._old_rule_name == "www.test.com"
+ # after adding a rule, we enter into editing mode, to allow editing it
+ # without closing the dialog.
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
+ assert self.rd.rule.operator.operand == "dest.host"
+ assert self.rd.rule.operator.data == "www.test.com"
+ assert self.rd.rule.duration == Config.DURATION_UNTIL_RESTART
+
+ def test_add_complex_rule(self, qtbot):
+ """ Test add complex rule.
+ """
+ self.rd.WORK_MODE = self.rd.ADD_RULE
+ self.rd._reset_state()
+ self.rd.statusLabel.setText("")
+ self.rd.ruleNameEdit.setText("www.test-complex.com")
+ self.rd.dstHostCheck.setChecked(True)
+ self.rd.dstHostLine.setText("www.test-complex.com")
+ self.rd.dstPortCheck.setChecked(True)
+ self.rd.dstPortLine.setText("443")
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ assert self.rd.statusLabel.text() == ""
+ assert self.rd._db.get_rule("www.test-complex.com", self.rd.nodesCombo.currentText()).next() == True
+ assert self.rd._old_rule_name == "www.test-complex.com"
+ # after adding a rule, we enter into editing mode, to allow editing it
+ # without closing the dialog.
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.rule.operator.type == Config.RULE_TYPE_LIST
+ assert self.rd.rule.operator.operand == Config.RULE_TYPE_LIST
+ json_rule = json.loads(self.rd.rule.operator.data)
+ assert json_rule[0]['type'] == "simple"
+ assert json_rule[0]['operand'] == "dest.port"
+ assert json_rule[0]['data'] == "443"
+ assert json_rule[1]['type'] == "simple"
+ assert json_rule[1]['operand'] == "dest.host"
+ assert json_rule[1]['data'] == "www.test-complex.com"
+
+ def test_add_reject_rule(self, qtbot):
+ """ Test adding new rule with action "reject".
+ """
+
+ self.rd.statusLabel.setText("")
+ self.rd.ruleNameEdit.setText("www.test-reject.com")
+ self.rd.dstHostCheck.setChecked(True)
+ self.rd.dstHostLine.setText("www.test-reject.com")
+ self.rd.actionRejectRadio.setChecked(True)
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ assert self.rd.statusLabel.text() == ""
+ assert self.rd._db.get_rule("www.test-reject.com", self.rd.nodesCombo.currentText()).next() == True
+ assert self.rd._old_rule_name == "www.test-reject.com"
+ # after adding a rule, we enter into editing mode, to allow editing it
+ # without closing the dialog.
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
+ assert self.rd.rule.operator.operand == "dest.host"
+ assert self.rd.rule.operator.data == "www.test-reject.com"
+ assert self.rd.rule.action == Config.ACTION_REJECT
+
+ def test_add_deny_rule(self, qtbot):
+ """ Test adding new rule with action "deny".
+ """
+
+ self.rd.statusLabel.setText("")
+ self.rd.ruleNameEdit.setText("www.test-deny.com")
+ self.rd.dstHostCheck.setChecked(True)
+ self.rd.dstHostLine.setText("www.test-deny.com")
+ self.rd.actionDenyRadio.setChecked(True)
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ assert self.rd.statusLabel.text() == ""
+ assert self.rd._db.get_rule("www.test-deny.com", self.rd.nodesCombo.currentText()).next() == True
+ assert self.rd._old_rule_name == "www.test-deny.com"
+ # after adding a rule, we enter into editing mode, to allow editing it
+ # without closing the dialog.
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
+ assert self.rd.rule.operator.operand == "dest.host"
+ assert self.rd.rule.operator.data == "www.test-deny.com"
+ assert self.rd.rule.action == Config.ACTION_DENY
+
+ def test_add_allow_rule(self, qtbot):
+ """ Test adding new rule with action "allow".
+ """
+
+ self.rd.statusLabel.setText("")
+ self.rd.ruleNameEdit.setText("www.test-allow.com")
+ self.rd.dstHostCheck.setChecked(True)
+ self.rd.dstHostLine.setText("www.test-allow.com")
+ self.rd.actionAllowRadio.setChecked(True)
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ assert self.rd.statusLabel.text() == ""
+ assert self.rd._db.get_rule("www.test-allow.com", self.rd.nodesCombo.currentText()).next() == True
+ assert self.rd._old_rule_name == "www.test-allow.com"
+ # after adding a rule, we enter into editing mode, to allow editing it
+ # without closing the dialog.
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
+ assert self.rd.rule.operator.operand == "dest.host"
+ assert self.rd.rule.operator.data == "www.test-allow.com"
+ assert self.rd.rule.action == Config.ACTION_ALLOW
+
+ def test_add_rule_name_conflict(self, qtbot):
+ """ Test that rules with the same name cannot be added.
+ """
+ assert self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()).next() == True
+
+ self.rd.statusLabel.setText("")
+ self.rd.ruleNameEdit.setText("www.test.com")
+ self.rd.dstHostCheck.setChecked(True)
+ self.rd.dstHostLine.setText("www.test.com")
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ assert self.rd.statusLabel.text() != ""
+
+ def test_load_rule(self, qtbot):
+ """ Test loading a rule.
+ """
+ self.rd.WORK_MODE = self.rd.ADD_RULE
+ self.rd._reset_state()
+ records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText())
+ assert records.next() == True
+
+ self.rd.edit_rule(records, self.rd.nodesCombo.currentText())
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.ruleNameEdit.text() == "www.test.com"
+ assert self.rd.dstHostCheck.isChecked() == True
+ assert self.rd.dstHostLine.text() == "www.test.com"
+ assert self.rd.durationCombo.currentIndex() == self.rd._load_duration(Config.DURATION_UNTIL_RESTART)
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ def test_edit_and_rename_rule(self, qtbot):
+ """ Test loading, editing and renaming a rule.
+ """
+ self.rd.WORK_MODE = self.rd.ADD_RULE
+ self.rd._reset_state()
+ records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText())
+ assert records.next() == True
+
+ self.rd.edit_rule(records, self.rd.nodesCombo.currentText())
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.ruleNameEdit.text() == "www.test.com"
+ assert self.rd.dstHostCheck.isChecked() == True
+ assert self.rd.dstHostLine.text() == "www.test.com"
+
+ self.rd.ruleNameEdit.setText("www.test-renamed.com")
+ self.rd.dstHostLine.setText("www.test-renamed.com")
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText())
+ assert records.next() == False
+ records = self.rd._db.get_rule("www.test-renamed.com", self.rd.nodesCombo.currentText())
+ assert records.next() == True
+
+ def test_durations(self, qtbot):
+ """ Test adding new rule with action "deny".
+ """
+
+ self.rd.statusLabel.setText("")
+ self.rd.ruleNameEdit.setText("www.test-duration.com")
+ self.rd.dstHostCheck.setChecked(True)
+ self.rd.dstHostLine.setText("www.test-duration.com")
+ self.rd.actionDenyRadio.setChecked(True)
+ self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_ALWAYS))
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ assert self.rd.statusLabel.text() == ""
+ assert self.rd._db.get_rule("www.test-duration.com", self.rd.nodesCombo.currentText()).next() == True
+ assert self.rd._old_rule_name == "www.test-duration.com"
+ # after adding a rule, we enter into editing mode, to allow editing it
+ # without closing the dialog.
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
+ assert self.rd.rule.operator.operand == "dest.host"
+ assert self.rd.rule.operator.data == "www.test-duration.com"
+ assert self.rd.rule.action == Config.ACTION_DENY
+ assert self.rd.rule.duration == Config.DURATION_ALWAYS
+
+ def test_rule_LANs(self, qtbot):
+ """ Test rule with regexp and LAN keyword in particular.
+ """
+ self.rd.statusLabel.setText("")
+ self.rd.ruleNameEdit.setText("www.test-rule-LAN.com")
+ self.rd.dstIPCheck.setChecked(True)
+ self.rd.dstIPCombo.setCurrentText(self.rd.LAN_LABEL)
+ self.rd.actionDenyRadio.setChecked(True)
+ self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_ALWAYS))
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ assert self.rd.statusLabel.text() == ""
+ assert self.rd._db.get_rule("www.test-rule-LAN.com", self.rd.nodesCombo.currentText()).next() == True
+ assert self.rd._old_rule_name == "www.test-rule-LAN.com"
+ # after adding a rule, we enter into editing mode, to allow editing it
+ # without closing the dialog.
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.rule.operator.type == Config.RULE_TYPE_REGEXP
+ assert self.rd.rule.operator.operand == "dest.ip"
+ assert self.rd.rule.operator.data == self.rd.LAN_RANGES
+ assert self.rd.rule.action == Config.ACTION_DENY
+ assert self.rd.rule.duration == Config.DURATION_ALWAYS
+
+ def test_rule_networks(self, qtbot):
+ """ Test rule with networks.
+ """
+ self.rd.statusLabel.setText("")
+ self.rd.ruleNameEdit.setText("www.test-rule-networks.com")
+ self.rd.dstIPCheck.setChecked(True)
+ self.rd.dstIPCombo.setCurrentText("192.168.111.0/24")
+ self.rd.actionDenyRadio.setChecked(True)
+ self.rd.durationCombo.setCurrentIndex(self.rd._load_duration(Config.DURATION_ALWAYS))
+
+ def handle_dialog():
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Apply), QtCore.Qt.LeftButton)
+ qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.Close), QtCore.Qt.LeftButton)
+
+ QtCore.QTimer.singleShot(100, handle_dialog)
+ self.rd.exec_()
+
+ assert self.rd.statusLabel.text() == ""
+ assert self.rd._db.get_rule("www.test-rule-networks.com", self.rd.nodesCombo.currentText()).next() == True
+ assert self.rd._old_rule_name == "www.test-rule-networks.com"
+ # after adding a rule, we enter into editing mode, to allow editing it
+ # without closing the dialog.
+ assert self.rd.WORK_MODE == self.rd.EDIT_RULE
+ assert self.rd.rule.operator.type == Config.RULE_TYPE_NETWORK
+ assert self.rd.rule.operator.operand == "dest.network"
+ assert self.rd.rule.operator.data == "192.168.111.0/24"
+ assert self.rd.rule.action == Config.ACTION_DENY
+ assert self.rd.rule.duration == Config.DURATION_ALWAYS
+
--- /dev/null
+#
+# pytest -v tests/nodes.py
+#
+
+import json
+from PyQt5 import QtCore
+from opensnitch import ui_pb2
+from opensnitch.config import Config
+from opensnitch.nodes import Nodes
+from tests.dialogs import ClientConfig
+
+class NotifTest(QtCore.QObject):
+ """We need to subclass from QObject in order to be able to user signals and slots.
+ """
+ signal = QtCore.pyqtSignal(ui_pb2.NotificationReply)
+
+ @QtCore.pyqtSlot(ui_pb2.NotificationReply)
+ def callback(self, reply):
+ assert reply != None
+ assert reply.code == ui_pb2.OK and reply.type == ui_pb2.LOAD_FIREWALL and reply.data == "test"
+
+
+class TestNodes():
+
+ @classmethod
+ def setup_method(self):
+ self.nid = None
+ self.daemon_config = ClientConfig
+ self.nodes = Nodes.instance()
+ self.nodes._db.insert("nodes",
+ "(addr, status, hostname, daemon_version, daemon_uptime, " \
+ "daemon_rules, cons, cons_dropped, version, last_connection)",
+ (
+ "1.2.3.4", Nodes.ONLINE, "xxx", "v1.2.3", str(0),
+ "", "0", "0", "",
+ "2022-01-03 11:22:48.101624"
+ )
+ )
+
+
+ def test_add(self, qtbot):
+ node = self.nodes.add("peer:1.2.3.4", self.daemon_config)
+
+ assert node != None
+
+ def test_get_node(self, qtbot):
+ node = self.nodes.get_node("peer:1.2.3.4")
+
+ assert node != None
+
+ def test_get_addr(self, qtbot):
+ proto, addr = self.nodes.get_addr("peer:1.2.3.4")
+
+ assert proto == "peer" and addr == "1.2.3.4"
+
+ def test_get_nodes(self, qtbot):
+ nodes = self.nodes.get_nodes()
+ print(nodes)
+
+ assert nodes.get("peer:1.2.3.4") != None
+
+ def test_add_rule(self, qtbot):
+ self.nodes.add_rule(
+ "2022-01-03 11:22:48.101624",
+ "peer:1.2.3.4",
+ "test",
+ True,
+ False,
+ Config.ACTION_ALLOW, Config.DURATION_30s,
+ Config.RULE_TYPE_SIMPLE, False, "dest.host", ""
+ )
+
+ query = self.nodes._db.get_rule("test", "peer:1.2.3.4")
+
+ assert query.first() == True
+ assert query.record().value(0) == "2022-01-03 11:22:48.101624"
+ assert query.record().value(1) == "peer:1.2.3.4"
+ assert query.record().value(2) == "test"
+ assert query.record().value(3) == "1"
+ assert query.record().value(4) == "0"
+ assert query.record().value(5) == Config.ACTION_ALLOW
+ assert query.record().value(6) == Config.DURATION_30s
+ assert query.record().value(7) == Config.RULE_TYPE_SIMPLE
+ assert query.record().value(8) == "0"
+ assert query.record().value(9) == "dest.host"
+
+ def test_update_rule_time(self, qtbot):
+ query = self.nodes._db.get_rule("test", "peer:1.2.3.4")
+ assert query.first() == True
+ assert query.record().value(0) == "2022-01-03 11:22:48.101624"
+
+ self.nodes.update_rule_time("2022-01-03 21:22:48.101624", "test", "peer:1.2.3.4")
+ query = self.nodes._db.get_rule("test", "peer:1.2.3.4")
+ assert query.first() == True
+ assert query.record().value(0) == "2022-01-03 21:22:48.101624"
+
+ def test_delete_rule(self, qtbot):
+ query = self.nodes._db.get_rule("test", "peer:1.2.3.4")
+ assert query.first() == True
+
+ self.nodes.delete_rule("test", "peer:1.2.3.4", None)
+ query = self.nodes._db.get_rule("test", "peer:1.2.3.4")
+ assert query.first() == False
+
+ def test_update_node_status(self, qtbot):
+ query = self.nodes._db.select("SELECT status FROM nodes WHERE addr = '{0}'".format("1.2.3.4"))
+ assert query != None and query.exec_() == True and query.first() == True
+ assert query.record().value(0) == Nodes.ONLINE
+
+ self.nodes.update("peer", "1.2.3.4", Nodes.OFFLINE)
+ query = self.nodes._db.select("SELECT status FROM nodes WHERE addr = '{0}'".format("1.2.3.4"))
+ assert query != None and query.exec_() == True and query.first() == True
+ assert query.record().value(0) == Nodes.OFFLINE
+
+ def test_send_notification(self, qtbot):
+ notifs = NotifTest()
+ notifs.signal.connect(notifs.callback)
+
+ test_notif = ui_pb2.Notification(
+ clientName="",
+ serverName="",
+ type=ui_pb2.LOAD_FIREWALL,
+ data="test",
+ rules=[])
+
+ self.nid = self.nodes.send_notification("peer:1.2.3.4", test_notif, notifs.signal)
+ assert self.nodes._notifications_sent[self.nid] != None
+ assert self.nodes._notifications_sent[self.nid]['type'] == ui_pb2.LOAD_FIREWALL
+
+ def test_reply_notification(self, qtbot):
+ reply_notif = ui_pb2.Notification(
+ id = self.nid,
+ clientName="",
+ serverName="",
+ type=ui_pb2.LOAD_FIREWALL,
+ data="test",
+ rules=[])
+ # just after process the reply, the notification is deleted (except if
+ # is of type MONITOR_PROCESS
+ self.nodes.reply_notification("peer:1.2.3.4", reply_notif)
+ assert self.nid not in self.nodes._notifications_sent
+
+ def test_delete(self, qtbot):
+ self.nodes.delete("peer:1.2.3.4")
+ node = self.nodes.get_node("peer:1.2.3.4")
+ nodes = self.nodes.get_nodes()
+
+ assert node == None
+ assert nodes.get("peer:1.2.3.4") == None
--- /dev/null
+import requests
+import re
+import ipaddress
+import datetime
+import os
+
+lists = ( \
+ "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts",
+ "https://mirror1.malwaredomains.com/files/justdomains",
+ "http://sysctl.org/cameleon/hosts",
+ "https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist",
+ "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt",
+ "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt",
+ "https://hosts-file.net/ad_servers.txt" )
+
+domains = {}
+
+for url in lists:
+ print "Downloading %s ..." % url
+ r = requests.get(url)
+ if r.status_code != 200:
+ print "Error, status code %d" % r.status_code
+ continue
+
+ for line in r.text.split("\n"):
+ line = line.strip()
+ if line == "":
+ continue
+
+ elif line[0] == "#":
+ continue
+
+ for part in re.split(r'\s+', line):
+ part = part.strip()
+ if part == "":
+ continue
+
+ try:
+ duh = ipaddress.ip_address(part)
+ except ValueError:
+ if part != "localhost":
+ domains[part] = 1
+
+print "Got %d unique domains, saving as rules to ./rules/ ..." % len(domains)
+
+os.system("mkdir -p rules")
+
+idx = 0
+for domain, _ in domains.iteritems():
+ with open("rules/adv-%d.json" % idx, "wt") as fp:
+ tpl = """
+{
+ "created": "%s",
+ "updated": "%s",
+ "name": "deny-adv-%d",
+ "enabled": true,
+ "action": "deny",
+ "duration": "always",
+ "operator": {
+ "type": "simple",
+ "operand": "dest.host",
+ "data": "%s"
+ }
+}"""
+ now = datetime.datetime.utcnow().isoformat("T") + "Z"
+ data = tpl % ( now, now, idx, domain )
+ fp.write(data)
+
+ idx = idx + 1
--- /dev/null
+#!/bin/bash
+# opensnitch - 2021
+#
+# https://github.com/evilsocket/opensnitch/wiki/block-lists
+#
+# Add the script to a regular user's crontab:
+# $ crontab -e
+# 0 11,17,23 * * * /home/user/scripts/update_adlists.sh
+
+# https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintext
+# https://hostfiles.frogeye.fr/multiparty-trackers-hosts.txt
+# https://hostfiles.frogeye.fr/firstparty-trackers-hosts.txt
+# https://adaway.org/hosts.txt
+# https://www.github.developerdan.com/hosts/lists/tracking-aggressive-extended.txt
+# https://raw.githubusercontent.com/Kees1958/W3C_annual_most_used_survey_blocklist/master/TOP_EU_US_Ads_Trackers_HOST
+# https://curben.gitlab.io/malware-filter/urlhaus-filter-hosts.txt
+
+# Use any directory you want to save the lists.
+# If you use /etc/opensnitchd, give write permissions to blocklists/* for your user (chown -R /etc/opensnitchd/blocklists/).
+# or use a directory from your user's home.
+adsDir="/etc/opensnitchd/blocklists/domains/"
+
+# If you add new urls, remember to add the corresponding filename where it'll be save on disk.
+adsList=(
+ "https://curben.gitlab.io/malware-filter/urlhaus-filter-hosts.txt"
+ "https://raw.githubusercontent.com/Kees1958/W3C_annual_most_used_survey_blocklist/master/TOP_EU_US_Ads_Trackers_HOST"
+ "https://hostfiles.frogeye.fr/multiparty-trackers-hosts.txt"
+ "https://hostfiles.frogeye.fr/firstparty-trackers-hosts.txt"
+ "https://www.github.developerdan.com/hosts/lists/tracking-aggressive-extended.txt"
+ "https://adaway.org/hosts.txt"
+ "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=0&mimetype=plaintex")
+adsListNames=(
+ "urlhaus-filter-hosts.txt"
+ "top_eu_us_ads_trackers.txt"
+ "multiparty-trackers-hosts.txt"
+ "firstparty-trackers-hosts.txt"
+ "tracking-aggressive-extended.txt"
+ "adaway-hosts.txt"
+ "yoyo-adservers.txt")
+
+function download_cname_trackers
+{
+ remoteSize=$(curl --silent -I https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/SpywareFilter/sections/cname_trackers.txt | awk '/content-length:/ {print $2}'|tr -d '\r')
+ localSize=$(stat -c %s $adsDir/cname_trackers.txt)
+ if [ ! -z $remoteSize ]; then
+ if [ "$remoteSize" != "$localSize" ]; then
+ > /tmp/.cname_temp
+ cname_trackers=$(curl --silent https://raw.githubusercontent.com/AdguardTeam/AdguardFilters/master/SpywareFilter/sections/cname_trackers.txt | awk '/^!#include/ { print $2 }')
+ for tracker in $cname_trackers
+ do
+ curl --silent $tracker | grep "^||" | sed 's/^||\(.*\)^/0.0.0.0 \1/' >> /tmp/.cname_temp
+ done
+ mv /tmp/.cname_temp $adsDir/cname_trackers.txt
+ else
+ echo "[-] cname trackers not updated yet"
+ fi
+ fi
+}
+
+function download_ads_list
+{
+ reload=0
+ for idx in ${!adsList[@]}
+ do
+ echo "[+] Checking list ${adsList[$idx]}, ${adsListNames[$idx]}"
+ remoteSize=$(curl --silent -I ${adsList[$idx]}|awk '/content-length:/ {print $2}'|tr -d '\r')
+ localSize=$(stat -c %s $adsDir/${adsListNames[$idx]})
+
+ if [ ! -z $remoteSize ]; then
+ if [ "$remoteSize" != "$localSize" ]; then
+ echo "[+] downloading new ads list... ${adsList[$idx]}, $remoteSize, $localSize"
+ curl --silent "${adsList[$idx]}" -o $adsDir/${adsListNames[$idx]}
+ reload=1
+ else
+ echo "[-] ads list not updated yet: $remoteSize, $localSize - ${adsList[$idx]}"
+ fi
+ else
+ echo "[!] No content-length header found: ${adsList[$idx]}"
+ fi
+ done
+}
+
+
+if [ ! -d $adsDir ]; then
+ mkdir -p $adsDir
+fi
+
+cd $adsDir
+
+download_ads_list
+download_cname_trackers