From 2d8f19679fba03b2453e760dd0ad5071f84ad77d Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Sun, 2 Feb 2025 19:54:42 +0100 Subject: [PATCH 01/11] nfqueue to hyperscan and stream match, removed proxyregex --- Dockerfile | 15 +- backend/app.py | 22 +- backend/binsrc/classes/netfilter.cpp | 485 +++++++++++++++++ backend/binsrc/classes/netfilter.hpp | 294 ----------- backend/binsrc/classes/regex_filter.hpp | 95 ---- backend/binsrc/classes/regex_rules.cpp | 161 ++++++ backend/binsrc/cppqueue | Bin 0 -> 282672 bytes backend/binsrc/nfqueue.cpp | 122 ++++- backend/binsrc/nfqueue_regex/Cargo.lock | 32 -- backend/binsrc/nfqueue_regex/Cargo.toml | 11 - backend/binsrc/nfqueue_regex/src/main.rs | 150 ------ .../binsrc/nfqueue_regex/src/regex_rules.rs | 1 - backend/binsrc/proxy.cpp | 493 ------------------ backend/binsrc/utils.hpp | 2 +- backend/modules/firewall/firewall.py | 4 +- backend/modules/nfregex/firegex.py | 30 +- backend/modules/nfregex/firewall.py | 10 +- backend/modules/nfregex/nftables.py | 47 +- backend/modules/porthijack/firewall.py | 6 +- backend/modules/porthijack/nftables.py | 3 +- backend/modules/regexproxy/proxy.py | 116 ----- backend/modules/regexproxy/utils.py | 199 ------- backend/routers/firewall.py | 2 +- backend/routers/nfregex.py | 12 +- backend/routers/porthijack.py | 6 +- backend/routers/regexproxy.py | 311 ----------- backend/utils/__init__.py | 22 +- backend/utils/loader.py | 15 +- backend/utils/sqlite.py | 31 +- frontend/bun.lockb | Bin 117435 -> 115834 bytes frontend/package.json | 28 +- frontend/src/App.tsx | 5 - frontend/src/components/AddNewRegex.tsx | 20 +- .../src/components/FilterTypeSelector.tsx | 29 -- frontend/src/components/NavBar/index.tsx | 5 - frontend/src/components/PortHijack/utils.ts | 2 +- .../components/RegexProxy/AddNewService.tsx | 117 ----- .../RegexProxy/ServiceRow/ChangePortModal.tsx | 101 ---- .../RegexProxy/ServiceRow/RenameForm.tsx | 68 --- .../RegexProxy/ServiceRow/index.tsx | 205 -------- frontend/src/components/RegexProxy/utils.ts | 97 ---- frontend/src/components/RegexView/index.tsx | 7 - frontend/src/js/models.ts | 1 - frontend/src/js/utils.tsx | 3 - .../src/pages/RegexProxy/ServiceDetails.tsx | 48 -- frontend/src/pages/RegexProxy/index.tsx | 91 ---- tests/api_test.py | 89 +++- tests/benchmark.py | 66 ++- tests/nf_test.py | 127 +++-- tests/ph_test.py | 89 +++- tests/px_test.py | 249 --------- tests/run_tests.sh | 2 - .../regexproxy => tests/utils}/__init__.py | 0 tests/utils/firegexapi.py | 80 +-- 54 files changed, 1134 insertions(+), 3092 deletions(-) create mode 100644 backend/binsrc/classes/netfilter.cpp delete mode 100644 backend/binsrc/classes/netfilter.hpp delete mode 100644 backend/binsrc/classes/regex_filter.hpp create mode 100644 backend/binsrc/classes/regex_rules.cpp create mode 100755 backend/binsrc/cppqueue delete mode 100644 backend/binsrc/nfqueue_regex/Cargo.lock delete mode 100644 backend/binsrc/nfqueue_regex/Cargo.toml delete mode 100644 backend/binsrc/nfqueue_regex/src/main.rs delete mode 100644 backend/binsrc/nfqueue_regex/src/regex_rules.rs delete mode 100644 backend/binsrc/proxy.cpp delete mode 100644 backend/modules/regexproxy/proxy.py delete mode 100644 backend/modules/regexproxy/utils.py delete mode 100644 backend/routers/regexproxy.py delete mode 100644 frontend/src/components/FilterTypeSelector.tsx delete mode 100644 frontend/src/components/RegexProxy/AddNewService.tsx delete mode 100644 frontend/src/components/RegexProxy/ServiceRow/ChangePortModal.tsx delete mode 100644 frontend/src/components/RegexProxy/ServiceRow/RenameForm.tsx delete mode 100644 frontend/src/components/RegexProxy/ServiceRow/index.tsx delete mode 100644 frontend/src/components/RegexProxy/utils.ts delete mode 100644 frontend/src/pages/RegexProxy/ServiceDetails.tsx delete mode 100644 frontend/src/pages/RegexProxy/index.tsx delete mode 100644 tests/px_test.py rename {backend/modules/regexproxy => tests/utils}/__init__.py (100%) diff --git a/Dockerfile b/Dockerfile index c5b567f..1922bea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,15 +17,9 @@ RUN bun run build FROM --platform=$TARGETARCH debian:stable-slim AS base RUN apt-get update -qq && apt-get upgrade -qq && \ apt-get install -qq python3-pip build-essential \ - git libpcre2-dev libnetfilter-queue-dev libssl-dev \ - libnfnetlink-dev libmnl-dev libcap2-bin make cmake \ - nftables libboost-all-dev autoconf automake cargo \ - libffi-dev libvectorscan-dev libtins-dev python3-nftables - -WORKDIR /tmp/ -RUN git clone --single-branch --branch release https://github.com/jpcre2/jpcre2 -WORKDIR /tmp/jpcre2 -RUN ./configure; make -j`nproc`; make install + git libnetfilter-queue-dev libssl-dev \ + libnfnetlink-dev libmnl-dev libcap2-bin \ + nftables libffi-dev libvectorscan-dev libtins-dev python3-nftables RUN mkdir -p /execute/modules WORKDIR /execute @@ -34,8 +28,7 @@ ADD ./backend/requirements.txt /execute/requirements.txt RUN pip3 install --no-cache-dir --break-system-packages -r /execute/requirements.txt --no-warn-script-location COPY ./backend/binsrc /execute/binsrc -RUN g++ binsrc/nfqueue.cpp -o modules/cppqueue -O3 -lnetfilter_queue -pthread -lpcre2-8 -ltins -lmnl -lnfnetlink -RUN g++ binsrc/proxy.cpp -o modules/proxy -O3 -pthread -lboost_system -lboost_thread -lpcre2-8 +RUN g++ binsrc/nfqueue.cpp -o modules/cppqueue -O3 -lnetfilter_queue -pthread -lnfnetlink $(pkg-config --cflags --libs libtins libhs libmnl) COPY ./backend/ /execute/ COPY --from=frontend /app/dist/ ./frontend/ diff --git a/backend/app.py b/backend/app.py index d4379ef..1ac8138 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1,6 +1,10 @@ -import uvicorn, secrets, utils -import os, asyncio, logging -from fastapi import FastAPI, HTTPException, Depends, APIRouter, Request +import uvicorn +import secrets +import utils +import os +import asyncio +import logging +from fastapi import FastAPI, HTTPException, Depends, APIRouter from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import jwt from passlib.context import CryptContext @@ -94,7 +98,8 @@ async def get_app_status(auth: bool = Depends(check_login)): @app.post("/api/login") async def login_api(form: OAuth2PasswordRequestForm = Depends()): """Get a login token to use the firegex api""" - if APP_STATUS() != "run": raise HTTPException(status_code=400) + if APP_STATUS() != "run": + raise HTTPException(status_code=400) if form.password == "": return {"status":"Cannot insert an empty password!"} await asyncio.sleep(0.3) # No bruteforce :) @@ -105,7 +110,8 @@ async def login_api(form: OAuth2PasswordRequestForm = Depends()): @app.post('/api/set-password', response_model=ChangePasswordModel) async def set_password(form: PasswordForm): """Set the password of firegex""" - if APP_STATUS() != "init": raise HTTPException(status_code=400) + if APP_STATUS() != "init": + raise HTTPException(status_code=400) if form.password == "": return {"status":"Cannot insert an empty password!"} set_psw(form.password) @@ -115,7 +121,8 @@ async def set_password(form: PasswordForm): @api.post('/change-password', response_model=ChangePasswordModel) async def change_password(form: PasswordChangeForm): """Change the password of firegex""" - if APP_STATUS() != "run": raise HTTPException(status_code=400) + if APP_STATUS() != "run": + raise HTTPException(status_code=400) if form.password == "": return {"status":"Cannot insert an empty password!"} @@ -144,7 +151,8 @@ async def startup_main(): except Exception as e: logging.error(f"Error setting sysctls: {e}") await startup() - if not JWT_SECRET(): db.put("secret", secrets.token_hex(32)) + if not JWT_SECRET(): + db.put("secret", secrets.token_hex(32)) await refresh_frontend() async def shutdown_main(): diff --git a/backend/binsrc/classes/netfilter.cpp b/backend/binsrc/classes/netfilter.cpp new file mode 100644 index 0000000..39dedf3 --- /dev/null +++ b/backend/binsrc/classes/netfilter.cpp @@ -0,0 +1,485 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Tins::TCPIP::Stream; +using Tins::TCPIP::StreamFollower; +using namespace std; + + +#ifndef NETFILTER_CLASSES_HPP +#define NETFILTER_CLASSES_HPP + +string inline client_endpoint(const Stream& stream) { + ostringstream output; + // Use the IPv4 or IPv6 address depending on which protocol the + // connection uses + if (stream.is_v6()) { + output << stream.client_addr_v6(); + } + else { + output << stream.client_addr_v4(); + } + output << ":" << stream.client_port(); + return output.str(); +} + +// Convert the server endpoint to a readable string +string inline server_endpoint(const Stream& stream) { + ostringstream output; + if (stream.is_v6()) { + output << stream.server_addr_v6(); + } + else { + output << stream.server_addr_v4(); + } + output << ":" << stream.server_port(); + return output.str(); +} + +// Concat both endpoints to get a readable stream identifier +string inline stream_identifier(const Stream& stream) { + ostringstream output; + output << client_endpoint(stream) << " - " << server_endpoint(stream); + return output.str(); +} + +typedef unordered_map matching_map; + +struct packet_info; + +struct tcp_stream_tmp { + bool matching_has_been_called = false; + bool result; + packet_info *pkt_info; +}; + +struct stream_ctx { + matching_map in_hs_streams; + matching_map out_hs_streams; + hs_scratch_t* in_scratch = nullptr; + hs_scratch_t* out_scratch = nullptr; + u_int16_t latest_config_ver = 0; + StreamFollower follower; + mnl_socket* nl; + tcp_stream_tmp tcp_match_util; + + void clean_scratches(){ + if (out_scratch != nullptr){ + hs_free_scratch(out_scratch); + out_scratch = nullptr; + } + if (in_scratch != nullptr){ + hs_free_scratch(in_scratch); + in_scratch = nullptr; + } + } +}; + +struct packet_info { + string packet; + string payload; + string stream_id; + bool is_input; + bool is_tcp; + stream_ctx* sctx; +}; + +typedef bool NetFilterQueueCallback(packet_info &); + + +Tins::PDU * find_transport_layer(Tins::PDU* pkt){ + while(pkt != nullptr){ + if (pkt->pdu_type() == Tins::PDU::TCP || pkt->pdu_type() == Tins::PDU::UDP) { + return pkt; + } + pkt = pkt->inner_pdu(); + } + return nullptr; +} + +template +class NetfilterQueue { + public: + + size_t BUF_SIZE = 0xffff + (MNL_SOCKET_BUFFER_SIZE/2); + char *buf = nullptr; + unsigned int portid; + u_int16_t queue_num; + stream_ctx sctx; + + NetfilterQueue(u_int16_t queue_num): queue_num(queue_num) { + sctx.nl = mnl_socket_open(NETLINK_NETFILTER); + + if (sctx.nl == nullptr) { throw runtime_error( "mnl_socket_open" );} + + if (mnl_socket_bind(sctx.nl, 0, MNL_SOCKET_AUTOPID) < 0) { + mnl_socket_close(sctx.nl); + throw runtime_error( "mnl_socket_bind" ); + } + portid = mnl_socket_get_portid(sctx.nl); + + buf = (char*) malloc(BUF_SIZE); + + if (!buf) { + mnl_socket_close(sctx.nl); + throw runtime_error( "allocate receive buffer" ); + } + + if (send_config_cmd(NFQNL_CFG_CMD_BIND) < 0) { + _clear(); + throw runtime_error( "mnl_socket_send" ); + } + //TEST if BIND was successful + if (send_config_cmd(NFQNL_CFG_CMD_NONE) < 0) { // SEND A NONE cmmand to generate an error meessage + _clear(); + throw runtime_error( "mnl_socket_send" ); + } + if (recv_packet() == -1) { //RECV the error message + _clear(); + throw runtime_error( "mnl_socket_recvfrom" ); + } + + struct nlmsghdr *nlh = (struct nlmsghdr *) buf; + + if (nlh->nlmsg_type != NLMSG_ERROR) { + _clear(); + throw runtime_error( "unexpected packet from kernel (expected NLMSG_ERROR packet)" ); + } + //nfqnl_msg_config_cmd + nlmsgerr* error_msg = (nlmsgerr *)mnl_nlmsg_get_payload(nlh); + + // error code taken from the linux kernel: + // https://elixir.bootlin.com/linux/v5.18.12/source/include/linux/errno.h#L27 + #define ENOTSUPP 524 /* Operation is not supported */ + + if (error_msg->error != -ENOTSUPP) { + _clear(); + throw invalid_argument( "queueid is already busy" ); + } + + //END TESTING BIND + nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); + nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); + + mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_GSO)); + mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(NFQA_CFG_F_GSO)); + + if (mnl_socket_sendto(sctx.nl, nlh, nlh->nlmsg_len) < 0) { + _clear(); + throw runtime_error( "mnl_socket_send" ); + } + + } + + //Input data filtering + void on_client_data(Stream& stream) { + string data(stream.client_payload().begin(), stream.client_payload().end()); + string stream_id = stream_identifier(stream); + this->sctx.tcp_match_util.pkt_info->is_input = true; + this->sctx.tcp_match_util.pkt_info->stream_id = stream_id; + this->sctx.tcp_match_util.matching_has_been_called = true; + bool result = callback_func(*sctx.tcp_match_util.pkt_info); + if (result){ + this->clean_stream_by_id(stream_id); + stream.ignore_client_data(); + stream.ignore_server_data(); + } + this->sctx.tcp_match_util.result = result; + } + + //Server data filtering + void on_server_data(Stream& stream) { + string data(stream.server_payload().begin(), stream.server_payload().end()); + string stream_id = stream_identifier(stream); + this->sctx.tcp_match_util.pkt_info->is_input = false; + this->sctx.tcp_match_util.pkt_info->stream_id = stream_id; + this->sctx.tcp_match_util.matching_has_been_called = true; + bool result = callback_func(*sctx.tcp_match_util.pkt_info); + if (result){ + this->clean_stream_by_id(stream_id); + stream.ignore_client_data(); + stream.ignore_server_data(); + } + this->sctx.tcp_match_util.result = result; + } + + void on_new_stream(Stream& stream) { + string stream_id = stream_identifier(stream); + if (stream.is_partial_stream()) { + return; + } + cout << "[+] New connection " << stream_id << endl; + stream.auto_cleanup_payloads(true); + stream.client_data_callback( + [&](auto a){this->on_client_data(a);} + ); + stream.server_data_callback( + [&](auto a){this->on_server_data(a);} + ); + } + + void clean_stream_by_id(string stream_id){ + auto stream_search = this->sctx.in_hs_streams.find(stream_id); + hs_stream_t* stream_match; + if (stream_search != this->sctx.in_hs_streams.end()){ + stream_match = stream_search->second; + if (hs_close_stream(stream_match, sctx.in_scratch, nullptr, nullptr) != HS_SUCCESS) { + cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; + throw invalid_argument("Cannot close stream match on hyperscan"); + } + this->sctx.in_hs_streams.erase(stream_search); + } + + stream_search = this->sctx.out_hs_streams.find(stream_id); + if (stream_search != this->sctx.out_hs_streams.end()){ + stream_match = stream_search->second; + if (hs_close_stream(stream_match, sctx.out_scratch, nullptr, nullptr) != HS_SUCCESS) { + cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; + throw invalid_argument("Cannot close stream match on hyperscan"); + } + this->sctx.out_hs_streams.erase(stream_search); + } + } + + // A stream was terminated. The second argument is the reason why it was terminated + void on_stream_terminated(Stream& stream, StreamFollower::TerminationReason reason) { + string stream_id = stream_identifier(stream); + cout << "[+] Connection closed: " << stream_id << endl; + this->clean_stream_by_id(stream_id); + } + + + void run(){ + /* + * ENOBUFS is signalled to userspace when packets were lost + * on kernel side. In most cases, userspace isn't interested + * in this information, so turn it off. + */ + int ret = 1; + mnl_socket_setsockopt(sctx.nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int)); + sctx.follower.new_stream_callback( + [&](auto a){this->on_new_stream(a);} + ); + sctx.follower.stream_termination_callback( + [&](auto a, auto b){this->on_stream_terminated(a, b);} + ); + for (;;) { + ret = recv_packet(); + if (ret == -1) { + throw runtime_error( "mnl_socket_recvfrom" ); + } + + ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, &sctx); + if (ret < 0){ + throw runtime_error( "mnl_cb_run" ); + } + } + } + + + ~NetfilterQueue() { + send_config_cmd(NFQNL_CFG_CMD_UNBIND); + _clear(); + } + private: + + ssize_t send_config_cmd(nfqnl_msg_config_cmds cmd){ + struct nlmsghdr *nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); + nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, cmd); + return mnl_socket_sendto(sctx.nl, nlh, nlh->nlmsg_len); + } + + ssize_t recv_packet(){ + return mnl_socket_recvfrom(sctx.nl, buf, BUF_SIZE); + } + + void _clear(){ + if (buf != nullptr) { + free(buf); + buf = nullptr; + } + mnl_socket_close(sctx.nl); + sctx.nl = nullptr; + sctx.clean_scratches(); + + for(auto ele: sctx.in_hs_streams){ + if (hs_close_stream(ele.second, sctx.in_scratch, nullptr, nullptr) != HS_SUCCESS) { + cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; + throw invalid_argument("Cannot close stream match on hyperscan"); + } + } + sctx.in_hs_streams.clear(); + for(auto ele: sctx.out_hs_streams){ + if (hs_close_stream(ele.second, sctx.out_scratch, nullptr, nullptr) != HS_SUCCESS) { + cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; + throw invalid_argument("Cannot close stream match on hyperscan"); + } + } + sctx.out_hs_streams.clear(); + } + + static int queue_cb(const nlmsghdr *nlh, void *data_ptr) + { + stream_ctx* sctx = (stream_ctx*)data_ptr; + + //Extract attributes from the nlmsghdr + nlattr *attr[NFQA_MAX+1] = {}; + + if (nfq_nlmsg_parse(nlh, attr) < 0) { + perror("problems parsing"); + return MNL_CB_ERROR; + } + if (attr[NFQA_PACKET_HDR] == nullptr) { + fputs("metaheader not set\n", stderr); + return MNL_CB_ERROR; + } + //Get Payload + uint16_t plen = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]); + uint8_t *payload = (uint8_t *)mnl_attr_get_payload(attr[NFQA_PAYLOAD]); + + //Return result to the kernel + struct nfqnl_msg_packet_hdr *ph = (nfqnl_msg_packet_hdr*) mnl_attr_get_payload(attr[NFQA_PACKET_HDR]); + struct nfgenmsg *nfg = (nfgenmsg *)mnl_nlmsg_get_payload(nlh); + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh_verdict; + struct nlattr *nest; + + nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(nfg->res_id)); + + // Check IP protocol version + Tins::PDU *packet; + if ( ((payload)[0] & 0xf0) == 0x40 ){ + Tins::IP parsed = Tins::IP(payload, plen); + packet = &parsed; + }else{ + Tins::IPv6 parsed = Tins::IPv6(payload, plen); + packet = &parsed; + } + Tins::PDU *transport_layer = find_transport_layer(packet); + if(transport_layer == nullptr || transport_layer->inner_pdu() == nullptr){ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); + }else{ + bool is_tcp = transport_layer->pdu_type() == Tins::PDU::TCP; + int size = transport_layer->inner_pdu()->size(); + packet_info pktinfo{ + packet: string(payload, payload+plen), + payload: string(payload+plen - size, payload+plen), + stream_id: "", // TODO We need to calculate this + is_input: true, // TODO We need to detect this + is_tcp: is_tcp, + sctx: sctx, + }; + if (is_tcp){ + sctx->tcp_match_util.matching_has_been_called = false; + sctx->tcp_match_util.pkt_info = &pktinfo; + sctx->follower.process_packet(*packet); + if (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result){ + auto tcp_layer = (Tins::TCP *)transport_layer; + tcp_layer->release_inner_pdu(); + tcp_layer->set_flag(Tins::TCP::FIN,1); + tcp_layer->set_flag(Tins::TCP::ACK,1); + tcp_layer->set_flag(Tins::TCP::SYN,0); + nfq_nlmsg_verdict_put_pkt(nlh_verdict, packet->serialize().data(), packet->size()); + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); + delete tcp_layer; + } + }else if(callback_func(pktinfo)){ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); + } else{ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_DROP ); + } + } + + /* example to set the connmark. First, start NFQA_CT section: */ + nest = mnl_attr_nest_start(nlh_verdict, NFQA_CT); + + /* then, add the connmark attribute: */ + mnl_attr_put_u32(nlh_verdict, CTA_MARK, htonl(42)); + /* more conntrack attributes, e.g. CTA_LABELS could be set here */ + + /* end conntrack section */ + mnl_attr_nest_end(nlh_verdict, nest); + + if (mnl_socket_sendto(sctx->nl, nlh_verdict, nlh_verdict->nlmsg_len) < 0) { + throw runtime_error( "mnl_socket_send" ); + } + + return MNL_CB_OK; + } + +}; + +template +class NFQueueSequence{ + private: + vector *> nfq; + uint16_t _init; + uint16_t _end; + vector threads; + public: + static const int QUEUE_BASE_NUM = 1000; + + NFQueueSequence(uint16_t seq_len){ + if (seq_len <= 0) throw invalid_argument("seq_len <= 0"); + nfq = vector*>(seq_len); + _init = QUEUE_BASE_NUM; + while(nfq[0] == nullptr){ + if (_init+seq_len-1 >= 65536){ + throw runtime_error("NFQueueSequence: too many queues!"); + } + for (int i=0;i(_init+i); + }catch(const invalid_argument e){ + for(int j = 0; j < i; j++) { + delete nfq[j]; + nfq[j] = nullptr; + } + _init += seq_len - i; + break; + } + } + } + _end = _init + seq_len - 1; + } + + void start(){ + if (threads.size() != 0) throw runtime_error("NFQueueSequence: already started!"); + for (int i=0;i::run, nfq[i])); + } + } + + void join(){ + for (int i=0;i -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef NETFILTER_CLASSES_HPP -#define NETFILTER_CLASSES_HPP - -typedef bool NetFilterQueueCallback(const uint8_t*,uint32_t); - -Tins::PDU * find_transport_layer(Tins::PDU* pkt){ - while(pkt != NULL){ - if (pkt->pdu_type() == Tins::PDU::TCP || pkt->pdu_type() == Tins::PDU::UDP) { - return pkt; - } - pkt = pkt->inner_pdu(); - } - return pkt; -} - -template -class NetfilterQueue { - public: - size_t BUF_SIZE = 0xffff + (MNL_SOCKET_BUFFER_SIZE/2); - char *buf = NULL; - unsigned int portid; - u_int16_t queue_num; - struct mnl_socket* nl = NULL; - - NetfilterQueue(u_int16_t queue_num): queue_num(queue_num) { - - nl = mnl_socket_open(NETLINK_NETFILTER); - - if (nl == NULL) { throw std::runtime_error( "mnl_socket_open" );} - - if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - mnl_socket_close(nl); - throw std::runtime_error( "mnl_socket_bind" ); - } - portid = mnl_socket_get_portid(nl); - - buf = (char*) malloc(BUF_SIZE); - - if (!buf) { - mnl_socket_close(nl); - throw std::runtime_error( "allocate receive buffer" ); - } - - if (send_config_cmd(NFQNL_CFG_CMD_BIND) < 0) { - _clear(); - throw std::runtime_error( "mnl_socket_send" ); - } - //TEST if BIND was successful - if (send_config_cmd(NFQNL_CFG_CMD_NONE) < 0) { // SEND A NONE cmmand to generate an error meessage - _clear(); - throw std::runtime_error( "mnl_socket_send" ); - } - if (recv_packet() == -1) { //RECV the error message - _clear(); - throw std::runtime_error( "mnl_socket_recvfrom" ); - } - - struct nlmsghdr *nlh = (struct nlmsghdr *) buf; - - if (nlh->nlmsg_type != NLMSG_ERROR) { - _clear(); - throw std::runtime_error( "unexpected packet from kernel (expected NLMSG_ERROR packet)" ); - } - //nfqnl_msg_config_cmd - nlmsgerr* error_msg = (nlmsgerr *)mnl_nlmsg_get_payload(nlh); - - // error code taken from the linux kernel: - // https://elixir.bootlin.com/linux/v5.18.12/source/include/linux/errno.h#L27 - #define ENOTSUPP 524 /* Operation is not supported */ - - if (error_msg->error != -ENOTSUPP) { - _clear(); - throw std::invalid_argument( "queueid is already busy" ); - } - - //END TESTING BIND - nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); - nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); - - - mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_GSO)); - mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(NFQA_CFG_F_GSO)); - - if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - _clear(); - throw std::runtime_error( "mnl_socket_send" ); - } - - } - - - - void run(){ - /* - * ENOBUFS is signalled to userspace when packets were lost - * on kernel side. In most cases, userspace isn't interested - * in this information, so turn it off. - */ - int ret = 1; - mnl_socket_setsockopt(nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int)); - - for (;;) { - ret = recv_packet(); - if (ret == -1) { - throw std::runtime_error( "mnl_socket_recvfrom" ); - } - - ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, nl); - if (ret < 0){ - throw std::runtime_error( "mnl_cb_run" ); - } - } - } - - ~NetfilterQueue() { - send_config_cmd(NFQNL_CFG_CMD_UNBIND); - _clear(); - } - private: - - ssize_t send_config_cmd(nfqnl_msg_config_cmds cmd){ - struct nlmsghdr *nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num); - nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, cmd); - return mnl_socket_sendto(nl, nlh, nlh->nlmsg_len); - } - - ssize_t recv_packet(){ - return mnl_socket_recvfrom(nl, buf, BUF_SIZE); - } - - void _clear(){ - if (buf != NULL) { - free(buf); - buf = NULL; - } - mnl_socket_close(nl); - } - - static int queue_cb(const struct nlmsghdr *nlh, void *data) - { - struct mnl_socket* nl = (struct mnl_socket*)data; - //Extract attributes from the nlmsghdr - struct nlattr *attr[NFQA_MAX+1] = {}; - - if (nfq_nlmsg_parse(nlh, attr) < 0) { - perror("problems parsing"); - return MNL_CB_ERROR; - } - if (attr[NFQA_PACKET_HDR] == NULL) { - fputs("metaheader not set\n", stderr); - return MNL_CB_ERROR; - } - //Get Payload - uint16_t plen = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]); - void *payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]); - - //Return result to the kernel - struct nfqnl_msg_packet_hdr *ph = (nfqnl_msg_packet_hdr*) mnl_attr_get_payload(attr[NFQA_PACKET_HDR]); - struct nfgenmsg *nfg = (nfgenmsg *)mnl_nlmsg_get_payload(nlh); - char buf[MNL_SOCKET_BUFFER_SIZE]; - struct nlmsghdr *nlh_verdict; - struct nlattr *nest; - - nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(nfg->res_id)); - - /* - This define allow to avoid to allocate new heap memory for each packet. - The code under this comment is replicated for ipv6 and ip - Better solutions are welcome. :) - */ - #define PKT_HANDLE \ - Tins::PDU *transport_layer = find_transport_layer(&packet); \ - if(transport_layer->inner_pdu() == nullptr || transport_layer == nullptr){ \ - nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); \ - }else{ \ - int size = transport_layer->inner_pdu()->size(); \ - if(callback_func((const uint8_t*)payload+plen - size, size)){ \ - nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); \ - } else{ \ - if (transport_layer->pdu_type() == Tins::PDU::TCP){ \ - ((Tins::TCP *)transport_layer)->release_inner_pdu(); \ - ((Tins::TCP *)transport_layer)->set_flag(Tins::TCP::FIN,1); \ - ((Tins::TCP *)transport_layer)->set_flag(Tins::TCP::ACK,1); \ - ((Tins::TCP *)transport_layer)->set_flag(Tins::TCP::SYN,0); \ - nfq_nlmsg_verdict_put_pkt(nlh_verdict, packet.serialize().data(), packet.size()); \ - nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); \ - }else{ \ - nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_DROP ); \ - } \ - } \ - } - - // Check IP protocol version - if ( (((uint8_t*)payload)[0] & 0xf0) == 0x40 ){ - Tins::IP packet = Tins::IP((uint8_t*)payload,plen); - PKT_HANDLE - }else{ - Tins::IPv6 packet = Tins::IPv6((uint8_t*)payload,plen); - PKT_HANDLE - } - - /* example to set the connmark. First, start NFQA_CT section: */ - nest = mnl_attr_nest_start(nlh_verdict, NFQA_CT); - - /* then, add the connmark attribute: */ - mnl_attr_put_u32(nlh_verdict, CTA_MARK, htonl(42)); - /* more conntrack attributes, e.g. CTA_LABELS could be set here */ - - /* end conntrack section */ - mnl_attr_nest_end(nlh_verdict, nest); - - if (mnl_socket_sendto(nl, nlh_verdict, nlh_verdict->nlmsg_len) < 0) { - throw std::runtime_error( "mnl_socket_send" ); - } - - return MNL_CB_OK; - } - -}; - -template -class NFQueueSequence{ - private: - std::vector *> nfq; - uint16_t _init; - uint16_t _end; - std::vector threads; - public: - static const int QUEUE_BASE_NUM = 1000; - - NFQueueSequence(uint16_t seq_len){ - if (seq_len <= 0) throw std::invalid_argument("seq_len <= 0"); - nfq = std::vector*>(seq_len); - _init = QUEUE_BASE_NUM; - while(nfq[0] == NULL){ - if (_init+seq_len-1 >= 65536){ - throw std::runtime_error("NFQueueSequence: too many queues!"); - } - for (int i=0;i(_init+i); - }catch(const std::invalid_argument e){ - for(int j = 0; j < i; j++) { - delete nfq[j]; - nfq[j] = nullptr; - } - _init += seq_len - i; - break; - } - } - } - _end = _init + seq_len - 1; - } - - void start(){ - if (threads.size() != 0) throw std::runtime_error("NFQueueSequence: already started!"); - for (int i=0;i::run, nfq[i])); - } - } - - void join(){ - for (int i=0;i -#include -#include -#include -#include "../utils.hpp" - - -#ifndef REGEX_FILTER_HPP -#define REGEX_FILTER_HPP - -typedef jpcre2::select jp; -typedef std::pair regex_rule_pair; -typedef std::vector regex_rule_vector; -struct regex_rules{ - regex_rule_vector output_whitelist, input_whitelist, output_blacklist, input_blacklist; - - regex_rule_vector* getByCode(char code){ - switch(code){ - case 'C': // Client to server Blacklist - return &input_blacklist; break; - case 'c': // Client to server Whitelist - return &input_whitelist; break; - case 'S': // Server to client Blacklist - return &output_blacklist; break; - case 's': // Server to client Whitelist - return &output_whitelist; break; - } - throw std::invalid_argument( "Expected 'C' 'c' 'S' or 's'" ); - } - - int add(const char* arg){ - //Integrity checks - size_t arg_len = strlen(arg); - if (arg_len < 2 || arg_len%2 != 0){ - std::cerr << "[warning] [regex_rules.add] invalid arg passed (" << arg << "), skipping..." << std::endl; - return -1; - } - if (arg[0] != '0' && arg[0] != '1'){ - std::cerr << "[warning] [regex_rules.add] invalid is_case_sensitive (" << arg[0] << ") in '" << arg << "', must be '1' or '0', skipping..." << std::endl; - return -1; - } - if (arg[1] != 'C' && arg[1] != 'c' && arg[1] != 'S' && arg[1] != 's'){ - std::cerr << "[warning] [regex_rules.add] invalid filter_type (" << arg[1] << ") in '" << arg << "', must be 'C', 'c', 'S' or 's', skipping..." << std::endl; - return -1; - } - std::string hex(arg+2), expr; - if (!unhexlify(hex, expr)){ - std::cerr << "[warning] [regex_rules.add] invalid hex regex value (" << hex << "), skipping..." << std::endl; - return -1; - } - //Push regex - jp::Regex regex(expr,arg[0] == '1'?"gS":"giS"); - if (regex){ - std::cerr << "[info] [regex_rules.add] adding new regex filter: '" << expr << "'" << std::endl; - getByCode(arg[1])->push_back(std::make_pair(std::string(arg), regex)); - } else { - std::cerr << "[warning] [regex_rules.add] compiling of '" << expr << "' regex failed, skipping..." << std::endl; - return -1; - } - return 0; - } - - bool check(unsigned char* data, const size_t& bytes_transferred, const bool in_input){ - std::string str_data((char *) data, bytes_transferred); - for (regex_rule_pair ele:(in_input?input_blacklist:output_blacklist)){ - try{ - if(ele.second.match(str_data)){ - std::stringstream msg; - msg << "BLOCKED " << ele.first << "\n"; - std::cout << msg.str() << std::flush; - return false; - } - } catch(...){ - std::cerr << "[info] [regex_rules.check] Error while matching blacklist regex: " << ele.first << std::endl; - } - } - for (regex_rule_pair ele:(in_input?input_whitelist:output_whitelist)){ - try{ - std::cerr << "[debug] [regex_rules.check] regex whitelist match " << ele.second.getPattern() << std::endl; - if(!ele.second.match(str_data)){ - std::stringstream msg; - msg << "BLOCKED " << ele.first << "\n"; - std::cout << msg.str() << std::flush; - return false; - } - } catch(...){ - std::cerr << "[info] [regex_rules.check] Error while matching whitelist regex: " << ele.first << std::endl; - } - } - return true; - } - -}; - -#endif // REGEX_FILTER_HPP \ No newline at end of file diff --git a/backend/binsrc/classes/regex_rules.cpp b/backend/binsrc/classes/regex_rules.cpp new file mode 100644 index 0000000..2c2dc50 --- /dev/null +++ b/backend/binsrc/classes/regex_rules.cpp @@ -0,0 +1,161 @@ +#include +#include +#include +#include "../utils.hpp" +#include +#include + +using namespace std; + + +#ifndef REGEX_FILTER_HPP +#define REGEX_FILTER_HPP + +enum FilterDirection{ CTOS, STOC }; + +struct decoded_regex { + string regex; + FilterDirection direction; + bool is_case_sensitive; +}; + +struct regex_ruleset { + hs_database_t* hs_db; + char** regexes; +}; + +decoded_regex decode_regex(string regex){ + + size_t arg_len = regex.size(); + if (arg_len < 2 || arg_len%2 != 0){ + cerr << "[warning] [decode_regex] invalid arg passed (" << regex << "), skipping..." << endl; + throw runtime_error( "Invalid expression len (too small)" ); + } + if (regex[0] != '0' && regex[0] != '1'){ + cerr << "[warning] [decode_regex] invalid is_case_sensitive (" << regex[0] << ") in '" << regex << "', must be '1' or '0', skipping..." << endl; + throw runtime_error( "Invalid is_case_sensitive" ); + } + if (regex[1] != 'C' && regex[1] != 'S'){ + cerr << "[warning] [decode_regex] invalid filter_direction (" << regex[1] << ") in '" << regex << "', must be 'C', 'S', skipping..." << endl; + throw runtime_error( "Invalid filter_direction" ); + } + string hex(regex.c_str()+2), expr; + if (!unhexlify(hex, expr)){ + cerr << "[warning] [decode_regex] invalid hex regex value (" << hex << "), skipping..." << endl; + throw runtime_error( "Invalid hex regex encoded value" ); + } + decoded_regex ruleset{ + regex: expr, + direction: regex[1] == 'C'? CTOS : STOC, + is_case_sensitive: regex[0] == '1' + }; + return ruleset; +} + +class RegexRules{ + public: + regex_ruleset output_ruleset, input_ruleset; + + private: + static inline u_int16_t glob_seq = 0; + u_int16_t version; + vector> decoded_input_rules; + vector> decoded_output_rules; + bool is_stream = true; + + void free_dbs(){ + if (output_ruleset.hs_db != nullptr){ + hs_free_database(output_ruleset.hs_db); + } + if (input_ruleset.hs_db != nullptr){ + hs_free_database(input_ruleset.hs_db); + } + } + + void fill_ruleset(vector> & decoded, regex_ruleset & ruleset){ + size_t n_of_regex = decoded.size(); + if (n_of_regex == 0){ + return; + } + const char* regex_match_rules[n_of_regex]; + unsigned int regex_array_ids[n_of_regex]; + unsigned int regex_flags[n_of_regex]; + for(int i = 0; i < n_of_regex; i++){ + regex_match_rules[i] = decoded[i].second.regex.c_str(); + regex_array_ids[i] = i; + regex_flags[i] = HS_FLAG_SINGLEMATCH | HS_FLAG_ALLOWEMPTY; + if (!decoded[i].second.is_case_sensitive){ + regex_flags[i] |= HS_FLAG_CASELESS; + } + } + + hs_database_t* rebuilt_db; + hs_compile_error_t *compile_err; + if ( + hs_compile_multi( + regex_match_rules, + regex_flags, + regex_array_ids, + n_of_regex, + is_stream?HS_MODE_STREAM:HS_MODE_BLOCK, + nullptr,&rebuilt_db, &compile_err + ) != HS_SUCCESS + ) { + cerr << "[warning] [RegexRules.fill_ruleset] hs_db failed to compile: '" << compile_err->message << "' skipping..." << endl; + hs_free_compile_error(compile_err); + throw runtime_error( "Failed to compile hyperscan db" ); + } + ruleset.hs_db = rebuilt_db; + } + + public: + RegexRules(vector raw_rules, bool is_stream){ + this->is_stream = is_stream; + this->version = ++glob_seq; // 0 version is a invalid version (useful for some logics) + for(string ele : raw_rules){ + try{ + decoded_regex rule = decode_regex(ele); + if (rule.direction == FilterDirection::CTOS){ + decoded_input_rules.push_back(make_pair(ele, rule)); + }else{ + decoded_output_rules.push_back(make_pair(ele, rule)); + } + }catch(...){ + throw current_exception(); + } + } + fill_ruleset(decoded_input_rules, input_ruleset); + try{ + fill_ruleset(decoded_output_rules, output_ruleset); + }catch(...){ + free_dbs(); + throw current_exception(); + } + } + + u_int16_t ver(){ + return version; + } + + RegexRules(bool is_stream){ + vector no_rules; + RegexRules(no_rules, is_stream); + } + + bool stream_mode(){ + return is_stream; + } + + + + RegexRules(){ + RegexRules(true); + } + + ~RegexRules(){ + free_dbs(); + } +}; + +#endif // REGEX_FILTER_HPP + diff --git a/backend/binsrc/cppqueue b/backend/binsrc/cppqueue new file mode 100755 index 0000000000000000000000000000000000000000..84696ecc35574763c2913f0ff6d098718a563c61 GIT binary patch literal 282672 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�p9|Dnw?+2=BoPhdiZ8|zgXA` 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

c*ro_&@K%V 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 using namespace std; -shared_ptr regex_config; +shared_ptr regex_config; void config_updater (){ string line; @@ -21,44 +21,116 @@ void config_updater (){ } cerr << "[info] [updater] Updating configuration with line " << line << endl; istringstream config_stream(line); - regex_rules *regex_new_config = new regex_rules(); + vector raw_rules; + while(!config_stream.eof()){ string data; config_stream >> data; if (data != "" && data != "\n"){ - regex_new_config->add(data.c_str()); + raw_rules.push_back(data); } } - regex_config.reset(regex_new_config); - cerr << "[info] [updater] Config update done" << endl; - + try{ + regex_config.reset(new RegexRules(raw_rules, regex_config->stream_mode())); + cerr << "[info] [updater] Config update done" << endl; + }catch(...){ + cerr << "[error] [updater] Failed to build new configuration!" << endl; + // TODO send a row on stdout for this error + } } } -template -bool filter_callback(const uint8_t *data, uint32_t len){ - shared_ptr current_config = regex_config; - return current_config->check((unsigned char *)data, len, is_input); +void inline scratch_setup(regex_ruleset &conf, hs_scratch_t* & scratch){ + if (scratch == nullptr){ + if (hs_alloc_scratch(conf.hs_db, &scratch) != HS_SUCCESS) { + throw invalid_argument("Cannot alloc scratch"); + } + } } -int main(int argc, char *argv[]) -{ +struct matched_data{ + unsigned int matched = 0; + bool has_matched = false; +}; + +bool filter_callback(packet_info & info){ + shared_ptr conf = regex_config; + if (conf->ver() != info.sctx->latest_config_ver){ + info.sctx->clean_scratches(); + } + scratch_setup(conf->input_ruleset, info.sctx->in_scratch); + scratch_setup(conf->output_ruleset, info.sctx->out_scratch); + + hs_database_t* regex_matcher = info.is_input ? conf->input_ruleset.hs_db : conf->output_ruleset.hs_db; + if (regex_matcher == nullptr){ + return true; + } + matched_data match_res; + hs_error_t err; + hs_scratch_t* scratch_space = info.is_input ? info.sctx->in_scratch: info.sctx->out_scratch; + auto match_func = [](unsigned int id, auto from, auto to, auto flags, auto ctx){ + auto res = (matched_data*)ctx; + res->has_matched = true; + res->matched = id; + return 1; // Stop matching + }; + if (conf->stream_mode()){ + matching_map match_map = info.is_input ? info.sctx->in_hs_streams : info.sctx->out_hs_streams; + auto stream_search = match_map.find(info.stream_id); + hs_stream_t* stream_match; + if (stream_search == match_map.end()){ + if (hs_open_stream(regex_matcher, 0, &stream_match) != HS_SUCCESS) { + cerr << "[error] [filter_callback] Error opening the stream matcher (hs)" << endl; + throw invalid_argument("Cannot open stream match on hyperscan"); + } + match_map[info.stream_id] = stream_match; + }else{ + stream_match = stream_search->second; + } + err = hs_scan_stream( + stream_match,info.payload.c_str(), info.payload.length(), + 0, scratch_space, match_func, &match_res + ); + }else{ + err = hs_scan( + regex_matcher,info.payload.c_str(), info.payload.length(), + 0, scratch_space, match_func, &match_res + ); + } + if (err != HS_SUCCESS) { + cerr << "[error] [filter_callback] Error while matching the stream (hs)" << endl; + throw invalid_argument("Error while matching the stream with hyperscan"); + } + if (match_res.has_matched){ + auto rules_vector = info.is_input ? conf->input_ruleset.regexes : conf->output_ruleset.regexes; + stringstream msg; + msg << "BLOCKED " << rules_vector[match_res.matched] << "\n"; + cout << msg.str() << flush; + return false; + } + return true; +} + +int main(int argc, char *argv[]){ int n_of_threads = 1; char * n_threads_str = getenv("NTHREADS"); - if (n_threads_str != NULL) n_of_threads = ::atoi(n_threads_str); + if (n_threads_str != nullptr) n_of_threads = ::atoi(n_threads_str); if(n_of_threads <= 0) n_of_threads = 1; - if (n_of_threads % 2 != 0 ) n_of_threads++; - cerr << "[info] [main] Using " << n_of_threads << " threads" << endl; - regex_config.reset(new regex_rules()); - NFQueueSequence> input_queues(n_of_threads/2); - input_queues.start(); - NFQueueSequence> output_queues(n_of_threads/2); - output_queues.start(); - cout << "QUEUES INPUT " << input_queues.init() << " " << input_queues.end() << " OUTPUT " << output_queues.init() << " " << output_queues.end() << endl; - cerr << "[info] [main] Input queues: " << input_queues.init() << ":" << input_queues.end() << " threads assigned: " << n_of_threads/2 << endl; - cerr << "[info] [main] Output queues: " << output_queues.init() << ":" << output_queues.end() << " threads assigned: " << n_of_threads/2 << endl; + char * matchmode = getenv("MATCH_MODE"); + bool stream_mode = true; + if (matchmode != nullptr && strcmp(matchmode, "block") == 0){ + stream_mode = false; + } + cerr << "[info] [main] Using " << n_of_threads << " threads" << endl; + regex_config.reset(new RegexRules(stream_mode)); + + NFQueueSequence queues(n_of_threads); + queues.start(); + + cout << "QUEUES " << queues.init() << " " << queues.end() << endl; + cerr << "[info] [main] Queues: " << queues.init() << ":" << queues.end() << " threads assigned: " << n_of_threads << endl; config_updater(); } diff --git a/backend/binsrc/nfqueue_regex/Cargo.lock b/backend/binsrc/nfqueue_regex/Cargo.lock deleted file mode 100644 index 11e8d94..0000000 --- a/backend/binsrc/nfqueue_regex/Cargo.lock +++ /dev/null @@ -1,32 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "atomic_refcell" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "nfq" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c8f4c88952507d9df9400a6a2e48640fb460e21dcb2b4716eb3ff156d6db9e" -dependencies = [ - "libc", -] - -[[package]] -name = "nfqueue_regex" -version = "0.1.0" -dependencies = [ - "atomic_refcell", - "nfq", -] diff --git a/backend/binsrc/nfqueue_regex/Cargo.toml b/backend/binsrc/nfqueue_regex/Cargo.toml deleted file mode 100644 index b9b0e0a..0000000 --- a/backend/binsrc/nfqueue_regex/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "nfqueue_regex" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -atomic_refcell = "0.1.13" -nfq = "0.2.5" -#hyperscan = "0.3.2" diff --git a/backend/binsrc/nfqueue_regex/src/main.rs b/backend/binsrc/nfqueue_regex/src/main.rs deleted file mode 100644 index 639dfd5..0000000 --- a/backend/binsrc/nfqueue_regex/src/main.rs +++ /dev/null @@ -1,150 +0,0 @@ -use atomic_refcell::AtomicRefCell; -use nfq::{Queue, Verdict}; -use std::cell::{Cell, RefCell}; -use std::env; -use std::pin::Pin; -use std::rc::Rc; -use std::sync::atomic::{AtomicPtr, AtomicU32}; -use std::sync::mpsc::{self, Receiver, Sender}; -use std::sync::Arc; -use std::thread::{self, sleep, sleep_ms, JoinHandle}; - -enum WorkerMessage { - Error(String), - Dropped(usize), -} - -impl ToString for WorkerMessage { - fn to_string(&self) -> String { - match self { - WorkerMessage::Error(e) => format!("E{}", e), - WorkerMessage::Dropped(d) => format!("D{}", d), - } - } -} -struct Pool { - _workers: Vec, - pub start: u16, - pub end: u16, -} - -const QUEUE_BASE_NUM: u16 = 1000; -impl Pool { - fn new(threads: u16, tx: Sender, db: RefCell<&str>) -> Self { - // Find free queues - let mut start = QUEUE_BASE_NUM; - let mut queues: Vec<(Queue, u16)> = vec![]; - while queues.len() != threads.into() { - for queue_num in - (start..start.checked_add(threads + 1).expect("No more queues left")).rev() - { - let mut queue = Queue::open().unwrap(); - if queue.bind(queue_num).is_err() { - start = queue_num; - while let Some((mut q, num)) = queues.pop() { - let _ = q.unbind(num); - } - break; - }; - queues.push((queue, queue_num)); - } - } - - Pool { - _workers: queues - .into_iter() - .map(|(queue, queue_num)| Worker::new(queue, queue_num, tx.clone())) - .collect(), - start, - end: (start + threads), - } - } - - // fn join(self) { - // for worker in self._workers { - // let _ = worker.join(); - // } - // } -} - -struct Worker { - _inner: JoinHandle<()>, -} - -impl Worker { - fn new(mut queue: Queue, _queue_num: u16, tx: Sender) -> Self { - Worker { - _inner: thread::spawn(move || loop { - let mut msg = queue.recv().unwrap_or_else(|_| { - let _ = tx.send(WorkerMessage::Error("Fuck".to_string())); - panic!(""); - }); - - msg.set_verdict(Verdict::Accept); - queue.verdict(msg).unwrap(); - }), - } - } -} -struct InputOuputPools { - pub output_queue: Pool, - pub input_queue: Pool, - rx: Receiver, -} -impl InputOuputPools { - fn new(threads: u16) -> InputOuputPools { - let (tx, rx) = mpsc::channel(); - InputOuputPools { - output_queue: Pool::new(threads / 2, tx.clone(), RefCell::new("ciao")), - input_queue: Pool::new(threads / 2, tx, RefCell::new("miao")), - rx, - } - } - - fn poll_events(&self) { - loop { - let event = self.rx.recv().expect("Channel has hung up"); - println!("{}", event.to_string()); - } - } -} - -static mut DB: AtomicPtr> = AtomicPtr::new(std::ptr::null_mut() as *mut Arc); - -fn main() -> std::io::Result<()> { - let mut my_x: Arc = Arc::new(0); - let my_x_ptr: *mut Arc = std::ptr::addr_of_mut!(my_x); - - unsafe { DB.store(my_x_ptr, std::sync::atomic::Ordering::SeqCst) }; - - thread::spawn(|| loop { - let x_ptr = unsafe { DB.load(std::sync::atomic::Ordering::SeqCst) }; - let x = unsafe { (*x_ptr).clone() }; - dbg!(x); - //sleep_ms(1000); - }); - - for i in 0..1000000000 { - let mut my_x: Arc = Arc::new(i); - let my_x_ptr: *mut Arc = std::ptr::addr_of_mut!(my_x); - unsafe { DB.store(my_x_ptr, std::sync::atomic::Ordering::SeqCst) }; - //sleep_ms(100); - } - - let mut threads = env::var("NPROCS").unwrap_or_default().parse().unwrap_or(2); - if threads % 2 != 0 { - threads += 1; - } - - let in_out_pools = InputOuputPools::new(threads); - eprintln!( - "[info] [main] Input queues: {}:{}", - in_out_pools.input_queue.start, in_out_pools.input_queue.end - ); - eprintln!( - "[info] [main] Output queues: {}:{}", - in_out_pools.output_queue.start, in_out_pools.output_queue.end - ); - in_out_pools.poll_events(); - Ok(()) -} diff --git a/backend/binsrc/nfqueue_regex/src/regex_rules.rs b/backend/binsrc/nfqueue_regex/src/regex_rules.rs deleted file mode 100644 index 8b13789..0000000 --- a/backend/binsrc/nfqueue_regex/src/regex_rules.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/backend/binsrc/proxy.cpp b/backend/binsrc/proxy.cpp deleted file mode 100644 index d572667..0000000 --- a/backend/binsrc/proxy.cpp +++ /dev/null @@ -1,493 +0,0 @@ -/* - Copyright (c) 2007 Arash Partow (http://www.partow.net) - URL: http://www.partow.net/programming/tcpproxy/index.html - Modified and adapted by Pwnzer0tt1 -*/ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -typedef jpcre2::select jp; -using namespace std; - -bool unhexlify(string const &hex, string &newString) { - try{ - int len = hex.length(); - for(int i=0; i< len; i+=2) - { - std::string byte = hex.substr(i,2); - char chr = (char) (int)strtol(byte.c_str(), NULL, 16); - newString.push_back(chr); - } - return true; - } - catch (...){ - return false; - } -} - -typedef pair regex_rule_pair; -typedef vector regex_rule_vector; -struct regex_rules{ - regex_rule_vector regex_s_c_w, regex_c_s_w, regex_s_c_b, regex_c_s_b; - - regex_rule_vector* getByCode(char code){ - switch(code){ - case 'C': // Client to server Blacklist - return ®ex_c_s_b; break; - case 'c': // Client to server Whitelist - return ®ex_c_s_w; break; - case 'S': // Server to client Blacklist - return ®ex_s_c_b; break; - case 's': // Server to client Whitelist - return ®ex_s_c_w; break; - } - throw invalid_argument( "Expected 'C' 'c' 'S' or 's'" ); - } - - void add(const char* arg){ - - //Integrity checks - size_t arg_len = strlen(arg); - if (arg_len < 2 || arg_len%2 != 0) return; - if (arg[0] != '0' && arg[0] != '1') return; - if (arg[1] != 'C' && arg[1] != 'c' && arg[1] != 'S' && arg[1] != 's') return; - string hex(arg+2), expr; - if (!unhexlify(hex, expr)) return; - //Push regex - jp::Regex regex(expr,arg[0] == '1'?"gS":"giS"); - if (regex){ - #ifdef DEBUG - cerr << "Added regex " << expr << " " << arg << endl; - #endif - getByCode(arg[1])->push_back(make_pair(string(arg), regex)); - } else { - cerr << "Regex " << arg << " was not compiled successfully" << endl; - } - } - -}; -shared_ptr regex_config; - -mutex update_mutex; - -bool filter_data(unsigned char* data, const size_t& bytes_transferred, regex_rule_vector const &blacklist, regex_rule_vector const &whitelist){ - #ifdef DEBUG_PACKET - cerr << "---------------- Packet ----------------" << endl; - for(int i=0;i - { - public: - - typedef ip::tcp::socket socket_type; - typedef boost::shared_ptr ptr_type; - - bridge(boost::asio::io_context& ios) - : downstream_socket_(ios), - upstream_socket_ (ios), - thread_safety(ios) - {} - - socket_type& downstream_socket() - { - // Client socket - return downstream_socket_; - } - - socket_type& upstream_socket() - { - // Remote server socket - return upstream_socket_; - } - - void start(const string& upstream_host, unsigned short upstream_port) - { - // Attempt connection to remote server (upstream side) - upstream_socket_.async_connect( - ip::tcp::endpoint( - boost::asio::ip::address::from_string(upstream_host), - upstream_port), - boost::asio::bind_executor(thread_safety, - boost::bind( - &bridge::handle_upstream_connect, - shared_from_this(), - boost::asio::placeholders::error))); - } - - void handle_upstream_connect(const boost::system::error_code& error) - { - if (!error) - { - // Setup async read from remote server (upstream) - - upstream_socket_.async_read_some( - boost::asio::buffer(upstream_data_,max_data_length), - boost::asio::bind_executor(thread_safety, - boost::bind(&bridge::handle_upstream_read, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); - - // Setup async read from client (downstream) - downstream_socket_.async_read_some( - boost::asio::buffer(downstream_data_,max_data_length), - boost::asio::bind_executor(thread_safety, - boost::bind(&bridge::handle_downstream_read, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); - } - else - close(); - } - - private: - - /* - Section A: Remote Server --> Proxy --> Client - Process data recieved from remote sever then send to client. - */ - - // Read from remote server complete, now send data to client - void handle_upstream_read(const boost::system::error_code& error, - const size_t& bytes_transferred) // Da Server a Client - { - if (!error) - { - shared_ptr regex_old_config = regex_config; - if (filter_data(upstream_data_, bytes_transferred, regex_old_config->regex_s_c_b, regex_old_config->regex_s_c_w)){ - async_write(downstream_socket_, - boost::asio::buffer(upstream_data_,bytes_transferred), - boost::asio::bind_executor(thread_safety, - boost::bind(&bridge::handle_downstream_write, - shared_from_this(), - boost::asio::placeholders::error))); - }else{ - close(); - } - } - else - close(); - } - - // Write to client complete, Async read from remote server - void handle_downstream_write(const boost::system::error_code& error) - { - if (!error) - { - - upstream_socket_.async_read_some( - boost::asio::buffer(upstream_data_,max_data_length), - boost::asio::bind_executor(thread_safety, - boost::bind(&bridge::handle_upstream_read, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); - } - else - close(); - } - // *** End Of Section A *** - - - /* - Section B: Client --> Proxy --> Remove Server - Process data recieved from client then write to remove server. - */ - - // Read from client complete, now send data to remote server - void handle_downstream_read(const boost::system::error_code& error, - const size_t& bytes_transferred) // Da Client a Server - { - if (!error) - { - shared_ptr regex_old_config = regex_config; - if (filter_data(downstream_data_, bytes_transferred, regex_old_config->regex_c_s_b, regex_old_config->regex_c_s_w)){ - async_write(upstream_socket_, - boost::asio::buffer(downstream_data_,bytes_transferred), - boost::asio::bind_executor(thread_safety, - boost::bind(&bridge::handle_upstream_write, - shared_from_this(), - boost::asio::placeholders::error))); - }else{ - close(); - } - } - else - close(); - } - - // Write to remote server complete, Async read from client - void handle_upstream_write(const boost::system::error_code& error) - { - if (!error) - { - downstream_socket_.async_read_some( - boost::asio::buffer(downstream_data_,max_data_length), - boost::asio::bind_executor(thread_safety, - boost::bind(&bridge::handle_downstream_read, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); - } - else - close(); - } - // *** End Of Section B *** - - void close() - { - boost::mutex::scoped_lock lock(mutex_); - - if (downstream_socket_.is_open()) - { - downstream_socket_.close(); - } - - if (upstream_socket_.is_open()) - { - upstream_socket_.close(); - } - } - - socket_type downstream_socket_; - socket_type upstream_socket_; - - enum { max_data_length = 8192 }; //8KB - unsigned char downstream_data_[max_data_length]; - unsigned char upstream_data_ [max_data_length]; - boost::asio::io_context::strand thread_safety; - boost::mutex mutex_; - public: - - class acceptor - { - public: - - acceptor(boost::asio::io_context& io_context, - const string& local_host, unsigned short local_port, - const string& upstream_host, unsigned short upstream_port) - : io_context_(io_context), - localhost_address(boost::asio::ip::address_v4::from_string(local_host)), - acceptor_(io_context_,ip::tcp::endpoint(localhost_address,local_port)), - upstream_port_(upstream_port), - upstream_host_(upstream_host) - {} - - bool accept_connections() - { - try - { - session_ = boost::shared_ptr(new bridge(io_context_)); - - acceptor_.async_accept(session_->downstream_socket(), - boost::asio::bind_executor(session_->thread_safety, - boost::bind(&acceptor::handle_accept, - this, - boost::asio::placeholders::error))); - } - catch(exception& e) - { - cerr << "acceptor exception: " << e.what() << endl; - return false; - } - - return true; - } - - private: - - void handle_accept(const boost::system::error_code& error) - { - if (!error) - { - session_->start(upstream_host_,upstream_port_); - - if (!accept_connections()) - { - cerr << "Failure during call to accept." << endl; - } - } - else - { - cerr << "Error: " << error.message() << endl; - } - } - - boost::asio::io_context& io_context_; - ip::address_v4 localhost_address; - ip::tcp::acceptor acceptor_; - ptr_type session_; - unsigned short upstream_port_; - string upstream_host_; - }; - - }; -} - -void update_config (boost::asio::streambuf &input_buffer){ - #ifdef DEBUG - cerr << "Updating configuration" << endl; - #endif - std::istream config_stream(&input_buffer); - std::unique_lock lck(update_mutex); - regex_rules *regex_new_config = new regex_rules(); - string data; - while(true){ - config_stream >> data; - if (config_stream.eof()) break; - regex_new_config->add(data.c_str()); - } - regex_config.reset(regex_new_config); -} - -class async_updater -{ -public: - async_updater(boost::asio::io_context& io_context) : input_(io_context, ::dup(STDIN_FILENO)), thread_safety(io_context) - { - - boost::asio::async_read_until(input_, input_buffer_, '\n', - boost::asio::bind_executor(thread_safety, - boost::bind(&async_updater::on_update, this, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); - } - - void on_update(const boost::system::error_code& error, std::size_t length) - { - if (!error) - { - update_config(input_buffer_); - boost::asio::async_read_until(input_, input_buffer_, '\n', - boost::asio::bind_executor(thread_safety, - boost::bind(&async_updater::on_update, this, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred))); - } - else - { - close(); - } - } - - void close() - { - input_.close(); - } - -private: - boost::asio::posix::stream_descriptor input_; - boost::asio::io_context::strand thread_safety; - boost::asio::streambuf input_buffer_; -}; - - -int main(int argc, char* argv[]) -{ - if (argc < 5) - { - cerr << "usage: tcpproxy_server " << endl; - return 1; - } - - const unsigned short local_port = static_cast(::atoi(argv[2])); - const unsigned short forward_port = static_cast(::atoi(argv[4])); - const string local_host = argv[1]; - const string forward_host = argv[3]; - - int threads = 1; - char * n_threads_str = getenv("NTHREADS"); - if (n_threads_str != NULL) threads = ::atoi(n_threads_str); - - boost::asio::io_context ios; - - boost::asio::streambuf buf; - boost::asio::posix::stream_descriptor cin_in(ios, ::dup(STDIN_FILENO)); - boost::asio::read_until(cin_in, buf,'\n'); - update_config(buf); - - async_updater updater(ios); - - #ifdef DEBUG - cerr << "Starting Proxy" << endl; - #endif - try - { - tcp_proxy::bridge::acceptor acceptor(ios, - local_host, local_port, - forward_host, forward_port); - - acceptor.accept_connections(); - - if (threads > 1){ - boost::thread_group tg; - for (unsigned i = 0; i < threads; ++i) - tg.create_thread(boost::bind(&boost::asio::io_context::run, &ios)); - - tg.join_all(); - }else{ - ios.run(); - } - } - catch(exception& e) - { - cerr << "Error: " << e.what() << endl; - return 1; - } - #ifdef DEBUG - cerr << "Proxy stopped!" << endl; - #endif - - return 0; -} diff --git a/backend/binsrc/utils.hpp b/backend/binsrc/utils.hpp index 9d40366..b61ef22 100644 --- a/backend/binsrc/utils.hpp +++ b/backend/binsrc/utils.hpp @@ -10,7 +10,7 @@ bool unhexlify(std::string const &hex, std::string &newString) { for(int i=0; i< len; i+=2) { std::string byte = hex.substr(i,2); - char chr = (char) (int)strtol(byte.c_str(), NULL, 16); + char chr = (char) (int)strtol(byte.c_str(), nullptr, 16); newString.push_back(chr); } return true; diff --git a/backend/modules/firewall/firewall.py b/backend/modules/firewall/firewall.py index 13c0122..b5bb292 100644 --- a/backend/modules/firewall/firewall.py +++ b/backend/modules/firewall/firewall.py @@ -1,6 +1,6 @@ import asyncio from modules.firewall.nftables import FiregexTables -from modules.firewall.models import * +from modules.firewall.models import Rule, FirewallSettings from utils.sqlite import SQLite from modules.firewall.models import Action @@ -131,5 +131,5 @@ class FirewallManager: return self.db.get("allow_dhcp", "1") == "1" @drop_invalid.setter - def allow_dhcp(self, value): + def allow_dhcp_set(self, value): self.db.set("allow_dhcp", "1" if value else "0") diff --git a/backend/modules/nfregex/firegex.py b/backend/modules/nfregex/firegex.py index 8d57d5d..b04409a 100644 --- a/backend/modules/nfregex/firegex.py +++ b/backend/modules/nfregex/firegex.py @@ -1,7 +1,9 @@ from modules.nfregex.nftables import FiregexTables -from utils import ip_parse, run_func +from utils import run_func from modules.nfregex.models import Service, Regex -import re, os, asyncio +import re +import os +import asyncio import traceback nft = FiregexTables() @@ -20,7 +22,8 @@ class RegexFilter: self.regex = regex self.is_case_sensitive = is_case_sensitive self.is_blacklist = is_blacklist - if input_mode == output_mode: input_mode = output_mode = True # (False, False) == (True, True) + if input_mode == output_mode: + input_mode = output_mode = True # (False, False) == (True, True) self.input_mode = input_mode self.output_mode = output_mode self.blocked = blocked_packets @@ -37,8 +40,10 @@ class RegexFilter: update_func = update_func ) def compile(self): - if isinstance(self.regex, str): self.regex = self.regex.encode() - if not isinstance(self.regex, bytes): raise Exception("Invalid Regex Paramether") + if isinstance(self.regex, str): + self.regex = self.regex.encode() + if not isinstance(self.regex, bytes): + raise Exception("Invalid Regex Paramether") re.compile(self.regex) # raise re.error if it's invalid! case_sensitive = "1" if self.is_case_sensitive else "0" if self.input_mode: @@ -67,9 +72,9 @@ class FiregexInterceptor: self.srv = srv self.filter_map_lock = asyncio.Lock() self.update_config_lock = asyncio.Lock() - input_range, output_range = await self._start_binary() + queue_range = await self._start_binary() self.update_task = asyncio.create_task(self.update_blocked()) - nft.add(self.srv, input_range, output_range) + nft.add(self.srv, queue_range) return self async def _start_binary(self): @@ -87,7 +92,7 @@ class FiregexInterceptor: line = line_fut.decode() if line.startswith("QUEUES "): params = line.split() - return (int(params[2]), int(params[3])), (int(params[5]), int(params[6])) + return (int(params[1]), int(params[2])) else: self.process.kill() raise Exception("Invalid binary output") @@ -102,8 +107,10 @@ class FiregexInterceptor: if regex_id in self.filter_map: self.filter_map[regex_id].blocked+=1 await self.filter_map[regex_id].update() - except asyncio.CancelledError: pass - except asyncio.IncompleteReadError: pass + except asyncio.CancelledError: + pass + except asyncio.IncompleteReadError: + pass except Exception: traceback.print_exc() @@ -135,6 +142,7 @@ class FiregexInterceptor: raw_filters = filter_obj.compile() for filter in raw_filters: res[filter] = filter_obj - except Exception: pass + except Exception: + pass return res diff --git a/backend/modules/nfregex/firewall.py b/backend/modules/nfregex/firewall.py index 9516f63..d0d5479 100644 --- a/backend/modules/nfregex/firewall.py +++ b/backend/modules/nfregex/firewall.py @@ -30,14 +30,15 @@ class ServiceManager: new_filters = set([f.id for f in regexes]) #remove old filters for f in old_filters: - if not f in new_filters: + if f not in new_filters: del self.filters[f] #add new filters for f in new_filters: - if not f in old_filters: + if f not in old_filters: filter = [ele for ele in regexes if ele.id == f][0] self.filters[f] = RegexFilter.from_regex(filter, self._stats_updater) - if self.interceptor: await self.interceptor.reload(self.filters.values()) + if self.interceptor: + await self.interceptor.reload(self.filters.values()) def __update_status_db(self, status): self.db.query("UPDATE services SET status = ? WHERE service_id = ?;", status, self.srv.id) @@ -114,4 +115,5 @@ class FirewallManager: else: raise ServiceNotFoundException() -class ServiceNotFoundException(Exception): pass +class ServiceNotFoundException(Exception): + pass diff --git a/backend/modules/nfregex/nftables.py b/backend/modules/nfregex/nftables.py index a0bc917..beb5953 100644 --- a/backend/modules/nfregex/nftables.py +++ b/backend/modules/nfregex/nftables.py @@ -45,36 +45,35 @@ class FiregexTables(NFTableManager): {"delete":{"chain":{"table":self.table_name,"family":"inet", "name":self.output_chain}}}, ]) - def add(self, srv:Service, queue_range_input, queue_range_output): + def add(self, srv:Service, queue_range): for ele in self.get(): if ele.__eq__(srv): return - init, end = queue_range_output + init, end = queue_range if init > end: init, end = end, init - self.cmd({ "insert":{ "rule": { - "family": "inet", - "table": self.table_name, - "chain": self.output_chain, - "expr": [ - {'match': {'left': {'payload': {'protocol': ip_family(srv.ip_int), 'field': 'saddr'}}, 'op': '==', 'right': nftables_int_to_json(srv.ip_int)}}, - {'match': {"left": { "payload": {"protocol": str(srv.proto), "field": "sport"}}, "op": "==", "right": int(srv.port)}}, - {"queue": {"num": str(init) if init == end else {"range":[init, end] }, "flags": ["bypass"]}} + self.cmd( + { "insert":{ "rule": { + "family": "inet", + "table": self.table_name, + "chain": self.output_chain, + "expr": [ + {'match': {'left': {'payload': {'protocol': ip_family(srv.ip_int), 'field': 'saddr'}}, 'op': '==', 'right': nftables_int_to_json(srv.ip_int)}}, + {'match': {"left": { "payload": {"protocol": str(srv.proto), "field": "sport"}}, "op": "==", "right": int(srv.port)}}, + {"queue": {"num": str(init) if init == end else {"range":[init, end] }, "flags": ["bypass"]}} ] - }}}) - - init, end = queue_range_input - if init > end: init, end = end, init - self.cmd({"insert":{"rule":{ - "family": "inet", - "table": self.table_name, - "chain": self.input_chain, - "expr": [ - {'match': {'left': {'payload': {'protocol': ip_family(srv.ip_int), 'field': 'daddr'}}, 'op': '==', 'right': nftables_int_to_json(srv.ip_int)}}, - {'match': {"left": { "payload": {"protocol": str(srv.proto), "field": "dport"}}, "op": "==", "right": int(srv.port)}}, - {"queue": {"num": str(init) if init == end else {"range":[init, end] }, "flags": ["bypass"]}} - ] - }}}) + }}}, + {"insert":{"rule":{ + "family": "inet", + "table": self.table_name, + "chain": self.input_chain, + "expr": [ + {'match': {'left': {'payload': {'protocol': ip_family(srv.ip_int), 'field': 'daddr'}}, 'op': '==', 'right': nftables_int_to_json(srv.ip_int)}}, + {'match': {"left": { "payload": {"protocol": str(srv.proto), "field": "dport"}}, "op": "==", "right": int(srv.port)}}, + {"queue": {"num": str(init) if init == end else {"range":[init, end] }, "flags": ["bypass"]}} + ] + }}} + ) def get(self) -> list[FiregexFilter]: diff --git a/backend/modules/porthijack/firewall.py b/backend/modules/porthijack/firewall.py index 29e2f06..03004b2 100644 --- a/backend/modules/porthijack/firewall.py +++ b/backend/modules/porthijack/firewall.py @@ -5,7 +5,8 @@ from utils.sqlite import SQLite nft = FiregexTables() -class ServiceNotFoundException(Exception): pass +class ServiceNotFoundException(Exception): + pass class ServiceManager: def __init__(self, srv: Service, db): @@ -29,7 +30,8 @@ class ServiceManager: async def refresh(self, srv:Service): self.srv = srv - if self.active: await self.restart() + if self.active: + await self.restart() def _set_status(self,active): self.active = active diff --git a/backend/modules/porthijack/nftables.py b/backend/modules/porthijack/nftables.py index 34e0669..1d8dcde 100644 --- a/backend/modules/porthijack/nftables.py +++ b/backend/modules/porthijack/nftables.py @@ -50,7 +50,8 @@ class FiregexTables(NFTableManager): def add(self, srv:Service): for ele in self.get(): - if ele.__eq__(srv): return + if ele.__eq__(srv): + return self.cmd({ "insert":{ "rule": { "family": "inet", diff --git a/backend/modules/regexproxy/proxy.py b/backend/modules/regexproxy/proxy.py deleted file mode 100644 index efaa090..0000000 --- a/backend/modules/regexproxy/proxy.py +++ /dev/null @@ -1,116 +0,0 @@ -import re, os, asyncio - -class Filter: - def __init__(self, regex, is_case_sensitive=True, is_blacklist=True, c_to_s=False, s_to_c=False, blocked_packets=0, code=None): - self.regex = regex - self.is_case_sensitive = is_case_sensitive - self.is_blacklist = is_blacklist - if c_to_s == s_to_c: c_to_s = s_to_c = True # (False, False) == (True, True) - self.c_to_s = c_to_s - self.s_to_c = s_to_c - self.blocked = blocked_packets - self.code = code - - def compile(self): - if isinstance(self.regex, str): self.regex = self.regex.encode() - if not isinstance(self.regex, bytes): raise Exception("Invalid Regex Paramether") - re.compile(self.regex) # raise re.error if is invalid! - case_sensitive = "1" if self.is_case_sensitive else "0" - if self.c_to_s: - yield case_sensitive + "C" + self.regex.hex() if self.is_blacklist else case_sensitive + "c"+ self.regex.hex() - if self.s_to_c: - yield case_sensitive + "S" + self.regex.hex() if self.is_blacklist else case_sensitive + "s"+ self.regex.hex() - -class Proxy: - def __init__(self, internal_port=0, public_port=0, callback_blocked_update=None, filters=None, public_host="0.0.0.0", internal_host="127.0.0.1"): - self.filter_map = {} - self.filter_map_lock = asyncio.Lock() - self.update_config_lock = asyncio.Lock() - self.status_change = asyncio.Lock() - self.public_host = public_host - self.public_port = public_port - self.internal_host = internal_host - self.internal_port = internal_port - self.filters = set(filters) if filters else set([]) - self.process = None - self.callback_blocked_update = callback_blocked_update - - async def start(self, in_pause=False): - await self.status_change.acquire() - if not self.isactive(): - try: - self.filter_map = self.compile_filters() - filters_codes = self.get_filter_codes() if not in_pause else [] - proxy_binary_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),"../proxy") - - self.process = await asyncio.create_subprocess_exec( - proxy_binary_path, str(self.public_host), str(self.public_port), str(self.internal_host), str(self.internal_port), - stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE - ) - await self.update_config(filters_codes) - finally: - self.status_change.release() - try: - while True: - buff = await self.process.stdout.readuntil() - stdout_line = buff.decode() - if stdout_line.startswith("BLOCKED"): - regex_id = stdout_line.split()[1] - async with self.filter_map_lock: - if regex_id in self.filter_map: - self.filter_map[regex_id].blocked+=1 - if self.callback_blocked_update: self.callback_blocked_update(self.filter_map[regex_id]) - except Exception: - return await self.process.wait() - else: - self.status_change.release() - - - async def stop(self): - async with self.status_change: - if self.isactive(): - self.process.kill() - return False - return True - - async def restart(self, in_pause=False): - status = await self.stop() - await self.start(in_pause=in_pause) - return status - - async def update_config(self, filters_codes): - async with self.update_config_lock: - if (self.isactive()): - self.process.stdin.write((" ".join(filters_codes)+"\n").encode()) - await self.process.stdin.drain() - - async def reload(self): - if self.isactive(): - async with self.filter_map_lock: - self.filter_map = self.compile_filters() - filters_codes = self.get_filter_codes() - await self.update_config(filters_codes) - - def get_filter_codes(self): - filters_codes = list(self.filter_map.keys()) - filters_codes.sort(key=lambda a: self.filter_map[a].blocked, reverse=True) - return filters_codes - - def isactive(self): - return self.process and self.process.returncode is None - - async def pause(self): - if self.isactive(): - await self.update_config([]) - else: - await self.start(in_pause=True) - - def compile_filters(self): - res = {} - for filter_obj in self.filters: - try: - raw_filters = filter_obj.compile() - for filter in raw_filters: - res[filter] = filter_obj - except Exception: pass - return res diff --git a/backend/modules/regexproxy/utils.py b/backend/modules/regexproxy/utils.py deleted file mode 100644 index 2695b87..0000000 --- a/backend/modules/regexproxy/utils.py +++ /dev/null @@ -1,199 +0,0 @@ -import secrets -from modules.regexproxy.proxy import Filter, Proxy -import random, socket, asyncio -from base64 import b64decode -from utils.sqlite import SQLite -from utils import socketio_emit - -class STATUS: - WAIT = "wait" - STOP = "stop" - PAUSE = "pause" - ACTIVE = "active" - -class ServiceNotFoundException(Exception): pass - -class ServiceManager: - def __init__(self, id, db): - self.id = id - self.db = db - self.proxy = Proxy( - internal_host="127.0.0.1", - callback_blocked_update=self._stats_updater - ) - self.status = STATUS.STOP - self.wanted_status = STATUS.STOP - self.filters = {} - self._update_port_from_db() - self._update_filters_from_db() - self.lock = asyncio.Lock() - self.starter = None - - def _update_port_from_db(self): - res = self.db.query(""" - SELECT - public_port, - internal_port - FROM services WHERE service_id = ?; - """, self.id) - if len(res) == 0: raise ServiceNotFoundException() - self.proxy.internal_port = res[0]["internal_port"] - self.proxy.public_port = res[0]["public_port"] - - def _update_filters_from_db(self): - res = self.db.query(""" - SELECT - regex, mode, regex_id `id`, is_blacklist, - blocked_packets n_packets, is_case_sensitive - FROM regexes WHERE service_id = ? AND active=1; - """, self.id) - - #Filter check - old_filters = set(self.filters.keys()) - new_filters = set([f["id"] for f in res]) - - #remove old filters - for f in old_filters: - if not f in new_filters: - del self.filters[f] - - for f in new_filters: - if not f in old_filters: - filter_info = [ele for ele in res if ele["id"] == f][0] - self.filters[f] = Filter( - is_case_sensitive=filter_info["is_case_sensitive"], - c_to_s=filter_info["mode"] in ["C","B"], - s_to_c=filter_info["mode"] in ["S","B"], - is_blacklist=filter_info["is_blacklist"], - regex=b64decode(filter_info["regex"]), - blocked_packets=filter_info["n_packets"], - code=f - ) - self.proxy.filters = list(self.filters.values()) - - def __update_status_db(self, status): - self.db.query("UPDATE services SET status = ? WHERE service_id = ?;", status, self.id) - - async def next(self,to): - async with self.lock: - return await self._next(to) - - async def _next(self, to): - if self.status != to: - # ACTIVE -> PAUSE or PAUSE -> ACTIVE - if (self.status, to) in [(STATUS.ACTIVE, STATUS.PAUSE)]: - await self.proxy.pause() - self._set_status(to) - - elif (self.status, to) in [(STATUS.PAUSE, STATUS.ACTIVE)]: - await self.proxy.reload() - self._set_status(to) - - # ACTIVE -> STOP - elif (self.status,to) in [(STATUS.ACTIVE, STATUS.STOP), (STATUS.WAIT, STATUS.STOP), (STATUS.PAUSE, STATUS.STOP)]: #Stop proxy - if self.starter: self.starter.cancel() - await self.proxy.stop() - self._set_status(to) - - # STOP -> ACTIVE or STOP -> PAUSE - elif (self.status, to) in [(STATUS.STOP, STATUS.ACTIVE), (STATUS.STOP, STATUS.PAUSE)]: - self.wanted_status = to - self._set_status(STATUS.WAIT) - self.__proxy_starter(to) - - - def _stats_updater(self,filter:Filter): - self.db.query("UPDATE regexes SET blocked_packets = ? WHERE regex_id = ?;", filter.blocked, filter.code) - - async def update_port(self): - async with self.lock: - self._update_port_from_db() - if self.status in [STATUS.PAUSE, STATUS.ACTIVE]: - next_status = self.status if self.status != STATUS.WAIT else self.wanted_status - await self._next(STATUS.STOP) - await self._next(next_status) - - def _set_status(self,status): - self.status = status - self.__update_status_db(status) - - - async def update_filters(self): - async with self.lock: - self._update_filters_from_db() - if self.status in [STATUS.PAUSE, STATUS.ACTIVE]: - await self.proxy.reload() - - def __proxy_starter(self,to): - async def func(): - try: - while True: - if check_port_is_open(self.proxy.public_port): - self._set_status(to) - await socketio_emit(["regexproxy"]) - await self.proxy.start(in_pause=(to==STATUS.PAUSE)) - self._set_status(STATUS.STOP) - await socketio_emit(["regexproxy"]) - return - else: - await asyncio.sleep(.5) - except asyncio.CancelledError: - self._set_status(STATUS.STOP) - await self.proxy.stop() - self.starter = asyncio.create_task(func()) - -class ProxyManager: - def __init__(self, db:SQLite): - self.db = db - self.proxy_table: dict[str, ServiceManager] = {} - self.lock = asyncio.Lock() - - async def close(self): - for key in list(self.proxy_table.keys()): - await self.remove(key) - - async def remove(self,id): - async with self.lock: - if id in self.proxy_table: - await self.proxy_table[id].next(STATUS.STOP) - del self.proxy_table[id] - - async def reload(self): - async with self.lock: - for srv in self.db.query('SELECT service_id, status FROM services;'): - srv_id, req_status = srv["service_id"], srv["status"] - if srv_id in self.proxy_table: - continue - - self.proxy_table[srv_id] = ServiceManager(srv_id,self.db) - await self.proxy_table[srv_id].next(req_status) - - def get(self,id) -> ServiceManager: - if id in self.proxy_table: - return self.proxy_table[id] - else: - raise ServiceNotFoundException() - -def check_port_is_open(port): - try: - sock = socket.socket() - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind(('0.0.0.0',port)) - sock.close() - return True - except Exception: - return False - -def gen_service_id(db): - while True: - res = secrets.token_hex(8) - if len(db.query('SELECT 1 FROM services WHERE service_id = ?;', res)) == 0: - break - return res - -def gen_internal_port(db): - while True: - res = random.randint(30000, 45000) - if len(db.query('SELECT 1 FROM services WHERE internal_port = ?;', res)) == 0: - break - return res \ No newline at end of file diff --git a/backend/routers/firewall.py b/backend/routers/firewall.py index 9f3d311..8801db9 100644 --- a/backend/routers/firewall.py +++ b/backend/routers/firewall.py @@ -5,7 +5,7 @@ from utils import ip_parse, ip_family, socketio_emit from utils.models import ResetRequest, StatusMessageModel from modules.firewall.nftables import FiregexTables from modules.firewall.firewall import FirewallManager -from modules.firewall.models import * +from modules.firewall.models import FirewallSettings, RuleInfo, RuleModel, RuleFormAdd, Mode, Table db = SQLite('db/firewall-rules.db', { 'rules': { diff --git a/backend/routers/nfregex.py b/backend/routers/nfregex.py index 9766a42..d0cdf79 100644 --- a/backend/routers/nfregex.py +++ b/backend/routers/nfregex.py @@ -147,7 +147,8 @@ async def get_service_by_id(service_id: str): FROM services s LEFT JOIN regexes r ON s.service_id = r.service_id WHERE s.service_id = ? GROUP BY s.service_id; """, service_id) - if len(res) == 0: raise HTTPException(status_code=400, detail="This service does not exists!") + if len(res) == 0: + raise HTTPException(status_code=400, detail="This service does not exists!") return res[0] @app.get('/service/{service_id}/stop', response_model=StatusMessageModel) @@ -177,7 +178,8 @@ async def service_delete(service_id: str): async def service_rename(service_id: str, form: RenameForm): """Request to change the name of a specific service""" form.name = refactor_name(form.name) - if not form.name: raise HTTPException(status_code=400, detail="The name cannot be empty!") + if not form.name: + raise HTTPException(status_code=400, detail="The name cannot be empty!") try: db.query('UPDATE services SET name=? WHERE service_id = ?;', form.name, service_id) except sqlite3.IntegrityError: @@ -188,7 +190,8 @@ async def service_rename(service_id: str, form: RenameForm): @app.get('/service/{service_id}/regexes', response_model=list[RegexModel]) async def get_service_regexe_list(service_id: str): """Get the list of the regexes of a service""" - if not db.query("SELECT 1 FROM services s WHERE s.service_id = ?;", service_id): raise HTTPException(status_code=400, detail="This service does not exists!") + if not db.query("SELECT 1 FROM services s WHERE s.service_id = ?;", service_id): + raise HTTPException(status_code=400, detail="This service does not exists!") return db.query(""" SELECT regex, mode, regex_id `id`, service_id, is_blacklist, @@ -205,7 +208,8 @@ async def get_regex_by_id(regex_id: int): blocked_packets n_packets, is_case_sensitive, active FROM regexes WHERE `id` = ?; """, regex_id) - if len(res) == 0: raise HTTPException(status_code=400, detail="This regex does not exists!") + if len(res) == 0: + raise HTTPException(status_code=400, detail="This regex does not exists!") return res[0] @app.get('/regex/{regex_id}/delete', response_model=StatusMessageModel) diff --git a/backend/routers/porthijack.py b/backend/routers/porthijack.py index 6bf8603..8fd3c54 100644 --- a/backend/routers/porthijack.py +++ b/backend/routers/porthijack.py @@ -96,7 +96,8 @@ async def get_service_list(): async def get_service_by_id(service_id: str): """Get info about a specific service using his id""" res = db.query("SELECT service_id, active, public_port, proxy_port, name, proto, ip_src, ip_dst FROM services WHERE service_id = ?;", service_id) - if len(res) == 0: raise HTTPException(status_code=400, detail="This service does not exists!") + if len(res) == 0: + raise HTTPException(status_code=400, detail="This service does not exists!") return res[0] @app.get('/service/{service_id}/stop', response_model=StatusMessageModel) @@ -125,7 +126,8 @@ async def service_delete(service_id: str): async def service_rename(service_id: str, form: RenameForm): """Request to change the name of a specific service""" form.name = refactor_name(form.name) - if not form.name: raise HTTPException(status_code=400, detail="The name cannot be empty!") + if not form.name: + raise HTTPException(status_code=400, detail="The name cannot be empty!") try: db.query('UPDATE services SET name=? WHERE service_id = ?;', form.name, service_id) except sqlite3.IntegrityError: diff --git a/backend/routers/regexproxy.py b/backend/routers/regexproxy.py deleted file mode 100644 index 5360592..0000000 --- a/backend/routers/regexproxy.py +++ /dev/null @@ -1,311 +0,0 @@ -from base64 import b64decode -import sqlite3, re -from fastapi import APIRouter, HTTPException -from pydantic import BaseModel -from modules.regexproxy.utils import STATUS, ProxyManager, gen_internal_port, gen_service_id -from utils.sqlite import SQLite -from utils.models import ResetRequest, StatusMessageModel -from utils import refactor_name, socketio_emit, PortType - -app = APIRouter() -db = SQLite("db/regextcpproxy.db",{ - 'services': { - 'status': 'VARCHAR(100) NOT NULL', - 'service_id': 'VARCHAR(100) PRIMARY KEY', - 'internal_port': 'INT NOT NULL CHECK(internal_port > 0 and internal_port < 65536)', - 'public_port': 'INT NOT NULL CHECK(internal_port > 0 and internal_port < 65536) UNIQUE', - 'name': 'VARCHAR(100) NOT NULL UNIQUE' - }, - 'regexes': { - 'regex': 'TEXT NOT NULL', - 'mode': 'VARCHAR(1) NOT NULL', - 'service_id': 'VARCHAR(100) NOT NULL', - 'is_blacklist': 'BOOLEAN NOT NULL CHECK (is_blacklist IN (0, 1))', - 'blocked_packets': 'INTEGER UNSIGNED NOT NULL DEFAULT 0', - 'regex_id': 'INTEGER PRIMARY KEY', - 'is_case_sensitive' : 'BOOLEAN NOT NULL CHECK (is_case_sensitive IN (0, 1))', - 'active' : 'BOOLEAN NOT NULL CHECK (is_case_sensitive IN (0, 1)) DEFAULT 1', - 'FOREIGN KEY (service_id)':'REFERENCES services (service_id)', - }, - 'QUERY':[ - "CREATE UNIQUE INDEX IF NOT EXISTS unique_regex_service ON regexes (regex,service_id,is_blacklist,mode,is_case_sensitive);" - ] -}) - -firewall = ProxyManager(db) - -async def reset(params: ResetRequest): - if not params.delete: - db.backup() - await firewall.close() - if params.delete: - db.delete() - db.init() - else: - db.restore() - await firewall.reload() - - -async def startup(): - db.init() - await firewall.reload() - -async def shutdown(): - db.backup() - await firewall.close() - db.disconnect() - db.restore() - -async def refresh_frontend(additional:list[str]=[]): - await socketio_emit(["regexproxy"]+additional) - -class GeneralStatModel(BaseModel): - closed:int - regexes: int - services: int - -@app.get('/stats', response_model=GeneralStatModel) -async def get_general_stats(): - """Get firegex general status about services""" - return db.query(""" - SELECT - (SELECT COALESCE(SUM(blocked_packets),0) FROM regexes) closed, - (SELECT COUNT(*) FROM regexes) regexes, - (SELECT COUNT(*) FROM services) services - """)[0] - -class ServiceModel(BaseModel): - id:str - status: str - public_port: PortType - internal_port: PortType - name: str - n_regex: int - n_packets: int - -@app.get('/services', response_model=list[ServiceModel]) -async def get_service_list(): - """Get the list of existent firegex services""" - return db.query(""" - SELECT - s.service_id `id`, - s.status status, - s.public_port public_port, - s.internal_port internal_port, - s.name name, - COUNT(r.regex_id) n_regex, - COALESCE(SUM(r.blocked_packets),0) n_packets - FROM services s LEFT JOIN regexes r ON r.service_id = s.service_id - GROUP BY s.service_id; - """) - -@app.get('/service/{service_id}', response_model=ServiceModel) -async def get_service_by_id(service_id: str): - """Get info about a specific service using his id""" - res = db.query(""" - SELECT - s.service_id `id`, - s.status status, - s.public_port public_port, - s.internal_port internal_port, - s.name name, - COUNT(r.regex_id) n_regex, - COALESCE(SUM(r.blocked_packets),0) n_packets - FROM services s LEFT JOIN regexes r ON r.service_id = s.service_id WHERE s.service_id = ? - GROUP BY s.service_id; - """, service_id) - if len(res) == 0: raise HTTPException(status_code=400, detail="This service does not exists!") - return res[0] - -@app.get('/service/{service_id}/stop', response_model=StatusMessageModel) -async def service_stop(service_id: str): - """Request the stop of a specific service""" - await firewall.get(service_id).next(STATUS.STOP) - await refresh_frontend() - return {'status': 'ok'} - -@app.get('/service/{service_id}/pause', response_model=StatusMessageModel) -async def service_pause(service_id: str): - """Request the pause of a specific service""" - await firewall.get(service_id).next(STATUS.PAUSE) - await refresh_frontend() - return {'status': 'ok'} - -@app.get('/service/{service_id}/start', response_model=StatusMessageModel) -async def service_start(service_id: str): - """Request the start of a specific service""" - await firewall.get(service_id).next(STATUS.ACTIVE) - await refresh_frontend() - return {'status': 'ok'} - -@app.get('/service/{service_id}/delete', response_model=StatusMessageModel) -async def service_delete(service_id: str): - """Request the deletion of a specific service""" - db.query('DELETE FROM services WHERE service_id = ?;', service_id) - db.query('DELETE FROM regexes WHERE service_id = ?;', service_id) - await firewall.remove(service_id) - await refresh_frontend() - return {'status': 'ok'} - - -@app.get('/service/{service_id}/regen-port', response_model=StatusMessageModel) -async def regen_service_port(service_id: str): - """Request the regeneration of a the internal proxy port of a specific service""" - db.query('UPDATE services SET internal_port = ? WHERE service_id = ?;', gen_internal_port(db), service_id) - await firewall.get(service_id).update_port() - await refresh_frontend() - return {'status': 'ok'} - -class ChangePortForm(BaseModel): - port: int|None = None - internalPort: int|None = None - -@app.post('/service/{service_id}/change-ports', response_model=StatusMessageModel) -async def change_service_ports(service_id: str, change_port:ChangePortForm): - """Choose and change the ports of the service""" - if change_port.port is None and change_port.internalPort is None: - raise HTTPException(status_code=400, detail="Invalid Request!") - try: - sql_inj = "" - query:list[str|int] = [] - if not change_port.port is None: - sql_inj+=" public_port = ? " - query.append(change_port.port) - if not change_port.port is None and not change_port.internalPort is None: - sql_inj += "," - if not change_port.internalPort is None: - sql_inj+=" internal_port = ? " - query.append(change_port.internalPort) - query.append(service_id) - db.query(f'UPDATE services SET {sql_inj} WHERE service_id = ?;', *query) - except sqlite3.IntegrityError: - raise HTTPException(status_code=400, detail="Port of the service has been already assigned to another service") - await firewall.get(service_id).update_port() - await refresh_frontend() - return {'status': 'ok'} - -class RegexModel(BaseModel): - regex:str - mode:str - id:int - service_id:str - is_blacklist: bool - n_packets:int - is_case_sensitive:bool - active:bool - -@app.get('/service/{service_id}/regexes', response_model=list[RegexModel]) -async def get_service_regexe_list(service_id: str): - """Get the list of the regexes of a service""" - if not db.query("SELECT 1 FROM services s WHERE s.service_id = ?;", service_id): raise HTTPException(status_code=400, detail="This service does not exists!") - return db.query(""" - SELECT - regex, mode, regex_id `id`, service_id, is_blacklist, - blocked_packets n_packets, is_case_sensitive, active - FROM regexes WHERE service_id = ?; - """, service_id) - -@app.get('/regex/{regex_id}', response_model=RegexModel) -async def get_regex_by_id(regex_id: int): - """Get regex info using his id""" - res = db.query(""" - SELECT - regex, mode, regex_id `id`, service_id, is_blacklist, - blocked_packets n_packets, is_case_sensitive, active - FROM regexes WHERE `id` = ?; - """, regex_id) - if len(res) == 0: raise HTTPException(status_code=400, detail="This regex does not exists!") - return res[0] - -@app.get('/regex/{regex_id}/delete', response_model=StatusMessageModel) -async def regex_delete(regex_id: int): - """Delete a regex using his id""" - res = db.query('SELECT * FROM regexes WHERE regex_id = ?;', regex_id) - if len(res) != 0: - db.query('DELETE FROM regexes WHERE regex_id = ?;', regex_id) - await firewall.get(res[0]["service_id"]).update_filters() - await refresh_frontend() - return {'status': 'ok'} - -@app.get('/regex/{regex_id}/enable', response_model=StatusMessageModel) -async def regex_enable(regex_id: int): - """Request the enabling of a regex""" - res = db.query('SELECT * FROM regexes WHERE regex_id = ?;', regex_id) - if len(res) != 0: - db.query('UPDATE regexes SET active=1 WHERE regex_id = ?;', regex_id) - await firewall.get(res[0]["service_id"]).update_filters() - await refresh_frontend() - return {'status': 'ok'} - -@app.get('/regex/{regex_id}/disable', response_model=StatusMessageModel) -async def regex_disable(regex_id: int): - """Request the deactivation of a regex""" - res = db.query('SELECT * FROM regexes WHERE regex_id = ?;', regex_id) - if len(res) != 0: - db.query('UPDATE regexes SET active=0 WHERE regex_id = ?;', regex_id) - await firewall.get(res[0]["service_id"]).update_filters() - await refresh_frontend() - return {'status': 'ok'} - -class RegexAddForm(BaseModel): - service_id: str - regex: str - mode: str - active: bool|None = None - is_blacklist: bool - is_case_sensitive: bool - -@app.post('/regexes/add', response_model=StatusMessageModel) -async def add_new_regex(form: RegexAddForm): - """Add a new regex""" - try: - re.compile(b64decode(form.regex)) - except Exception: - raise HTTPException(status_code=400, detail="Invalid regex") - try: - db.query("INSERT INTO regexes (service_id, regex, is_blacklist, mode, is_case_sensitive, active ) VALUES (?, ?, ?, ?, ?, ?);", - form.service_id, form.regex, form.is_blacklist, form.mode, form.is_case_sensitive, True if form.active is None else form.active ) - except sqlite3.IntegrityError: - raise HTTPException(status_code=400, detail="An identical regex already exists") - await firewall.get(form.service_id).update_filters() - await refresh_frontend() - return {'status': 'ok'} - -class ServiceAddForm(BaseModel): - name: str - port: PortType - internalPort: int|None = None - -class ServiceAddStatus(BaseModel): - status:str - id: str|None = None - -class RenameForm(BaseModel): - name:str - -@app.post('/service/{service_id}/rename', response_model=StatusMessageModel) -async def service_rename(service_id: str, form: RenameForm): - """Request to change the name of a specific service""" - form.name = refactor_name(form.name) - if not form.name: raise HTTPException(status_code=400, detail="The name cannot be empty!") - try: - db.query('UPDATE services SET name=? WHERE service_id = ?;', form.name, service_id) - except sqlite3.IntegrityError: - raise HTTPException(status_code=400, detail="The name is already used!") - await refresh_frontend() - return {'status': 'ok'} - -@app.post('/services/add', response_model=ServiceAddStatus) -async def add_new_service(form: ServiceAddForm): - """Add a new service""" - serv_id = gen_service_id(db) - form.name = refactor_name(form.name) - try: - internal_port = form.internalPort if form.internalPort else gen_internal_port(db) - db.query("INSERT INTO services (name, service_id, internal_port, public_port, status) VALUES (?, ?, ?, ?, ?)", - form.name, serv_id, internal_port, form.port, 'stop') - except sqlite3.IntegrityError: - raise HTTPException(status_code=400, detail="Name or/and ports of the service has been already assigned to another service") - await firewall.reload() - await refresh_frontend() - return {'status': 'ok', "id": serv_id } \ No newline at end of file diff --git a/backend/utils/__init__.py b/backend/utils/__init__.py index fa3fe4f..bb8d985 100644 --- a/backend/utils/__init__.py +++ b/backend/utils/__init__.py @@ -1,10 +1,13 @@ import asyncio from ipaddress import ip_address, ip_interface -import os, socket, psutil, sys, nftables +import os +import socket +import psutil +import sys +import nftables from fastapi_socketio import SocketManager from fastapi import Path from typing import Annotated -import json LOCALHOST_IP = socket.gethostbyname(os.getenv("LOCALHOST_IP","127.0.0.1")) @@ -31,7 +34,8 @@ async def socketio_emit(elements:list[str]): def refactor_name(name:str): name = name.strip() - while " " in name: name = name.replace(" "," ") + while " " in name: + name = name.replace(" "," ") return name class SysctlManager: @@ -125,8 +129,10 @@ class NFTableManager(Singleton): def cmd(self, *cmds): code, out, err = self.raw_cmd(*cmds) - if code == 0: return out - else: raise Exception(err) + if code == 0: + return out + else: + raise Exception(err) def init(self): self.reset() @@ -138,8 +144,10 @@ class NFTableManager(Singleton): def list_rules(self, tables = None, chains = None): for filter in [ele["rule"] for ele in self.raw_list() if "rule" in ele ]: - if tables and filter["table"] not in tables: continue - if chains and filter["chain"] not in chains: continue + if tables and filter["table"] not in tables: + continue + if chains and filter["chain"] not in chains: + continue yield filter def raw_list(self): diff --git a/backend/utils/loader.py b/backend/utils/loader.py index 3b72b53..179c5d8 100644 --- a/backend/utils/loader.py +++ b/backend/utils/loader.py @@ -1,5 +1,6 @@ -import os, httpx +import os +import httpx from typing import Callable from fastapi import APIRouter from starlette.responses import StreamingResponse @@ -31,7 +32,8 @@ def frontend_deploy(app): return await frontend_debug_proxy(full_path) except Exception: return {"details":"Frontend not started at "+f"http://127.0.0.1:{os.getenv('F_PORT','5173')}"} - else: return await react_deploy(full_path) + else: + return await react_deploy(full_path) def list_routers(): return [ele[:-3] for ele in list_files(ROUTERS_DIR) if ele != "__init__.py" and " " not in ele and ele.endswith(".py")] @@ -79,9 +81,12 @@ def load_routers(app): if router.shutdown: shutdowns.append(router.shutdown) async def reset(reset_option:ResetRequest): - for func in resets: await run_func(func, reset_option) + for func in resets: + await run_func(func, reset_option) async def startup(): - for func in startups: await run_func(func) + for func in startups: + await run_func(func) async def shutdown(): - for func in shutdowns: await run_func(func) + for func in shutdowns: + await run_func(func) return reset, startup, shutdown diff --git a/backend/utils/sqlite.py b/backend/utils/sqlite.py index 426265c..430dbd6 100644 --- a/backend/utils/sqlite.py +++ b/backend/utils/sqlite.py @@ -1,4 +1,6 @@ -import json, sqlite3, os +import json +import sqlite3 +import os from hashlib import md5 class SQLite(): @@ -15,8 +17,10 @@ class SQLite(): self.conn = sqlite3.connect(self.db_name, check_same_thread = False) except Exception: path_name = os.path.dirname(self.db_name) - if not os.path.exists(path_name): os.makedirs(path_name) - with open(self.db_name, 'x'): pass + if not os.path.exists(path_name): + os.makedirs(path_name) + with open(self.db_name, 'x'): + pass self.conn = sqlite3.connect(self.db_name, check_same_thread = False) def dict_factory(cursor, row): d = {} @@ -36,13 +40,15 @@ class SQLite(): with open(self.db_name, "wb") as f: f.write(self.__backup) self.__backup = None - if were_active: self.connect() + if were_active: + self.connect() def delete_backup(self): self.__backup = None def disconnect(self) -> None: - if self.conn: self.conn.close() + if self.conn: + self.conn.close() self.conn = None def create_schema(self, tables = {}) -> None: @@ -50,9 +56,11 @@ class SQLite(): cur = self.conn.cursor() cur.execute("CREATE TABLE IF NOT EXISTS main.keys_values(key VARCHAR(100) PRIMARY KEY, value VARCHAR(100) NOT NULL);") for t in tables: - if t == "QUERY": continue + if t == "QUERY": + continue cur.execute('CREATE TABLE IF NOT EXISTS main.{}({});'.format(t, ''.join([(c + ' ' + tables[t][c] + ', ') for c in tables[t]])[:-2])) - if "QUERY" in tables: [cur.execute(qry) for qry in tables["QUERY"]] + if "QUERY" in tables: + [cur.execute(qry) for qry in tables["QUERY"]] cur.close() def query(self, query, *values): @@ -82,8 +90,10 @@ class SQLite(): raise e finally: cur.close() - try: self.conn.commit() - except Exception: pass + try: + self.conn.commit() + except Exception: + pass def delete(self): self.disconnect() @@ -92,7 +102,8 @@ class SQLite(): def init(self): self.connect() try: - if self.get('DB_VERSION') != self.DB_VER: raise Exception("DB_VERSION is not correct") + if self.get('DB_VERSION') != self.DB_VER: + raise Exception("DB_VERSION is not correct") except Exception: self.delete() self.connect() diff --git a/frontend/bun.lockb b/frontend/bun.lockb index 0bc4166a510cb52a36a68de7020cdb0140832401..6fdb21bd0e8dabca0ac607aa680ed566544d0902 100755 GIT binary patch delta 24254 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 ZhnW30Wi}nc8*;6H#+?hT53>Qv{{un}o4x=5 diff --git a/frontend/package.json b/frontend/package.json index e8b633e..a5fdcdc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,25 +5,25 @@ "private": true, "dependencies": { "@hello-pangea/dnd": "^16.6.0", - "@mantine/core": "^7.13.2", - "@mantine/form": "^7.13.2", - "@mantine/hooks": "^7.13.2", - "@mantine/modals": "^7.13.2", - "@mantine/notifications": "^7.13.2", + "@mantine/core": "^7.16.2", + "@mantine/form": "^7.16.2", + "@mantine/hooks": "^7.16.2", + "@mantine/modals": "^7.16.2", + "@mantine/notifications": "^7.16.2", "@tanstack/react-query": "^4.36.1", "@types/jest": "^27.5.2", - "@types/node": "^20.16.11", - "@types/react": "^18.3.11", - "@types/react-dom": "^18.3.1", + "@types/node": "^20.17.16", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "buffer": "^6.0.3", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-icons": "^5.3.0", - "react-router-dom": "^6.27.0", - "socket.io-client": "^4.8.0", + "react-icons": "^5.4.0", + "react-router-dom": "^6.29.0", + "socket.io-client": "^4.8.1", "typescript": "^4.9.5", "web-vitals": "^2.1.4", - "zustand": "^5.0.0-rc.2" + "zustand": "^5.0.3" }, "scripts": { "dev": "vite", @@ -50,8 +50,8 @@ }, "devDependencies": { "@tanstack/react-query-devtools": "^4.36.1", - "@vitejs/plugin-react": "^4.3.2", - "vite": "^4.5.5", + "@vitejs/plugin-react": "^4.3.4", + "vite": "^4.5.9", "vite-plugin-svgr": "^3.3.0", "vite-tsconfig-paths": "^4.3.2" } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 8a027a7..fcfd39b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,9 +8,7 @@ import { PasswordSend, ServerStatusResponse } from './js/models'; import { DEV_IP_BACKEND, errorNotify, getstatus, HomeRedirector, IS_DEV, login, setpassword } from './js/utils'; import NFRegex from './pages/NFRegex'; import io from 'socket.io-client'; -import RegexProxy from './pages/RegexProxy'; import ServiceDetailsNFRegex from './pages/NFRegex/ServiceDetails'; -import ServiceDetailsProxyRegex from './pages/RegexProxy/ServiceDetails'; import PortHijack from './pages/PortHijack'; import { Firewall } from './pages/Firewall'; import { useQueryClient } from '@tanstack/react-query'; @@ -150,9 +148,6 @@ function App() { } > } /> - } > - } /> - } /> } /> } /> diff --git a/frontend/src/components/AddNewRegex.tsx b/frontend/src/components/AddNewRegex.tsx index 623120a..0f846e1 100644 --- a/frontend/src/components/AddNewRegex.tsx +++ b/frontend/src/components/AddNewRegex.tsx @@ -1,15 +1,12 @@ -import { Button, Group, Space, TextInput, Notification, Switch, NativeSelect, Modal, Alert } from '@mantine/core'; +import { Button, Group, Space, TextInput, Notification, Switch, NativeSelect, Modal } from '@mantine/core'; import { useForm } from '@mantine/form'; import { useState } from 'react'; import { RegexAddForm } from '../js/models'; import { b64decode, b64encode, getapiobject, okNotify } from '../js/utils'; import { ImCross } from "react-icons/im" -import FilterTypeSelector from './FilterTypeSelector'; -import { AiFillWarning } from 'react-icons/ai'; type RegexAddInfo = { regex:string, - type:string, mode:string, is_case_insensitive:boolean, deactive:boolean @@ -20,14 +17,12 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=> const form = useForm({ initialValues: { regex:"", - type:"blacklist", mode:"C -> S", is_case_insensitive:false, deactive:false }, validate:{ regex: (value) => value !== "" ? null : "Regex is required", - type: (value) => ["blacklist","whitelist"].includes(value) ? null : "Invalid type", mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value) ? null : "Invalid mode", } }) @@ -46,7 +41,6 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=> const filter_mode = ({'C -> S':'C', 'S -> C':'S', 'C <-> S':'B'}[values.mode]) const request:RegexAddForm = { - is_blacklist:values.type !== "whitelist", is_case_sensitive: !values.is_case_insensitive, service_id: service, mode: filter_mode?filter_mode:"B", @@ -58,7 +52,7 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=> if (!res){ setSubmitLoading(false) close(); - okNotify(`Regex ${b64decode(request.regex)} has been added`, `Successfully added ${request.is_case_sensitive?"case sensitive":"case insensitive"} ${request.is_blacklist?"blacklist":"whitelist"} regex to ${request.service_id} service`) + okNotify(`Regex ${b64decode(request.regex)} has been added`, `Successfully added ${request.is_case_sensitive?"case sensitive":"case insensitive"} regex to ${request.service_id} service`) }else if (res.toLowerCase() === "invalid regex"){ setSubmitLoading(false) form.setFieldError("regex", "Invalid Regex") @@ -98,16 +92,6 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=> variant="filled" {...form.getInputProps('mode')} /> - - - {form.values.type == "whitelist"?<> - }> - Using whitelist means that EVERY packet that doesn't match the regex will be DROPPED... In most cases this cause the service interruption. - :null} diff --git a/frontend/src/components/FilterTypeSelector.tsx b/frontend/src/components/FilterTypeSelector.tsx deleted file mode 100644 index aa331be..0000000 --- a/frontend/src/components/FilterTypeSelector.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { Box, Center, SegmentedControl } from "@mantine/core"; -import { FaListAlt } from "react-icons/fa"; -import { TiCancel } from "react-icons/ti"; - -export default function FilterTypeSelector(props:any){ - return - - Blacklist - - ), - }, - { - value: 'whitelist', - label: ( -

- - Whitelist -
- ), - }, - ]} - {...props} - /> -} \ No newline at end of file diff --git a/frontend/src/components/NavBar/index.tsx b/frontend/src/components/NavBar/index.tsx index 6b06f16..456e12b 100644 --- a/frontend/src/components/NavBar/index.tsx +++ b/frontend/src/components/NavBar/index.tsx @@ -39,11 +39,6 @@ export default function NavBar() { } /> } /> } /> - - : } onClick={()=>setToggleState(!toggle)}/> - - } /> - diff --git a/frontend/src/components/PortHijack/utils.ts b/frontend/src/components/PortHijack/utils.ts index 1421dec..80875cc 100644 --- a/frontend/src/components/PortHijack/utils.ts +++ b/frontend/src/components/PortHijack/utils.ts @@ -1,6 +1,6 @@ import { ServerResponse } from "../../js/models" import { getapi, postapi } from "../../js/utils" -import { UseQueryOptions, useQuery } from "@tanstack/react-query" +import { useQuery } from "@tanstack/react-query" export type GeneralStats = { services:number diff --git a/frontend/src/components/RegexProxy/AddNewService.tsx b/frontend/src/components/RegexProxy/AddNewService.tsx deleted file mode 100644 index a9920be..0000000 --- a/frontend/src/components/RegexProxy/AddNewService.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { Button, Group, Space, TextInput, Notification, Modal, Switch } from '@mantine/core'; -import { useForm } from '@mantine/form'; -import { useState } from 'react'; -import { okNotify } from '../../js/utils'; -import { ImCross } from "react-icons/im" -import { regexproxy } from './utils'; -import PortInput from '../PortInput'; - -type ServiceAddForm = { - name:string, - port:number, - autostart: boolean, - chosenInternalPort: boolean, - internalPort: number -} - -function AddNewService({ opened, onClose }:{ opened:boolean, onClose:()=>void }) { - - const form = useForm({ - initialValues: { - name:"", - port:8080, - internalPort:30001, - chosenInternalPort:false, - autostart: true - }, - validate:{ - name: (value) => value !== ""? null : "Service name is required", - port: (value) => (value>0 && value<65536) ? null : "Invalid port", - internalPort: (value) => (value>0 && value<65536) ? null : "Invalid internal port", - } - }) - - const close = () =>{ - onClose() - form.reset() - setError(null) - } - - const [submitLoading, setSubmitLoading] = useState(false) - const [error, setError] = useState(null) - - const submitRequest = ({ name, port, autostart, chosenInternalPort, internalPort }:ServiceAddForm) =>{ - setSubmitLoading(true) - regexproxy.servicesadd(chosenInternalPort?{ internalPort, name, port }:{ name, port }).then( res => { - if (res.status === "ok"){ - setSubmitLoading(false) - close(); - if (autostart) regexproxy.servicestart(res.id) - okNotify(`Service ${name} has been added`, `Successfully added ${res.id} with port ${port}`) - }else{ - setSubmitLoading(false) - setError("Invalid request! [ "+res.status+" ]") - } - }).catch( err => { - setSubmitLoading(false) - setError("Request Failed! [ "+err+" ]") - }) - } - - - return -
- - - - - - {form.values.chosenInternalPort?<> - - - - :null} - - - - - - - - - - - - - - - - {error?<> - } color="red" onClose={()=>{setError(null)}}> - Error: {error} - :null} - - -
- -} - -export default AddNewService; diff --git a/frontend/src/components/RegexProxy/ServiceRow/ChangePortModal.tsx b/frontend/src/components/RegexProxy/ServiceRow/ChangePortModal.tsx deleted file mode 100644 index a211831..0000000 --- a/frontend/src/components/RegexProxy/ServiceRow/ChangePortModal.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { Button, Group, Space, Notification, Modal, Center, Title } from '@mantine/core'; -import { useForm } from '@mantine/form'; -import React, { useEffect, useState } from 'react'; -import { ImCross } from "react-icons/im" -import { FaLongArrowAltDown } from 'react-icons/fa'; -import { regexproxy, Service } from '../utils'; -import { okNotify } from '../../../js/utils'; -import PortInput from '../../PortInput'; - -type InputForm = { - internalPort:number, - port:number -} - -function ChangePortModal({ service, opened, onClose }:{ service:Service, opened:boolean, onClose:()=>void }) { - - const form = useForm({ - initialValues: { - internalPort: service.internal_port, - port: service.public_port - }, - validate:{ - internalPort: (value) => (value>0 && value<65536) ? null : "Invalid internal port", - port: (value) => (value>0 && value<65536) ? null : "Invalid public port", - } - }) - - useEffect(()=>{ - form.setValues({internalPort: service.internal_port, port:service.public_port}) - },[opened]) - - const close = () =>{ - onClose() - form.reset() - setError(null) - } - - const [submitLoading, setSubmitLoading] = useState(false) - const [error, setError] = useState(null) - - const submitRequest = (data:InputForm) =>{ - setSubmitLoading(true) - regexproxy.servicechangeport(service.id, data).then( res => { - if (!res){ - setSubmitLoading(false) - close(); - okNotify(`Internal port on ${service.name} service has changed in ${data.internalPort}`, `Successfully changed internal port of service with id ${service.id}`) - }else{ - setSubmitLoading(false) - setError("Invalid request! [ "+res+" ]") - } - }).catch( err => { - setSubmitLoading(false) - setError("Request Failed! [ "+err+" ]") - }) - } - - - return -
- - - - -
- - - - - -
The change of the ports will cause a temporarily shutdown of the service! ⚠️
- - - - - - - - - - {error?<> - } color="red" onClose={()=>{setError(null)}}> - Error: {error} - :null} - - -
- -} - -export default ChangePortModal; diff --git a/frontend/src/components/RegexProxy/ServiceRow/RenameForm.tsx b/frontend/src/components/RegexProxy/ServiceRow/RenameForm.tsx deleted file mode 100644 index 6cb96e6..0000000 --- a/frontend/src/components/RegexProxy/ServiceRow/RenameForm.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Button, Group, Space, TextInput, Notification, Modal } from '@mantine/core'; -import { useForm } from '@mantine/form'; -import { useEffect, useState } from 'react'; -import { okNotify } from '../../../js/utils'; -import { ImCross } from "react-icons/im" -import { regexproxy, Service } from '../utils'; - -function RenameForm({ opened, onClose, service }:{ opened:boolean, onClose:()=>void, service:Service }) { - - const form = useForm({ - initialValues: { name:service.name }, - validate:{ name: (value) => value !== ""? null : "Service name is required" } - }) - - const close = () =>{ - onClose() - form.reset() - setError(null) - } - - useEffect(()=> form.setFieldValue("name", service.name),[opened]) - - const [submitLoading, setSubmitLoading] = useState(false) - const [error, setError] = useState(null) - - const submitRequest = ({ name }:{ name:string }) => { - setSubmitLoading(true) - regexproxy.servicerename(service.id, name).then( res => { - if (!res){ - setSubmitLoading(false) - close(); - okNotify(`Service ${service.name} has been renamed in ${ name }`, `Successfully renamed service on port ${service.public_port}`) - }else{ - setSubmitLoading(false) - setError("Error: [ "+res+" ]") - } - }).catch( err => { - setSubmitLoading(false) - setError("Request Failed! [ "+err+" ]") - }) - - } - - - return -
- - - - - - - - {error?<> - } color="red" onClose={()=>{setError(null)}}> - Error: {error} - :null} - - -
- -} - -export default RenameForm; diff --git a/frontend/src/components/RegexProxy/ServiceRow/index.tsx b/frontend/src/components/RegexProxy/ServiceRow/index.tsx deleted file mode 100644 index 3aa5cb0..0000000 --- a/frontend/src/components/RegexProxy/ServiceRow/index.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import { ActionIcon, Badge, Box, Divider, Grid, Menu, Space, Title, Tooltip } from '@mantine/core'; -import { useState } from 'react'; -import { FaPause, FaPlay, FaStop } from 'react-icons/fa'; -import { MdOutlineArrowForwardIos } from "react-icons/md" -import YesNoModal from '../../YesNoModal'; -import { errorNotify, isMediumScreen, okNotify } from '../../../js/utils'; -import { BsArrowRepeat, BsTrashFill } from 'react-icons/bs'; -import { TbNumbers } from 'react-icons/tb'; -import { BiRename } from 'react-icons/bi' -import ChangePortModal from './ChangePortModal'; -import RenameForm from './RenameForm'; -import { regexproxy, Service } from '../utils'; -import { MenuDropDownWithButton } from '../../MainLayout'; - -//"status":"stop"/"wait"/"active"/"pause", -function ServiceRow({ service, onClick }:{ service:Service, onClick?:()=>void }) { - - let status_color = "gray"; - switch(service.status){ - case "stop": status_color = "red"; break; - case "wait": status_color = "yellow"; break; - case "active": status_color = "teal"; break; - case "pause": status_color = "cyan"; break; - } - - const [stopModal, setStopModal] = useState(false); - const [buttonLoading, setButtonLoading] = useState(false) - const [tooltipStopOpened, setTooltipStopOpened] = useState(false); - const [deleteModal, setDeleteModal] = useState(false) - const [changePortModal, setChangePortModal] = useState(false) - const [choosePortModal, setChoosePortModal] = useState(false) - const [renameModal, setRenameModal] = useState(false) - - const isMedium = isMediumScreen() - - const stopService = async () => { - setButtonLoading(true) - await regexproxy.servicestop(service.id).then(res => { - if(!res){ - okNotify(`Service ${service.id} stopped successfully!`,`The service ${service.name} has been stopped!`) - }else{ - errorNotify(`An error as occurred during the stopping of the service ${service.id}`,`Error: ${res}`) - } - }).catch(err => { - errorNotify(`An error as occurred during the stopping of the service ${service.id}`,`Error: ${err}`) - }) - setButtonLoading(false); - } - - const startService = async () => { - setButtonLoading(true) - await regexproxy.servicestart(service.id).then(res => { - if(!res){ - okNotify(`Service ${service.id} started successfully!`,`The service ${service.name} has been started!`) - }else{ - errorNotify(`An error as occurred during the starting of the service ${service.id}`,`Error: ${res}`) - } - }).catch(err => { - errorNotify(`An error as occurred during the starting of the service ${service.id}`,`Error: ${err}`) - }) - setButtonLoading(false) - } - - const pauseService = async () => { - setButtonLoading(true) - await regexproxy.servicepause(service.id).then(res => { - if(!res){ - okNotify(`Service ${service.id} paused successfully!`,`The service ${service.name} has been paused (Transparent mode)!`) - }else{ - errorNotify(`An error as occurred during the pausing of the service ${service.id}`,`Error: ${res}`) - } - }).catch(err => { - errorNotify(`An error as occurred during the pausing of the service ${service.id}`,`Error: ${err}`) - }) - setButtonLoading(false) - - } - - const deleteService = () => { - regexproxy.servicedelete(service.id).then(res => { - if (!res){ - okNotify("Service delete complete!",`The service ${service.id} has been deleted!`) - }else - errorNotify("An error occurred while deleting a service",`Error: ${res}`) - }).catch(err => { - errorNotify("An error occurred while deleting a service",`Error: ${err}`) - }) - - } - - const changePort = () => { - regexproxy.serviceregenport(service.id).then(res => { - if (!res){ - okNotify("Service port regeneration completed!",`The service ${service.id} has changed the internal port!`) - }else - errorNotify("An error occurred while changing the internal service port",`Error: ${res}`) - }).catch(err => { - errorNotify("An error occurred while changing the internal service port",`Error: ${err}`) - }) - } - - return <> - - - - {service.name} :{service.public_port} - {service.internal_port} {"->"} {service.public_port} - - {!isMedium?:null} - - - - {!isMedium?:<>} - - Status: {service.status} - Regex: {service.n_regex} - Connections Blocked: {service.n_packets} - - {isMedium?:<>} - - - Rename service - } onClick={()=>setRenameModal(true)}>Change service name - - Change ports - } onClick={()=>setChoosePortModal(true)}>Change port - } onClick={()=>setChangePortModal(true)}>Regen proxy port - - Danger zone - } onClick={()=>setDeleteModal(true)}>Delete Service - - - {["pause","wait"].includes(service.status)? - - - setStopModal(true)} size="xl" radius="md" variant="filled" - disabled={service.status === "stop"} - aria-describedby="tooltip-stop-id" - onFocus={() => setTooltipStopOpened(false)} onBlur={() => setTooltipStopOpened(false)} - onMouseEnter={() => setTooltipStopOpened(true)} onMouseLeave={() => setTooltipStopOpened(false)}> - - - : - - - - - - } - - - - - - - - - - {onClick? - - - :null} - {!isMedium?<>:null} - - - - {setStopModal(false);}} - action={stopService} - opened={stopModal} - /> - - setDeleteModal(false) } - action={deleteService} - opened={deleteModal} - /> - setChangePortModal(false)} - action={changePort} - opened={changePortModal} - /> - setChoosePortModal(false)} - opened={choosePortModal} - /> - setRenameModal(false)} - opened={renameModal} - service={service} - /> - -} - -export default ServiceRow; diff --git a/frontend/src/components/RegexProxy/utils.ts b/frontend/src/components/RegexProxy/utils.ts deleted file mode 100644 index f7e882a..0000000 --- a/frontend/src/components/RegexProxy/utils.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { useQuery } from "@tanstack/react-query" -import { RegexAddForm, RegexFilter, ServerResponse } from "../../js/models" -import { getapi, postapi } from "../../js/utils" - -export type Service = { - id:string, - name:string, - status:string, - public_port:number, - internal_port:number, - n_packets:number, - n_regex:number, -} - -export type ServiceAddForm = { - name:string, - port:number, - internalPort?:number -} - -export type ServerResponseWithID = { - status:string, - id:string -} - -export type ChangePort = { - port?: number, - internalPort?: number -} - -export const serviceQueryKey = ["regexproxy","services"] - -export const regexproxyServiceQuery = () => useQuery({queryKey:serviceQueryKey, queryFn:regexproxy.services}) -export const regexproxyServiceRegexesQuery = (service_id:string) => useQuery({ - queryKey:[...serviceQueryKey,service_id,"regexes"], - queryFn:() => regexproxy.serviceregexes(service_id) -}) - - -export const regexproxy = { - services: async() => { - return await getapi("regexproxy/services") as Service[]; - }, - serviceinfo: async (service_id:string) => { - return await getapi(`regexproxy/service/${service_id}`) as Service; - }, - regexdelete: async (regex_id:number) => { - const { status } = await getapi(`regexproxy/regex/${regex_id}/delete`) as ServerResponse; - return status === "ok"?undefined:status - }, - regexenable: async (regex_id:number) => { - const { status } = await getapi(`regexproxy/regex/${regex_id}/enable`) as ServerResponse; - return status === "ok"?undefined:status - }, - regexdisable: async (regex_id:number) => { - const { status } = await getapi(`regexproxy/regex/${regex_id}/disable`) as ServerResponse; - return status === "ok"?undefined:status - }, - servicestart: async (service_id:string) => { - const { status } = await getapi(`regexproxy/service/${service_id}/start`) as ServerResponse; - return status === "ok"?undefined:status - }, - servicestop: async (service_id:string) => { - const { status } = await getapi(`regexproxy/service/${service_id}/stop`) as ServerResponse; - return status === "ok"?undefined:status - }, - servicepause: async (service_id:string) => { - const { status } = await getapi(`regexproxy/service/${service_id}/pause`) as ServerResponse; - return status === "ok"?undefined:status - }, - serviceregenport: async (service_id:string) => { - const { status } = await getapi(`regexproxy/service/${service_id}/regen-port`) as ServerResponse; - return status === "ok"?undefined:status - }, - servicechangeport: async (service_id:string, data:ChangePort) => { - const { status } = await postapi(`regexproxy/service/${service_id}/change-ports`,data) as ServerResponse; - return status === "ok"?undefined:status - }, - servicesadd: async (data:ServiceAddForm) => { - return await postapi("regexproxy/services/add",data) as ServerResponseWithID; - }, - servicerename: async (service_id:string, name: string) => { - const { status } = await postapi(`regexproxy/service/${service_id}/rename`,{ name }) as ServerResponse; - return status === "ok"?undefined:status - }, - servicedelete: async (service_id:string) => { - const { status } = await getapi(`regexproxy/service/${service_id}/delete`) as ServerResponse; - return status === "ok"?undefined:status - }, - regexesadd: async (data:RegexAddForm) => { - const { status } = await postapi("regexproxy/regexes/add",data) as ServerResponse; - return status === "ok"?undefined:status - }, - serviceregexes: async (service_id:string) => { - return await getapi(`regexproxy/service/${service_id}/regexes`) as RegexFilter[]; - } -} \ No newline at end of file diff --git a/frontend/src/components/RegexView/index.tsx b/frontend/src/components/RegexView/index.tsx index 2c3d8dc..20b9d18 100644 --- a/frontend/src/components/RegexView/index.tsx +++ b/frontend/src/components/RegexView/index.tsx @@ -4,7 +4,6 @@ import { RegexFilter } from '../../js/models'; import { b64decode, errorNotify, getapiobject, okNotify } from '../../js/utils'; import { BsTrashFill } from "react-icons/bs" import YesNoModal from '../YesNoModal'; -import FilterTypeSelector from '../FilterTypeSelector'; import { FaPause, FaPlay } from 'react-icons/fa'; import { useClipboard } from '@mantine/hooks'; @@ -74,12 +73,6 @@ function RegexView({ regexInfo }:{ regexInfo:RegexFilter }) { - Service: {regexInfo.service_id} diff --git a/frontend/src/js/models.ts b/frontend/src/js/models.ts index 43fff7a..3a78c15 100644 --- a/frontend/src/js/models.ts +++ b/frontend/src/js/models.ts @@ -47,7 +47,6 @@ export type RegexAddForm = { service_id:string, regex:string, is_case_sensitive:boolean, - is_blacklist:boolean, mode:string, // C->S S->C BOTH, active: boolean } \ No newline at end of file diff --git a/frontend/src/js/utils.tsx b/frontend/src/js/utils.tsx index 60af1ed..159d6d6 100644 --- a/frontend/src/js/utils.tsx +++ b/frontend/src/js/utils.tsx @@ -3,7 +3,6 @@ import { ImCross } from "react-icons/im"; import { TiTick } from "react-icons/ti" import { Navigate } from "react-router-dom"; import { nfregex } from "../components/NFRegex/utils"; -import { regexproxy } from "../components/RegexProxy/utils"; import { ChangePassword, IpInterface, LoginResponse, PasswordSend, ServerResponse, ServerResponseToken, ServerStatusResponse } from "./models"; import { Buffer } from "buffer" import { QueryClient, useQuery } from "@tanstack/react-query"; @@ -111,8 +110,6 @@ export function getapiobject(){ switch(getMainPath()){ case "nfregex": return nfregex - case "regexproxy": - return regexproxy } throw new Error('No api for this tool!'); } diff --git a/frontend/src/pages/RegexProxy/ServiceDetails.tsx b/frontend/src/pages/RegexProxy/ServiceDetails.tsx deleted file mode 100644 index d539e89..0000000 --- a/frontend/src/pages/RegexProxy/ServiceDetails.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { ActionIcon, Box, Grid, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core'; -import { useState } from 'react'; -import { Navigate, useParams } from 'react-router-dom'; -import { BsPlusLg } from "react-icons/bs"; -import { regexproxyServiceQuery, regexproxyServiceRegexesQuery } from '../../components/RegexProxy/utils'; -import ServiceRow from '../../components/RegexProxy/ServiceRow'; -import AddNewRegex from '../../components/AddNewRegex'; -import RegexView from '../../components/RegexView'; - -function ServiceDetailsProxyRegex() { - - const {srv} = useParams() - const [open, setOpen] = useState(false) - const services = regexproxyServiceQuery() - const serviceInfo = services.data?.find(s => s.id == srv) - const [tooltipAddRegexOpened, setTooltipAddRegexOpened] = useState(false) - const regexesList = regexproxyServiceRegexesQuery(srv??"") - - if (!srv || !serviceInfo || regexesList.isError) return - - return - - - {(!regexesList.data || regexesList.data.length == 0)?<> - - No regex found for this service! Add one by clicking the "+" buttons - - - - setOpen(true)} size="xl" radius="md" variant="filled" - aria-describedby="tooltip-AddRegex-id" - onFocus={() => setTooltipAddRegexOpened(false)} onBlur={() => setTooltipAddRegexOpened(false)} - onMouseEnter={() => setTooltipAddRegexOpened(true)} onMouseLeave={() => setTooltipAddRegexOpened(false)}> - - - : - - {regexesList.data.map( (regexInfo) => )} - - } - - {srv? {setOpen(false)}} service={srv} />:null} - - - -} - -export default ServiceDetailsProxyRegex; diff --git a/frontend/src/pages/RegexProxy/index.tsx b/frontend/src/pages/RegexProxy/index.tsx deleted file mode 100644 index 4ddfe31..0000000 --- a/frontend/src/pages/RegexProxy/index.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { ActionIcon, Badge, Box, LoadingOverlay, Space, Title, Tooltip } from '@mantine/core'; -import { useEffect, useState } from 'react'; -import { BsPlusLg } from "react-icons/bs"; -import { useNavigate, useParams } from 'react-router-dom'; -import ServiceRow from '../../components/RegexProxy/ServiceRow'; -import { regexproxyServiceQuery } from '../../components/RegexProxy/utils'; -import { errorNotify, getErrorMessage } from '../../js/utils'; -import AddNewService from '../../components/RegexProxy/AddNewService'; -import AddNewRegex from '../../components/AddNewRegex'; -import { useQueryClient } from '@tanstack/react-query'; -import { TbReload } from 'react-icons/tb'; - - -function RegexProxy({ children }: { children: any }) { - - const navigator = useNavigate() - const [open, setOpen] = useState(false); - const {srv} = useParams() - const [tooltipAddServOpened, setTooltipAddServOpened] = useState(false); - const [tooltipAddOpened, setTooltipAddOpened] = useState(false); - const queryClient = useQueryClient() - const [tooltipRefreshOpened, setTooltipRefreshOpened] = useState(false); - - const services = regexproxyServiceQuery() - - useEffect(()=> { - if(services.isError){ - errorNotify("RegexProxy Update failed!", getErrorMessage(services.error)) - } - },[services.isError]) - - const closeModal = () => {setOpen(false);} - - return <> - - - TCP Proxy Regex Filter (IPv4 Only) - - Services: {services.isLoading?0:services.data?.length} - - Filtered Connections: {services.isLoading?0:services.data?.reduce((acc, s)=> acc+=s.n_packets, 0)} - - Regexes: {services.isLoading?0:services.data?.reduce((acc, s)=> acc+=s.n_regex, 0)} - - { srv? - - setOpen(true)} size="lg" radius="md" variant="filled" - onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)} - onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}> - - : - setOpen(true)} size="lg" radius="md" variant="filled" - onFocus={() => setTooltipAddOpened(false)} onBlur={() => setTooltipAddOpened(false)} - onMouseEnter={() => setTooltipAddOpened(true)} onMouseLeave={() => setTooltipAddOpened(false)}> - - } - - - queryClient.invalidateQueries(["regexproxy"])} size="lg" radius="md" variant="filled" - loading={services.isFetching} - onFocus={() => setTooltipRefreshOpened(false)} onBlur={() => setTooltipRefreshOpened(false)} - onMouseEnter={() => setTooltipRefreshOpened(true)} onMouseLeave={() => setTooltipRefreshOpened(false)}> - - - - {srv?null:<> - - {(services.data && services.data?.length > 0)?services.data.map( srv => { - navigator("/regexproxy/"+srv.id) - }} />):<> No services found! Add one clicking the "+" buttons - - - - setOpen(true)} size="xl" radius="md" variant="filled" - onFocus={() => setTooltipAddServOpened(false)} onBlur={() => setTooltipAddServOpened(false)} - onMouseEnter={() => setTooltipAddServOpened(true)} onMouseLeave={() => setTooltipAddServOpened(false)}> - - - } - - } - - {srv?children:null} - {srv? - : - - } - -} - -export default RegexProxy; diff --git a/tests/api_test.py b/tests/api_test.py index f21f28d..ccc3097 100644 --- a/tests/api_test.py +++ b/tests/api_test.py @@ -1,60 +1,95 @@ #!/usr/bin/env python3 -from utils.colors import * -from utils.firegexapi import * -import argparse, secrets +from utils.colors import colors, puts, sep +from utils.firegexapi import FiregexAPI +import argparse +import secrets parser = argparse.ArgumentParser() parser.add_argument("--address", "-a", type=str , required=False, help='Address of firegex backend', default="http://127.0.0.1:4444/") parser.add_argument("--password", "-p", type=str, required=True, help='Firegex password') args = parser.parse_args() sep() -puts(f"Testing will start on ", color=colors.cyan, end="") +puts("Testing will start on ", color=colors.cyan, end="") puts(f"{args.address}", color=colors.yellow) firegex = FiregexAPI(args.address) #Connect to Firegex if firegex.status()["status"] =="init": - if (firegex.set_password(args.password)): puts(f"Sucessfully set password to {args.password} ✔", color=colors.green) - else: puts(f"Test Failed: Unknown response or password already put ✗", color=colors.red); exit(1) + if (firegex.set_password(args.password)): + puts(f"Sucessfully set password to {args.password} ✔", color=colors.green) + else: + puts("Test Failed: Unknown response or password already put ✗", color=colors.red) + exit(1) else: - if (firegex.login(args.password)): puts(f"Sucessfully logged in ✔", color=colors.green) - else: puts(f"Test Failed: Unknown response or wrong passowrd ✗", color=colors.red); exit(1) + if (firegex.login(args.password)): + puts("Sucessfully logged in ✔", color=colors.green) + else: + puts("Test Failed: Unknown response or wrong passowrd ✗", color=colors.red) + exit(1) -if(firegex.status()["loggined"]): puts(f"Correctly received status ✔", color=colors.green) -else: puts(f"Test Failed: Unknown response or not logged in✗", color=colors.red); exit(1) +if(firegex.status()["loggined"]): + puts("Correctly received status ✔", color=colors.green) +else: + puts("Test Failed: Unknown response or not logged in✗", color=colors.red) + exit(1) #Prepare second instance firegex2 = FiregexAPI(args.address) -if (firegex2.login(args.password)): puts(f"Sucessfully logged in on second instance ✔", color=colors.green) -else: puts(f"Test Failed: Unknown response or wrong passowrd on second instance ✗", color=colors.red); exit(1) +if (firegex2.login(args.password)): + puts("Sucessfully logged in on second instance ✔", color=colors.green) +else: + puts("Test Failed: Unknown response or wrong passowrd on second instance ✗", color=colors.red) + exit(1) -if(firegex2.status()["loggined"]): puts(f"Correctly received status on second instance✔", color=colors.green) -else: puts(f"Test Failed: Unknown response or not logged in on second instance✗", color=colors.red); exit(1) +if(firegex2.status()["loggined"]): + puts("Correctly received status on second instance✔", color=colors.green) +else: + puts("Test Failed: Unknown response or not logged in on second instance✗", color=colors.red) + exit(1) #Change password new_password = secrets.token_hex(10) -if (firegex.change_password(new_password,expire=True)): puts(f"Sucessfully changed password to {new_password} ✔", color=colors.green) -else: puts(f"Test Failed: Coundl't change the password ✗", color=colors.red); exit(1) +if (firegex.change_password(new_password,expire=True)): + puts(f"Sucessfully changed password to {new_password} ✔", color=colors.green) +else: + puts("Test Failed: Coundl't change the password ✗", color=colors.red) + exit(1) #Check if we are still logged in -if(firegex.status()["loggined"]): puts(f"Correctly received status after password change ✔", color=colors.green) -else: puts(f"Test Failed: Unknown response or not logged after password change ✗", color=colors.red); exit(1) +if(firegex.status()["loggined"]): + puts("Correctly received status after password change ✔", color=colors.green) +else: + puts("Test Failed: Unknown response or not logged after password change ✗", color=colors.red) + exit(1) #Check if second session expired and relog -if(not firegex2.status()["loggined"]): puts(f"Second instance was expired currectly ✔", color=colors.green) -else: puts(f"Test Failed: Still logged in on second instance, expire expected ✗", color=colors.red); exit(1) -if (firegex2.login(new_password)): puts(f"Sucessfully logged in on second instance ✔", color=colors.green) -else: puts(f"Test Failed: Unknown response or wrong passowrd on second instance ✗", color=colors.red); exit(1) +if(not firegex2.status()["loggined"]): + puts("Second instance was expired currectly ✔", color=colors.green) +else: + puts("Test Failed: Still logged in on second instance, expire expected ✗", color=colors.red) + exit(1) +if (firegex2.login(new_password)): + puts("Sucessfully logged in on second instance ✔", color=colors.green) +else: + puts("Test Failed: Unknown response or wrong passowrd on second instance ✗", color=colors.red) + exit(1) #Change it back -if (firegex.change_password(args.password,expire=False)): puts(f"Sucessfully restored the password ✔", color=colors.green) -else: puts(f"Test Failed: Coundl't change the password ✗", color=colors.red); exit(1) +if (firegex.change_password(args.password,expire=False)): + puts("Sucessfully restored the password ✔", color=colors.green) +else: + puts("Test Failed: Coundl't change the password ✗", color=colors.red) + exit(1) #Check if we are still logged in -if(firegex2.status()["loggined"]): puts(f"Correctly received status after password change ✔", color=colors.green) -else: puts(f"Test Failed: Unknown response or not logged after password change ✗", color=colors.red); exit(1) +if(firegex2.status()["loggined"]): + puts("Correctly received status after password change ✔", color=colors.green) +else: + puts("Test Failed: Unknown response or not logged after password change ✗", color=colors.red) + exit(1) puts("List of available interfaces:", color=colors.yellow) -for interface in firegex.get_interfaces(): puts("name: {}, address: {}".format(interface["name"], interface["addr"]), color=colors.yellow) \ No newline at end of file +for interface in firegex.get_interfaces(): + puts("name: {}, address: {}".format(interface["name"], interface["addr"]), color=colors.yellow) \ No newline at end of file diff --git a/tests/benchmark.py b/tests/benchmark.py index 6625b53..0ff5a63 100644 --- a/tests/benchmark.py +++ b/tests/benchmark.py @@ -1,24 +1,25 @@ #!/usr/bin/env python3 -from utils.colors import * -from utils.firegexapi import * -from utils.tcpserver import * +from utils.colors import colors, puts, sep +from utils.firegexapi import FiregexAPI from multiprocessing import Process from time import sleep -import iperf3, csv, argparse, base64, secrets +import iperf3 +import csv +import argparse +import base64 +import secrets #TODO: make it work with Proxy and not only netfilter parser = argparse.ArgumentParser() parser.add_argument("--address", "-a", type=str , required=False, help='Address of firegex backend', default="http://127.0.0.1:4444/") parser.add_argument("--port", "-P", type=int , required=False, help='Port of the Benchmark service', default=1337) -parser.add_argument("--internal-port", "-I", type=int , required=False, help='Internal port of the Benchmark service', default=1338) parser.add_argument("--service-name", "-n", type=str , required=False, help='Name of the Benchmark service', default="Benchmark Service") parser.add_argument("--password", "-p", type=str, required=True, help='Firegex password') parser.add_argument("--num-of-regexes", "-r", type=int, required=True, help='Number of regexes to benchmark with') parser.add_argument("--duration", "-d", type=int, required=False, help='Duration of the Benchmark in seconds', default=5) parser.add_argument("--output-file", "-o", type=str, required=False, help='Output results csv file', default="benchmark.csv") parser.add_argument("--num-of-streams", "-s", type=int, required=False, help='Output results csv file', default=1) -parser.add_argument("--mode", "-m" , type=str, required=True, choices=["netfilter","proxy"], help='Type of filtering') args = parser.parse_args() sep() @@ -28,22 +29,36 @@ puts(f"{args.address}", color=colors.yellow) firegex = FiregexAPI(args.address) #Connect to Firegex -if (firegex.login(args.password)): puts(f"Sucessfully logged in ✔", color=colors.green) -else: puts(f"Benchmark Failed: Unknown response or wrong passowrd ✗", color=colors.red); exit(1) +if (firegex.login(args.password)): + puts("Sucessfully logged in ✔", color=colors.green) +else: + puts("Benchmark Failed: Unknown response or wrong passowrd ✗", color=colors.red) + exit(1) + +def exit_test(code): + if service_id: + server.stop() + if(firegex.nf_delete_service(service_id)): + puts("Sucessfully deleted service ✔", color=colors.green) + else: + puts("Test Failed: Coulnd't delete serivce ✗", color=colors.red) + exit_test(1) + exit(code) #Create new Service -if args.mode == "netfilter": - service_id = firegex.nf_add_service(args.service_name, args.port, "tcp", "127.0.0.1/24") + +service_id = firegex.nf_add_service(args.service_name, args.port, "tcp", "127.0.0.1/24") +if service_id: + puts(f"Sucessfully created service {service_id} ✔", color=colors.green) else: - service_id = firegex.px_add_service(args.service_name, args.port, internalPort=args.internal_port) -if service_id: puts(f"Sucessfully created service {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Failed to create service ✗", color=colors.red); exit(1) + puts("Test Failed: Failed to create service ✗", color=colors.red) + exit(1) #Start iperf3 def startServer(): server = iperf3.Server() server.bind_address = '127.0.0.1' - server.port = args.port if args.mode == "netfilter" else args.internal_port + server.port = args.port server.verbose = False while True: server.run() @@ -63,27 +78,29 @@ sleep(1) #Get baseline reading -puts(f"Baseline without proxy: ", color=colors.blue, end='') -print(f"{getReading(args.port if args.mode == 'netfilter' else args.internal_port)} MB/s") +puts("Baseline without proxy: ", color=colors.blue, end='') +print(f"{getReading(args.port)} MB/s") #Start firewall -if(firegex.nf_start_service(service_id) if args.mode == "netfilter" else firegex.px_start_service(service_id)): - puts(f"Sucessfully started service with id {service_id} ✔", color=colors.green) +if firegex.nf_start_service(service_id): + puts(f"Sucessfully started service with id {service_id} ✔", color=colors.green) else: - puts(f"Benchmark Failed: Coulnd't start the service ✗", color=colors.red); exit_test(1) + puts("Benchmark Failed: Coulnd't start the service ✗", color=colors.red) + exit_test(1) #Get no regexs reading results = [] -puts(f"Performance with no regexes: ", color=colors.yellow , end='') +puts("Performance with no regexes: ", color=colors.yellow , end='') results.append(getReading(args.port)) print(f"{results[0]} MB/s") #Add all the regexs for i in range(1,args.num_of_regexes+1): regex = base64.b64encode(bytes(secrets.token_hex(16).encode())).decode() - if(not (firegex.nf_add_regex if args.mode == "netfilter" else firegex.px_add_regex)(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=False) ): - puts(f"Benchmark Failed: Coulnd't add the regex ✗", color=colors.red); exit_test(1) + if not firegex.nf_add_regex(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=False): + puts("Benchmark Failed: Coulnd't add the regex ✗", color=colors.red) + exit_test(1) puts(f"Performance with {i} regex(s): ", color=colors.red, end='') results.append(getReading(args.port)) print(f"{results[i]} MB/s") @@ -96,9 +113,10 @@ with open(args.output_file,'w') as f: puts(f"Sucessfully written results to {args.output_file} ✔", color=colors.magenta) #Delete the Service -if(firegex.nf_delete_service(service_id) if args.mode == "netfilter" else firegex.px_delete_service(service_id)): +if firegex.nf_delete_service(service_id): puts(f"Sucessfully delete service with id {service_id} ✔", color=colors.green) else: - puts(f"Test Failed: Couldn't delete service ✗", color=colors.red); exit(1) + puts("Test Failed: Couldn't delete service ✗", color=colors.red) + exit(1) server.terminate() \ No newline at end of file diff --git a/tests/nf_test.py b/tests/nf_test.py index 67c0046..7ff0405 100644 --- a/tests/nf_test.py +++ b/tests/nf_test.py @@ -1,9 +1,12 @@ #!/usr/bin/env python3 -from utils.colors import * -from utils.firegexapi import * +from utils.colors import colors, puts, sep +from utils.firegexapi import FiregexAPI from utils.tcpserver import TcpServer from utils.udpserver import UdpServer -import argparse, secrets, base64,time +import argparse +import secrets +import base64 +import time parser = argparse.ArgumentParser() parser.add_argument("--address", "-a", type=str , required=False, help='Address of firegex backend', default="http://127.0.0.1:4444/") @@ -15,14 +18,17 @@ parser.add_argument("--proto", "-m" , type=str, required=False, choices=["tcp"," args = parser.parse_args() sep() -puts(f"Testing will start on ", color=colors.cyan, end="") +puts("Testing will start on ", color=colors.cyan, end="") puts(f"{args.address}", color=colors.yellow) firegex = FiregexAPI(args.address) #Login -if (firegex.login(args.password)): puts(f"Sucessfully logged in ✔", color=colors.green) -else: puts(f"Test Failed: Unknown response or wrong passowrd ✗", color=colors.red); exit(1) +if (firegex.login(args.password)): + puts("Sucessfully logged in ✔", color=colors.green) +else: + puts("Test Failed: Unknown response or wrong passowrd ✗", color=colors.red) + exit(1) #Create server server = (TcpServer if args.proto == "tcp" else UdpServer)(args.port,ipv6=args.ipv6) @@ -30,30 +36,42 @@ server = (TcpServer if args.proto == "tcp" else UdpServer)(args.port,ipv6=args.i def exit_test(code): if service_id: server.stop() - if(firegex.nf_delete_service(service_id)): puts(f"Sucessfully deleted service ✔", color=colors.green) - else: puts(f"Test Failed: Coulnd't delete serivce ✗", color=colors.red); exit_test(1) + if(firegex.nf_delete_service(service_id)): + puts("Sucessfully deleted service ✔", color=colors.green) + else: + puts("Test Failed: Coulnd't delete serivce ✗", color=colors.red) + exit_test(1) exit(code) service_id = firegex.nf_add_service(args.service_name, args.port, args.proto , "::1" if args.ipv6 else "127.0.0.1" ) -if service_id: puts(f"Sucessfully created service {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Failed to create service ✗", color=colors.red); exit(1) +if service_id: + puts("Sucessfully created service {service_id} ✔", color=colors.green) +else: + puts("Test Failed: Failed to create service ✗", color=colors.red) + exit(1) -if(firegex.nf_start_service(service_id)): puts(f"Sucessfully started service ✔", color=colors.green) -else: puts(f"Test Failed: Failed to start service ✗", color=colors.red); exit_test(1) +if(firegex.nf_start_service(service_id)): + puts("Sucessfully started service ✔", color=colors.green) +else: + puts("Test Failed: Failed to start service ✗", color=colors.red) + exit_test(1) server.start() time.sleep(0.5) if server.sendCheckData(secrets.token_bytes(432)): - puts(f"Successfully tested first proxy with no regex ✔", color=colors.green) + puts("Successfully tested first proxy with no regex ✔", color=colors.green) else: - puts(f"Test Failed: Data was corrupted ", color=colors.red); exit_test(1) + puts("Test Failed: Data was corrupted ", color=colors.red) + exit_test(1) #Add new regex secret = bytes(secrets.token_hex(16).encode()) regex = base64.b64encode(secret).decode() -if(firegex.nf_add_regex(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=True)): +if firegex.nf_add_regex(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=True): puts(f"Sucessfully added regex {str(secret)} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't add the regex {str(secret)} ✗", color=colors.red); exit_test(1) +else: + puts("Test Failed: Coulnd't add the regex {str(secret)} ✗", color=colors.red) + exit_test(1) #Check if regex is present in the service n_blocked = 0 @@ -66,35 +84,45 @@ def checkRegex(regex, should_work=True, upper=False): #Test the regex s = base64.b64decode(regex).upper() if upper else base64.b64decode(regex) if not server.sendCheckData(secrets.token_bytes(200) + s + secrets.token_bytes(200)): - puts(f"The malicious request was successfully blocked ✔", color=colors.green) + puts("The malicious request was successfully blocked ✔", color=colors.green) n_blocked += 1 time.sleep(1) if firegex.nf_get_regex(r["id"])["n_packets"] == n_blocked: - puts(f"The packed was reported as blocked ✔", color=colors.green) + puts("The packed was reported as blocked ✔", color=colors.green) else: - puts(f"Test Failed: The packed wasn't reported as blocked ✗", color=colors.red); exit_test(1) + puts("Test Failed: The packed wasn't reported as blocked ✗", color=colors.red) + exit_test(1) else: - puts(f"Test Failed: The request wasn't blocked ✗", color=colors.red);exit_test(1) + puts("Test Failed: The request wasn't blocked ✗", color=colors.red) + exit_test(1) return - puts(f"Test Failed: The regex wasn't found ✗", color=colors.red); exit_test(1) + puts("Test Failed: The regex wasn't found ✗", color=colors.red) + exit_test(1) else: if server.sendCheckData(secrets.token_bytes(200) + base64.b64decode(regex) + secrets.token_bytes(200)): - puts(f"The request wasn't blocked ✔", color=colors.green) + puts("The request wasn't blocked ✔", color=colors.green) else: - puts(f"Test Failed: The request was blocked when it shouldn't have", color=colors.red); exit_test(1) + puts("Test Failed: The request was blocked when it shouldn't have", color=colors.red) + exit_test(1) checkRegex(regex) #Pause the proxy -if(firegex.nf_stop_service(service_id)): puts(f"Sucessfully paused service with id {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't pause the service ✗", color=colors.red); exit_test(1) +if(firegex.nf_stop_service(service_id)): + puts(f"Sucessfully paused service with id {service_id} ✔", color=colors.green) +else: + puts("Test Failed: Coulnd't pause the service ✗", color=colors.red) + exit_test(1) #Check if it's actually paused checkRegex(regex,should_work=False) #Start firewall -if(firegex.nf_start_service(service_id)): puts(f"Sucessfully started service with id {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't start the service ✗", color=colors.red); exit_test(1) +if(firegex.nf_start_service(service_id)): + puts(f"Sucessfully started service with id {service_id} ✔", color=colors.green) +else: + puts("Test Failed: Coulnd't start the service ✗", color=colors.red) + exit_test(1) checkRegex(regex) @@ -104,7 +132,8 @@ for r in firegex.nf_get_service_regexes(service_id): if(firegex.nf_disable_regex(r["id"])): puts(f"Sucessfully disabled regex with id {r['id']} ✔", color=colors.green) else: - puts(f"Test Failed: Coulnd't disable the regex ✗", color=colors.red); exit_test(1) + puts("Test Failed: Coulnd't disable the regex ✗", color=colors.red) + exit_test(1) break #Check if it's actually disabled @@ -116,7 +145,8 @@ for r in firegex.nf_get_service_regexes(service_id): if(firegex.nf_enable_regex(r["id"])): puts(f"Sucessfully enabled regex with id {r['id']} ✔", color=colors.green) else: - puts(f"Test Failed: Coulnd't enable the regex ✗", color=colors.red); exit_test(1) + puts("Test Failed: Coulnd't enable the regex ✗", color=colors.red) + exit_test(1) break checkRegex(regex) @@ -128,7 +158,8 @@ for r in firegex.nf_get_service_regexes(service_id): if(firegex.nf_delete_regex(r["id"])): puts(f"Sucessfully deleted regex with id {r['id']} ✔", color=colors.green) else: - puts(f"Test Failed: Coulnd't delete the regex ✗", color=colors.red); exit_test(1) + puts("Test Failed: Coulnd't delete the regex ✗", color=colors.red) + exit_test(1) break #Check if it's actually deleted @@ -137,7 +168,9 @@ checkRegex(regex,should_work=False) #Add case insensitive regex if(firegex.nf_add_regex(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=False)): puts(f"Sucessfully added case insensitive regex {str(secret)} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't add the case insensitive regex {str(secret)} ✗", color=colors.red); exit_test(1) +else: + puts(f"Test Failed: Coulnd't add the case insensitive regex {str(secret)} ✗", color=colors.red) + exit_test(1) checkRegex(regex,upper=True) checkRegex(regex) @@ -149,36 +182,22 @@ for r in firegex.nf_get_service_regexes(service_id): if(firegex.nf_delete_regex(r["id"])): puts(f"Sucessfully deleted regex with id {r['id']} ✔", color=colors.green) else: - puts(f"Test Failed: Coulnd't delete the regex ✗", color=colors.red); exit_test(1) - break - -#Add whitelist regex -if(firegex.nf_add_regex(service_id,regex,"B",active=True,is_blacklist=False,is_case_sensitive=True)): - puts(f"Sucessfully added case whitelist regex {str(secret)} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't add the case whiteblist regex {str(secret)} ✗", color=colors.red); exit_test(1) - -checkRegex(regex,should_work=False) -checkRegex(regex,upper=True) #Dirty way to test the whitelist :p - -#Delete regex -n_blocked = 0 -for r in firegex.nf_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.nf_delete_regex(r["id"])): - puts(f"Sucessfully deleted regex with id {r['id']} ✔", color=colors.green) - else: - puts(f"Test Failed: Coulnd't delete the regex ✗", color=colors.red); exit_test(1) + puts("Test Failed: Coulnd't delete the regex ✗", color=colors.red) + exit_test(1) break #Rename service -if(firegex.nf_rename_service(service_id,f"{args.service_name}2")): puts(f"Sucessfully renamed service to {args.service_name}2 ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't rename service ✗", color=colors.red); exit_test(1) +if(firegex.nf_rename_service(service_id,f"{args.service_name}2")): + puts(f"Sucessfully renamed service to {args.service_name}2 ✔", color=colors.green) +else: + puts("Test Failed: Coulnd't rename service ✗", color=colors.red) + exit_test(1) #Check if service was renamed correctly for services in firegex.nf_get_services(): if services["name"] == f"{args.service_name}2": - puts(f"Checked that service was renamed correctly ✔", color=colors.green) + puts("Checked that service was renamed correctly ✔", color=colors.green) exit_test(0) -puts(f"Test Failed: Service wasn't renamed correctly ✗", color=colors.red); exit_test(1) +puts("Test Failed: Service wasn't renamed correctly ✗", color=colors.red) exit_test(1) diff --git a/tests/ph_test.py b/tests/ph_test.py index 02cf222..98bae5a 100644 --- a/tests/ph_test.py +++ b/tests/ph_test.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 -from utils.colors import * -from utils.firegexapi import * +from utils.colors import colors, puts, sep +from utils.firegexapi import FiregexAPI from utils.tcpserver import TcpServer from utils.udpserver import UdpServer -import argparse, secrets, base64,time +import argparse +import secrets +import time parser = argparse.ArgumentParser() parser.add_argument("--address", "-a", type=str , required=False, help='Address of firegex backend', default="http://127.0.0.1:4444/") @@ -15,14 +17,17 @@ parser.add_argument("--proto", "-m" , type=str, required=False, choices=["tcp"," args = parser.parse_args() sep() -puts(f"Testing will start on ", color=colors.cyan, end="") +puts("Testing will start on ", color=colors.cyan, end="") puts(f"{args.address}", color=colors.yellow) firegex = FiregexAPI(args.address) #Login -if (firegex.login(args.password)): puts(f"Sucessfully logged in ✔", color=colors.green) -else: puts(f"Test Failed: Unknown response or wrong passowrd ✗", color=colors.red); exit(1) +if (firegex.login(args.password)): + puts("Sucessfully logged in ✔", color=colors.green) +else: + puts("Test Failed: Unknown response or wrong passowrd ✗", color=colors.red) + exit(1) #Create server server = (TcpServer if args.proto == "tcp" else UdpServer)(args.port+1,ipv6=args.ipv6,proxy_port=args.port) @@ -30,17 +35,26 @@ server = (TcpServer if args.proto == "tcp" else UdpServer)(args.port+1,ipv6=args def exit_test(code): if service_id: server.stop() - if(firegex.ph_delete_service(service_id)): puts(f"Sucessfully deleted service ✔", color=colors.green) - else: puts(f"Test Failed: Coulnd't delete serivce ✗", color=colors.red); exit_test(1) + if(firegex.ph_delete_service(service_id)): + puts("Sucessfully deleted service ✔", color=colors.green) + else: + puts("Test Failed: Coulnd't delete serivce ✗", color=colors.red) + exit_test(1) exit(code) #Create and start serivce service_id = firegex.ph_add_service(args.service_name, args.port, args.port+1, args.proto , "::1" if args.ipv6 else "127.0.0.1", "::1" if args.ipv6 else "127.0.0.1") -if service_id: puts(f"Sucessfully created service {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Failed to create service ✗", color=colors.red); exit(1) +if service_id: + puts("Sucessfully created service {service_id} ✔", color=colors.green) +else: + puts("Test Failed: Failed to create service ✗", color=colors.red) + exit(1) -if(firegex.ph_start_service(service_id)): puts(f"Sucessfully started service ✔", color=colors.green) -else: puts(f"Test Failed: Failed to start service ✗", color=colors.red); exit_test(1) +if(firegex.ph_start_service(service_id)): + puts("Sucessfully started service ✔", color=colors.green) +else: + puts("Test Failed: Failed to start service ✗", color=colors.red) + exit_test(1) server.start() time.sleep(0.5) @@ -48,33 +62,49 @@ time.sleep(0.5) #Check if it started def checkData(should_work): res = None - try: res = server.sendCheckData(secrets.token_bytes(432)) - except (ConnectionRefusedError, TimeoutError): res = None + try: + res = server.sendCheckData(secrets.token_bytes(432)) + except (ConnectionRefusedError, TimeoutError): + res = None if res: - if should_work: puts(f"Successfully received data ✔", color=colors.green) - else: puts("Test Failed: Connection wasn't blocked ✗", color=colors.red); exit_test(1) + if should_work: + puts("Successfully received data ✔", color=colors.green) + else: + puts("Test Failed: Connection wasn't blocked ✗", color=colors.red) + exit_test(1) else: - if should_work: puts(f"Test Failed: Data wans't received ✗", color=colors.red); exit_test(1) - else: puts(f"Successfully blocked connection ✔", color=colors.green) + if should_work: + puts("Test Failed: Data wans't received ✗", color=colors.red) + exit_test(1) + else: + puts("Successfully blocked connection ✔", color=colors.green) checkData(True) #Pause the proxy -if(firegex.ph_stop_service(service_id)): puts(f"Sucessfully paused service with id {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't pause the service ✗", color=colors.red); exit_test(1) +if(firegex.ph_stop_service(service_id)): + puts(f"Sucessfully paused service with id {service_id} ✔", color=colors.green) +else: + puts("Test Failed: Coulnd't pause the service ✗", color=colors.red) + exit_test(1) checkData(False) #Start firewall -if(firegex.ph_start_service(service_id)): puts(f"Sucessfully started service with id {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't start the service ✗", color=colors.red); exit_test(1) +if(firegex.ph_start_service(service_id)): + puts(f"Sucessfully started service with id {service_id} ✔", color=colors.green) +else: + puts("Test Failed: Coulnd't start the service ✗", color=colors.red) + exit_test(1) checkData(True) #Change port if(firegex.ph_change_destination(service_id, "::1" if args.ipv6 else "127.0.0.1", args.port+2)): - puts(f"Sucessfully changed port ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't change destination ✗", color=colors.red); exit_test(1) + puts("Sucessfully changed port ✔", color=colors.green) +else: + puts("Test Failed: Coulnd't change destination ✗", color=colors.red) + exit_test(1) checkData(False) @@ -86,14 +116,17 @@ time.sleep(0.5) checkData(True) #Rename service -if(firegex.ph_rename_service(service_id,f"{args.service_name}2")): puts(f"Sucessfully renamed service to {args.service_name}2 ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't rename service ✗", color=colors.red); exit_test(1) +if(firegex.ph_rename_service(service_id,f"{args.service_name}2")): + puts(f"Sucessfully renamed service to {args.service_name}2 ✔", color=colors.green) +else: + puts("Test Failed: Coulnd't rename service ✗", color=colors.red) + exit_test(1) #Check if service was renamed correctly for services in firegex.ph_get_services(): if services["name"] == f"{args.service_name}2": - puts(f"Checked that service was renamed correctly ✔", color=colors.green) + puts("Checked that service was renamed correctly ✔", color=colors.green) exit_test(0) -puts(f"Test Failed: Service wasn't renamed correctly ✗", color=colors.red); exit_test(1) +puts("Test Failed: Service wasn't renamed correctly ✗", color=colors.red) exit_test(1) diff --git a/tests/px_test.py b/tests/px_test.py deleted file mode 100644 index 46eccf9..0000000 --- a/tests/px_test.py +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env python3 -from utils.colors import * -from utils.firegexapi import * -from utils.tcpserver import TcpServer -import argparse, secrets, base64,time,random - -parser = argparse.ArgumentParser() -parser.add_argument("--address", "-a", type=str , required=False, help='Address of firegex backend', default="http://127.0.0.1:4444/") -parser.add_argument("--password", "-p", type=str, required=True, help='Firegex password') -parser.add_argument("--service_name", "-n", type=str , required=False, help='Name of the test service', default="Test Service") -parser.add_argument("--port", "-P", type=int , required=False, help='Port of the test service', default=1337) -args = parser.parse_args() -sep() -puts(f"Testing will start on ", color=colors.cyan, end="") -puts(f"{args.address}", color=colors.yellow) - -#Create and start server -server = TcpServer(args.port,ipv6=False) -server.start() -time.sleep(0.5) - -firegex = FiregexAPI(args.address) - -#Login -if (firegex.login(args.password)): puts(f"Sucessfully logged in ✔", color=colors.green) -else: puts(f"Test Failed: Unknown response or wrong passowrd ✗", color=colors.red); exit(1) - -def exit_test(code): - if service_id: - server.stop() - if(firegex.px_delete_service(service_id)): puts(f"Sucessfully deleted service ✔", color=colors.green) - else: puts(f"Test Failed: Coulnd't deleted serivce ✗", color=colors.red); exit_test(1) - exit(code) - -#Create service -service_id = firegex.px_add_service(args.service_name, args.port, 6140) -if service_id: puts(f"Sucessfully created service {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Failed to create service ✗", color=colors.red); exit(1) - -if(firegex.px_start_service(service_id)): puts(f"Sucessfully started service ✔", color=colors.green) -else: puts(f"Test Failed: Failed to start service ✗", color=colors.red); exit_test(1) - -#Check if service is in wait mode -if(firegex.px_get_service(service_id)["status"] == "wait"): puts(f"Sucessfully started service in WAIT mode ✔", color=colors.green) -else: puts(f"Test Failed: Service not in WAIT mode ✗", color=colors.red); exit_test(1) - -#Get inernal_port -internal_port = firegex.px_get_service(service_id)["internal_port"] -if (internal_port): puts(f"Sucessfully got internal port {internal_port} ✔", color=colors.green) -else: puts(f"Test Failed: Coundn't get internal_port ✗", color=colors.red); exit_test(1) - -server.stop() -server = TcpServer(internal_port,ipv6=False, proxy_port=args.port) -server.start() -time.sleep(1) - -if(firegex.px_get_service(service_id)["status"] == "active"): puts(f"Service went in ACTIVE mode ✔", color=colors.green) -else: puts(f"Test Failed: Service not in ACTIVE mode ✗", color=colors.red); exit_test(1) - -if server.sendCheckData(secrets.token_bytes(432)): - puts(f"Successfully tested first proxy with no regex ✔", color=colors.green) -else: - puts(f"Test Failed: Data was corrupted ", color=colors.red); exit_test(1) - -#Add new regex -secret = bytes(secrets.token_hex(16).encode()) -regex = base64.b64encode(secret).decode() -if(firegex.px_add_regex(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=True)): - puts(f"Sucessfully added regex {str(secret)} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't add the regex {str(secret)} ✗", color=colors.red); exit_test(1) - -#Check if regex is present in the service -n_blocked = 0 - -def checkRegex(regex, should_work=True, upper=False): - if should_work: - global n_blocked - for r in firegex.px_get_service_regexes(service_id): - if r["regex"] == regex: - #Test the regex - s = base64.b64decode(regex).upper() if upper else base64.b64decode(regex) - if not server.sendCheckData(secrets.token_bytes(200) + s + secrets.token_bytes(200)): - puts(f"The malicious request was successfully blocked ✔", color=colors.green) - n_blocked += 1 - time.sleep(0.5) - if firegex.px_get_regex(r["id"])["n_packets"] == n_blocked: - puts(f"The packed was reported as blocked ✔", color=colors.green) - else: - puts(f"Test Failed: The packed wasn't reported as blocked ✗", color=colors.red); exit_test(1) - else: - puts(f"Test Failed: The request wasn't blocked ✗", color=colors.red);exit_test(1) - return - puts(f"Test Failed: The regex wasn't found ✗", color=colors.red); exit_test(1) - else: - if server.sendCheckData(secrets.token_bytes(200) + base64.b64decode(regex) + secrets.token_bytes(200)): - puts(f"The request wasn't blocked ✔", color=colors.green) - else: - puts(f"Test Failed: The request was blocked when it shouldn't have", color=colors.red); exit_test(1) - -checkRegex(regex) - -#Pause the proxy -if(firegex.px_pause_service(service_id)): puts(f"Sucessfully paused service with id {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't pause the service ✗", color=colors.red); exit_test(1) - -#Check if it's actually paused -checkRegex(regex,should_work=False) - -#Start firewall -if(firegex.px_start_service(service_id)): puts(f"Sucessfully started service with id {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't start the service ✗", color=colors.red); exit_test(1) - -checkRegex(regex) - -#Stop firewall -if(firegex.px_stop_service(service_id)): puts(f"Sucessfully stopped service with id {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't stop the service ✗", color=colors.red); exit_test(1) - -try: - checkRegex(regex) - puts(f"Test Failed: The service was still active ✗", color=colors.red); exit_test(1) -except Exception: - puts(f"Service was correctly stopped ✔", color=colors.green) - -#Start firewall in pause -if(firegex.px_pause_service(service_id)): puts(f"Sucessfully started service in pause mode with id {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't start the service ✗", color=colors.red); exit_test(1) - -time.sleep(0.5) -#Check if it's actually paused -checkRegex(regex,should_work=False) - -#Start firewall -if(firegex.px_start_service(service_id)): puts(f"Sucessfully started service with id {service_id} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't start the service ✗", color=colors.red); exit_test(1) - -checkRegex(regex) - -#Disable regex -for r in firegex.px_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.px_disable_regex(r["id"])): - puts(f"Sucessfully disabled regex with id {r['id']} ✔", color=colors.green) - else: - puts(f"Test Failed: Coulnd't disable the regex ✗", color=colors.red); exit_test(1) - break - -#Check if it's actually disabled -checkRegex(regex,should_work=False) - -#Enable regex -for r in firegex.px_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.px_enable_regex(r["id"])): - puts(f"Sucessfully enabled regex with id {r['id']} ✔", color=colors.green) - else: - puts(f"Test Failed: Coulnd't enable the regex ✗", color=colors.red); exit_test(1) - break - -checkRegex(regex) - -#Delete regex -n_blocked = 0 -for r in firegex.px_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.px_delete_regex(r["id"])): - puts(f"Sucessfully deleted regex with id {r['id']} ✔", color=colors.green) - else: - puts(f"Test Failed: Coulnd't delete the regex ✗", color=colors.red); exit_test(1) - break - -#Check if it's actually deleted -checkRegex(regex,should_work=False) - -#Add case insensitive regex -if(firegex.px_add_regex(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=False)): - puts(f"Sucessfully added case insensitive regex {str(secret)} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't add the case insensitive regex {str(secret)} ✗", color=colors.red); exit_test(1) - -checkRegex(regex,upper=True) -checkRegex(regex) - -#Delete regex -n_blocked = 0 -for r in firegex.px_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.px_delete_regex(r["id"])): - puts(f"Sucessfully deleted regex with id {r['id']} ✔", color=colors.green) - else: - puts(f"Test Failed: Coulnd't delete the regex ✗", color=colors.red); exit_test(1) - break - -#Add whitelist regex -if(firegex.px_add_regex(service_id,regex,"B",active=True,is_blacklist=False,is_case_sensitive=True)): - puts(f"Sucessfully added case whitelist regex {str(secret)} ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't add the case whiteblist regex {str(secret)} ✗", color=colors.red); exit_test(1) - -checkRegex(regex,should_work=False) -checkRegex(regex,upper=True) #Dirty way to test the whitelist :p - -#Delete regex -n_blocked = 0 -for r in firegex.px_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.px_delete_regex(r["id"])): - puts(f"Sucessfully deleted regex with id {r['id']} ✔", color=colors.green) - else: - puts(f"Test Failed: Coulnd't delete the regex ✗", color=colors.red); exit_test(1) - break - -#Rename service -if(firegex.px_rename_service(service_id,f"{args.service_name}2")): puts(f"Sucessfully renamed service to {args.service_name}2 ✔", color=colors.green) -else: puts(f"Test Failed: Coulnd't rename service ✗", color=colors.red); exit_test(1) - -#Check if service was renamed correctly -found = False -for services in firegex.px_get_services(): - if services["name"] == f"{args.service_name}2": - puts(f"Checked that service was renamed correctly ✔", color=colors.green) - found = True - break - -if not found: - puts(f"Test Failed: Service wasn't renamed correctly ✗", color=colors.red); exit_test(1) - exit(1) - -#Change service port -new_internal_port = random.randrange(6000,9000) -if(firegex.px_change_service_port(service_id,internalPort=new_internal_port)): - puts(f"Sucessfully changed internal_port to {new_internal_port} ✔", color=colors.green) -else: - puts(f"Test Failed: Coulnd't change intenral port ✗", color=colors.red); exit_test(1) - -#Get inernal_port -internal_port = firegex.px_get_service(service_id)["internal_port"] -if (internal_port == new_internal_port): puts(f"Sucessfully got internal port {internal_port} ✔", color=colors.green) -else: puts(f"Test Failed: Coundn't get internal_port or port changed incorrectly ✗", color=colors.red); exit_test(1) - -if(firegex.px_regen_service_port(service_id)): - puts(f"Sucessfully changed internal_port to {new_internal_port} ✔", color=colors.green) -else: - puts(f"Test Failed: Coulnd't change internal port ✗", color=colors.red); exit_test(1) - -#Get regenerated inernal_port -new_internal_port = firegex.px_get_service(service_id)["internal_port"] -if (internal_port != new_internal_port): puts(f"Sucessfully got regenerated port {new_internal_port} ✔", color=colors.green) -else: puts(f"Test Failed: Coundn't get internal port, or it was the same as previous ✗", color=colors.red); exit_test(1) - -exit_test(0) diff --git a/tests/run_tests.sh b/tests/run_tests.sh index e38aa49..cfe00d9 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -18,8 +18,6 @@ echo "Running Netfilter Regex UDP ipv4" python3 nf_test.py -p $PASSWORD -m udp || ERROR=1 echo "Running Netfilter Regex UDP ipv6" python3 nf_test.py -p $PASSWORD -m udp -6 || ERROR=1 -echo "Running Proxy Regex" -python3 px_test.py -p $PASSWORD || ERROR=1 echo "Running Port Hijack TCP ipv4" python3 ph_test.py -p $PASSWORD -m tcp || ERROR=1 echo "Running Port Hijack TCP ipv6" diff --git a/backend/modules/regexproxy/__init__.py b/tests/utils/__init__.py similarity index 100% rename from backend/modules/regexproxy/__init__.py rename to tests/utils/__init__.py diff --git a/tests/utils/firegexapi.py b/tests/utils/firegexapi.py index 51da773..ad2930b 100644 --- a/tests/utils/firegexapi.py +++ b/tests/utils/firegexapi.py @@ -70,7 +70,7 @@ class FiregexAPI: return req.json() def reset(self, delete: bool): - req = self.s.post(f"{self.address}api/reset", json={"delete":delete}) + self.s.post(f"{self.address}api/reset", json={"delete":delete}) #Netfilter regex def nf_get_stats(self): @@ -131,84 +131,6 @@ class FiregexAPI: json={"name":name,"port":port, "proto": proto, "ip_int": ip_int}) return req.json()["service_id"] if verify(req) else False - #Proxy regex - def px_get_stats(self): - req = self.s.get(f"{self.address}api/regexproxy/stats") - return req.json() - - def px_get_services(self): - req = self.s.get(f"{self.address}api/regexproxy/services") - return req.json() - - def px_get_service(self,service_id: str): - req = self.s.get(f"{self.address}api/regexproxy/service/{service_id}") - return req.json() - - def px_stop_service(self,service_id: str): - req = self.s.get(f"{self.address}api/regexproxy/service/{service_id}/stop") - return verify(req) - - def px_pause_service(self,service_id: str): - req = self.s.get(f"{self.address}api/regexproxy/service/{service_id}/pause") - return verify(req) - - def px_start_service(self,service_id: str): - req = self.s.get(f"{self.address}api/regexproxy/service/{service_id}/start") - return verify(req) - - def px_delete_service(self,service_id: str): - req = self.s.get(f"{self.address}api/regexproxy/service/{service_id}/delete") - return verify(req) - - def px_regen_service_port(self,service_id: str): - req = self.s.get(f"{self.address}api/regexproxy/service/{service_id}/regen-port") - return verify(req) - - def px_change_service_port(self,service_id: str, port:int =None, internalPort:int =None): - payload = {} - if port: payload["port"] = port - if internalPort: payload["internalPort"] = internalPort - req = self.s.post(f"{self.address}api/regexproxy/service/{service_id}/change-ports", json=payload) - return req.json() if verify(req) else False - - def px_get_service_regexes(self,service_id: str): - req = self.s.get(f"{self.address}api/regexproxy/service/{service_id}/regexes") - return req.json() - - def px_get_regex(self,regex_id: str): - req = self.s.get(f"{self.address}api/regexproxy/regex/{regex_id}") - return req.json() - - def px_delete_regex(self,regex_id: str): - req = self.s.get(f"{self.address}api/regexproxy/regex/{regex_id}/delete") - return verify(req) - - def px_enable_regex(self,regex_id: str): - req = self.s.get(f"{self.address}api/regexproxy/regex/{regex_id}/enable") - return verify(req) - - def px_disable_regex(self,regex_id: str): - req = self.s.get(f"{self.address}api/regexproxy/regex/{regex_id}/disable") - return verify(req) - - def px_add_regex(self, service_id: str, regex: str, mode: str, active: bool, is_blacklist: bool, is_case_sensitive: bool): - req = self.s.post(f"{self.address}api/regexproxy/regexes/add", - json={"service_id": service_id, "regex": regex, "mode": mode, "active": active, "is_blacklist": is_blacklist, "is_case_sensitive": is_case_sensitive}) - return verify(req) - - def px_rename_service(self,service_id: str, newname: str): - req = self.s.post(f"{self.address}api/regexproxy/service/{service_id}/rename" , json={"name":newname}) - return verify(req) - - def px_add_service(self, name: str, port: int, internalPort:int = None): - payload = {} - payload["name"] = name - payload["port"] = port - if internalPort: - payload["internalPort"] = internalPort - req = self.s.post(f"{self.address}api/regexproxy/services/add" , json=payload) - return req.json()["id"] if verify(req) else False - #PortHijack def ph_get_services(self): req = self.s.get(f"{self.address}api/porthijack/services") From 732b68ba828e17d1a47cfd7923f379cca48a2136 Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Sun, 2 Feb 2025 20:42:01 +0100 Subject: [PATCH 02/11] fix frontend versions --- Dockerfile | 9 +- frontend/bun.lock | 484 ++++++++++++++++++++++++++++++++++++++++++ frontend/bun.lockb | Bin 115834 -> 0 bytes frontend/package.json | 8 +- 4 files changed, 492 insertions(+), 9 deletions(-) create mode 100644 frontend/bun.lock delete mode 100755 frontend/bun.lockb diff --git a/Dockerfile b/Dockerfile index 1922bea..c576406 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ FROM --platform=$BUILDPLATFORM oven/bun AS frontend WORKDIR /app ADD ./frontend/package.json . -ADD ./frontend/bun.lockb . +ADD ./frontend/bun.lock . RUN bun i COPY ./frontend/ . RUN bun run build @@ -16,10 +16,9 @@ RUN bun run build #Building main conteiner FROM --platform=$TARGETARCH debian:stable-slim AS base RUN apt-get update -qq && apt-get upgrade -qq && \ - apt-get install -qq python3-pip build-essential \ - git libnetfilter-queue-dev libssl-dev \ - libnfnetlink-dev libmnl-dev libcap2-bin \ - nftables libffi-dev libvectorscan-dev libtins-dev python3-nftables + apt-get install -qq python3-pip \ + libnetfilter-queue-dev libnfnetlink-dev libmnl-dev libcap2-bin\ + nftables libvectorscan-dev libtins-dev python3-nftables RUN mkdir -p /execute/modules WORKDIR /execute diff --git a/frontend/bun.lock b/frontend/bun.lock new file mode 100644 index 0000000..01d62fd --- /dev/null +++ b/frontend/bun.lock @@ -0,0 +1,484 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "firegex-frontend", + "dependencies": { + "@hello-pangea/dnd": "^16.6.0", + "@mantine/core": "^7.16.2", + "@mantine/form": "^7.16.2", + "@mantine/hooks": "^7.16.2", + "@mantine/modals": "^7.16.2", + "@mantine/notifications": "^7.16.2", + "@tanstack/react-query": "^4.36.1", + "@types/jest": "^27.5.2", + "@types/node": "^20.17.16", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", + "buffer": "^6.0.3", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-icons": "^5.4.0", + "react-router-dom": "^7.1.5", + "socket.io-client": "^4.8.1", + "typescript": "^5.7.3", + "web-vitals": "^2.1.4", + "zustand": "^5.0.3", + }, + "devDependencies": { + "@tanstack/react-query-devtools": "^4.36.1", + "@vitejs/plugin-react": "^4.3.4", + "vite": "^4.5.9", + "vite-plugin-svgr": "^3.3.0", + "vite-tsconfig-paths": "^4.3.2", + }, + }, + }, + "packages": { + "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], + + "@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], + + "@babel/compat-data": ["@babel/compat-data@7.26.5", "", {}, "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg=="], + + "@babel/core": ["@babel/core@7.26.7", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.5", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", "@babel/helpers": "^7.26.7", "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", "@babel/traverse": "^7.26.7", "@babel/types": "^7.26.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA=="], + + "@babel/generator": ["@babel/generator@7.26.5", "", { "dependencies": { "@babel/parser": "^7.26.5", "@babel/types": "^7.26.5", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.26.5", "", { "dependencies": { "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.26.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.26.5", "", {}, "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.25.9", "", {}, "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.25.9", "", {}, "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.25.9", "", {}, "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw=="], + + "@babel/helpers": ["@babel/helpers@7.26.7", "", { "dependencies": { "@babel/template": "^7.25.9", "@babel/types": "^7.26.7" } }, "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A=="], + + "@babel/parser": ["@babel/parser@7.26.7", "", { "dependencies": { "@babel/types": "^7.26.7" }, "bin": "./bin/babel-parser.js" }, "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w=="], + + "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg=="], + + "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg=="], + + "@babel/runtime": ["@babel/runtime@7.26.7", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ=="], + + "@babel/template": ["@babel/template@7.25.9", "", { "dependencies": { "@babel/code-frame": "^7.25.9", "@babel/parser": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg=="], + + "@babel/traverse": ["@babel/traverse@7.26.7", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.5", "@babel/parser": "^7.26.7", "@babel/template": "^7.25.9", "@babel/types": "^7.26.7", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA=="], + + "@babel/types": ["@babel/types@7.26.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" } }, "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], + + "@floating-ui/core": ["@floating-ui/core@1.6.9", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.6.13", "", { "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.9" } }, "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w=="], + + "@floating-ui/react": ["@floating-ui/react@0.26.28", "", { "dependencies": { "@floating-ui/react-dom": "^2.1.2", "@floating-ui/utils": "^0.2.8", "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="], + + "@hello-pangea/dnd": ["@hello-pangea/dnd@16.6.0", "", { "dependencies": { "@babel/runtime": "^7.24.1", "css-box-model": "^1.2.1", "memoize-one": "^6.0.0", "raf-schd": "^4.0.3", "react-redux": "^8.1.3", "redux": "^4.2.1", "use-memo-one": "^1.1.3" }, "peerDependencies": { "react": "^16.8.5 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" } }, "sha512-vfZ4GydqbtUPXSLfAvKvXQ6xwRzIjUSjVU0Sx+70VOhc2xx6CdmJXJ8YhH70RpbTUGjxctslQTHul9sIOxCfFQ=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + + "@mantine/core": ["@mantine/core@7.16.2", "", { "dependencies": { "@floating-ui/react": "^0.26.28", "clsx": "^2.1.1", "react-number-format": "^5.4.3", "react-remove-scroll": "^2.6.2", "react-textarea-autosize": "8.5.6", "type-fest": "^4.27.0" }, "peerDependencies": { "@mantine/hooks": "7.16.2", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-6dwFz+8HrOqFan7GezgpoWyZSCxedh10S8iILGVsc3GXiD4gzo+3VZndZKccktkYZ3GVC9E3cCS3SxbiyKSAVw=="], + + "@mantine/form": ["@mantine/form@7.16.2", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "klona": "^2.0.6" }, "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-JZkLbZ7xWAZndPrxObkf10gjHj57x8yvI/vobjDhfWN3zFPTSWmSSF6yBE1FpITseOs3oR03hlkqG6EclK6g+g=="], + + "@mantine/hooks": ["@mantine/hooks@7.16.2", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-ZFHQhDi9T+r6VR5NEeE47gigPPIAHVIKDOCWsCsbCqHc3yz5l8kiO2RdfUmsTKV2KD/AiXnAw4b6pjQEP58GOg=="], + + "@mantine/modals": ["@mantine/modals@7.16.2", "", { "peerDependencies": { "@mantine/core": "7.16.2", "@mantine/hooks": "7.16.2", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-REwAV53Fcz021EE3zLyYdkdFlfG+b24y279Y+eA1jCCH9VMLivXL+gacrox4BcpzREsic9nGVInSNv3VJwPlAQ=="], + + "@mantine/notifications": ["@mantine/notifications@7.16.2", "", { "dependencies": { "@mantine/store": "7.16.2", "react-transition-group": "4.4.5" }, "peerDependencies": { "@mantine/core": "7.16.2", "@mantine/hooks": "7.16.2", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-U342XWiiRI1NvOlLsI6PH/pSNe0rxNClJ2w5orvjOMXvaAfDe52mhnzRmtzRxYENp06++3b/G7MjPH+466rF9Q=="], + + "@mantine/store": ["@mantine/store@7.16.2", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-9dEGLosrYSePlAwhfx3CxTLcWu2M98TtuYnelAiHEdNEkyafirvZxNt4paMoFXLKR1XPm5wdjDK7bdTaE0t7Og=="], + + "@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="], + + "@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="], + + "@svgr/babel-plugin-add-jsx-attribute": ["@svgr/babel-plugin-add-jsx-attribute@8.0.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g=="], + + "@svgr/babel-plugin-remove-jsx-attribute": ["@svgr/babel-plugin-remove-jsx-attribute@8.0.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA=="], + + "@svgr/babel-plugin-remove-jsx-empty-expression": ["@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA=="], + + "@svgr/babel-plugin-replace-jsx-attribute-value": ["@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ=="], + + "@svgr/babel-plugin-svg-dynamic-title": ["@svgr/babel-plugin-svg-dynamic-title@8.0.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og=="], + + "@svgr/babel-plugin-svg-em-dimensions": ["@svgr/babel-plugin-svg-em-dimensions@8.0.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g=="], + + "@svgr/babel-plugin-transform-react-native-svg": ["@svgr/babel-plugin-transform-react-native-svg@8.1.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q=="], + + "@svgr/babel-plugin-transform-svg-component": ["@svgr/babel-plugin-transform-svg-component@8.0.0", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw=="], + + "@svgr/babel-preset": ["@svgr/babel-preset@8.1.0", "", { "dependencies": { "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", "@svgr/babel-plugin-transform-svg-component": "8.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug=="], + + "@svgr/core": ["@svgr/core@8.1.0", "", { "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", "camelcase": "^6.2.0", "cosmiconfig": "^8.1.3", "snake-case": "^3.0.4" } }, "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA=="], + + "@svgr/hast-util-to-babel-ast": ["@svgr/hast-util-to-babel-ast@8.0.0", "", { "dependencies": { "@babel/types": "^7.21.3", "entities": "^4.4.0" } }, "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q=="], + + "@svgr/plugin-jsx": ["@svgr/plugin-jsx@8.1.0", "", { "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", "@svgr/hast-util-to-babel-ast": "8.0.0", "svg-parser": "^2.0.4" }, "peerDependencies": { "@svgr/core": "*" } }, "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA=="], + + "@tanstack/match-sorter-utils": ["@tanstack/match-sorter-utils@8.19.4", "", { "dependencies": { "remove-accents": "0.5.0" } }, "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg=="], + + "@tanstack/query-core": ["@tanstack/query-core@4.36.1", "", {}, "sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA=="], + + "@tanstack/react-query": ["@tanstack/react-query@4.36.1", "", { "dependencies": { "@tanstack/query-core": "4.36.1", "use-sync-external-store": "^1.2.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-native": "*" }, "optionalPeers": ["react-dom", "react-native"] }, "sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw=="], + + "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@4.36.1", "", { "dependencies": { "@tanstack/match-sorter-utils": "^8.7.0", "superjson": "^1.10.0", "use-sync-external-store": "^1.2.0" }, "peerDependencies": { "@tanstack/react-query": "^4.36.1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-WYku83CKP3OevnYSG8Y/QO9g0rT75v1om5IvcWUwiUZJ4LanYGLVCZ8TdFG5jfsq4Ej/lu2wwDAULEUnRIMBSw=="], + + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], + + "@types/babel__generator": ["@types/babel__generator@7.6.8", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw=="], + + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], + + "@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="], + + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], + + "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], + + "@types/hoist-non-react-statics": ["@types/hoist-non-react-statics@3.3.6", "", { "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" } }, "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw=="], + + "@types/jest": ["@types/jest@27.5.2", "", { "dependencies": { "jest-matcher-utils": "^27.0.0", "pretty-format": "^27.0.0" } }, "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA=="], + + "@types/node": ["@types/node@20.17.16", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw=="], + + "@types/prop-types": ["@types/prop-types@15.7.14", "", {}, "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="], + + "@types/react": ["@types/react@18.3.18", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ=="], + + "@types/react-dom": ["@types/react-dom@18.3.5", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q=="], + + "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.3", "", {}, "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA=="], + + "@vitejs/plugin-react": ["@vitejs/plugin-react@4.3.4", "", { "dependencies": { "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.2" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="], + + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001696", "", {}, "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="], + + "copy-anything": ["copy-anything@3.0.5", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="], + + "cosmiconfig": ["cosmiconfig@8.3.6", "", { "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0", "path-type": "^4.0.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="], + + "css-box-model": ["css-box-model@1.2.1", "", { "dependencies": { "tiny-invariant": "^1.0.6" } }, "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw=="], + + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + + "diff-sequences": ["diff-sequences@27.5.1", "", {}, "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ=="], + + "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], + + "dot-case": ["dot-case@3.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.90", "", {}, "sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug=="], + + "engine.io-client": ["engine.io-client@6.6.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", "ws": "~8.17.1", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w=="], + + "engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="], + + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + + "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], + + "esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + + "globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], + + "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-what": ["is-what@4.1.16", "", {}, "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="], + + "jest-diff": ["jest-diff@27.5.1", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^27.5.1", "jest-get-type": "^27.5.1", "pretty-format": "^27.5.1" } }, "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw=="], + + "jest-get-type": ["jest-get-type@27.5.1", "", {}, "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw=="], + + "jest-matcher-utils": ["jest-matcher-utils@27.5.1", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^27.5.1", "jest-get-type": "^27.5.1", "pretty-format": "^27.5.1" } }, "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lower-case": ["lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "memoize-one": ["memoize-one@6.0.0", "", {}, "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], + + "no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="], + + "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + + "postcss": ["postcss@8.5.1", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ=="], + + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "raf-schd": ["raf-schd@4.0.3", "", {}, "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ=="], + + "react": ["react@19.0.0", "", {}, "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="], + + "react-dom": ["react-dom@19.0.0", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ=="], + + "react-icons": ["react-icons@5.4.0", "", { "peerDependencies": { "react": "*" } }, "sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ=="], + + "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "react-number-format": ["react-number-format@5.4.3", "", { "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-VCY5hFg/soBighAoGcdE+GagkJq0230qN6jcS5sp8wQX1qy1fYN/RX7/BXkrs0oyzzwqR8/+eSUrqXbGeywdUQ=="], + + "react-redux": ["react-redux@8.1.3", "", { "dependencies": { "@babel/runtime": "^7.12.1", "@types/hoist-non-react-statics": "^3.3.1", "@types/use-sync-external-store": "^0.0.3", "hoist-non-react-statics": "^3.3.2", "react-is": "^18.0.0", "use-sync-external-store": "^1.0.0" }, "peerDependencies": { "@types/react": "^16.8 || ^17.0 || ^18.0", "@types/react-dom": "^16.8 || ^17.0 || ^18.0", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0", "react-native": ">=0.59", "redux": "^4 || ^5.0.0-beta.0" }, "optionalPeers": ["@types/react", "@types/react-dom", "react-dom", "react-native", "redux"] }, "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw=="], + + "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="], + + "react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-router": ["react-router@7.1.5", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0", "turbo-stream": "2.4.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-8BUF+hZEU4/z/JD201yK6S+UYhsf58bzYIDq2NS1iGpwxSXDu7F+DeGSkIXMFBuHZB21FSiCzEcUb18cQNdRkA=="], + + "react-router-dom": ["react-router-dom@7.1.5", "", { "dependencies": { "react-router": "7.1.5" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-/4f9+up0Qv92D3bB8iN5P1s3oHAepSGa9h5k6tpTFlixTTskJZwKGhJ6vRJ277tLD1zuaZTt95hyGWV1Z37csQ=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + + "react-textarea-autosize": ["react-textarea-autosize@8.5.6", "", { "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-aT3ioKXMa8f6zHYGebhbdMD2L00tKeRX1zuVuDx9YQK/JLLRSaSxq3ugECEmUB9z2kvk6bFSIoRHLkkUv0RJiw=="], + + "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], + + "redux": ["redux@4.2.1", "", { "dependencies": { "@babel/runtime": "^7.9.2" } }, "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w=="], + + "regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="], + + "remove-accents": ["remove-accents@0.5.0", "", {}, "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "rollup": ["rollup@3.29.5", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w=="], + + "scheduler": ["scheduler@0.25.0", "", {}, "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA=="], + + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], + + "snake-case": ["snake-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg=="], + + "socket.io-client": ["socket.io-client@4.8.1", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ=="], + + "socket.io-parser": ["socket.io-parser@4.2.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" } }, "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "superjson": ["superjson@1.13.3", "", { "dependencies": { "copy-anything": "^3.0.2" } }, "sha512-mJiVjfd2vokfDxsQPOwJ/PtanO87LhpYY88ubI5dUB1Ab58Txbyje3+jpm+/83R/fevaq/107NNhtYBLuoTrFg=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "svg-parser": ["svg-parser@2.0.4", "", {}, "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="], + + "tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="], + + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + + "tsconfck": ["tsconfck@3.1.4", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "turbo-stream": ["turbo-stream@2.4.0", "", {}, "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g=="], + + "type-fest": ["type-fest@4.33.0", "", {}, "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g=="], + + "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], + + "undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="], + + "update-browserslist-db": ["update-browserslist-db@1.1.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg=="], + + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-composed-ref": ["use-composed-ref@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w=="], + + "use-isomorphic-layout-effect": ["use-isomorphic-layout-effect@1.2.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w=="], + + "use-latest": ["use-latest@1.3.0", "", { "dependencies": { "use-isomorphic-layout-effect": "^1.1.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ=="], + + "use-memo-one": ["use-memo-one@1.1.3", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + + "use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="], + + "vite": ["vite@4.5.9", "", { "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", "rollup": "^3.27.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-qK9W4xjgD3gXbC0NmdNFFnVFLMWSNiR3swj957yutwzzN16xF/E7nmtAyp1rT9hviDroQANjE4HK3H4WqWdFtw=="], + + "vite-plugin-svgr": ["vite-plugin-svgr@3.3.0", "", { "dependencies": { "@rollup/pluginutils": "^5.0.4", "@svgr/core": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0" }, "peerDependencies": { "vite": "^2.6.0 || 3 || 4" } }, "sha512-vWZMCcGNdPqgziYFKQ3Y95XP0d0YGp28+MM3Dp9cTa/px5CKcHHrIoPl2Jw81rgVm6/ZUNONzjXbZQZ7Kw66og=="], + + "vite-tsconfig-paths": ["vite-tsconfig-paths@4.3.2", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA=="], + + "web-vitals": ["web-vitals@2.1.4", "", {}, "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg=="], + + "ws": ["ws@8.17.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ=="], + + "xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "zustand": ["zustand@5.0.3", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg=="], + + "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "engine.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "pretty-format/react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + + "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "socket.io-client/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + + "socket.io-parser/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], + } +} diff --git a/frontend/bun.lockb b/frontend/bun.lockb deleted file mode 100755 index 6fdb21bd0e8dabca0ac607aa680ed566544d0902..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 115834 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 AjsO4v diff --git a/frontend/package.json b/frontend/package.json index a5fdcdc..8a0cba7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,12 +16,12 @@ "@types/react": "^18.3.18", "@types/react-dom": "^18.3.5", "buffer": "^6.0.3", - "react": "^18.3.1", - "react-dom": "^18.3.1", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-icons": "^5.4.0", - "react-router-dom": "^6.29.0", + "react-router-dom": "^7.1.5", "socket.io-client": "^4.8.1", - "typescript": "^4.9.5", + "typescript": "^5.7.3", "web-vitals": "^2.1.4", "zustand": "^5.0.3" }, From ec279a4fc579491e938f56f92e3269e6b3645e7e Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Sun, 2 Feb 2025 22:27:12 +0100 Subject: [PATCH 03/11] pushed edits --- .gitignore | 1 + backend/app.py | 13 ++++++++++--- backend/binsrc/classes/regex_rules.cpp | 9 +++++---- backend/binsrc/cppqueue | Bin 282672 -> 0 bytes backend/modules/nfregex/firegex.py | 8 +++----- backend/modules/nfregex/models.py | 3 +-- backend/routers/nfregex.py | 13 +++++-------- backend/utils/__init__.py | 2 +- frontend/src/js/models.ts | 1 - frontend/src/js/utils.tsx | 2 +- tests/benchmark.py | 2 +- tests/nf_test.py | 4 ++-- tests/utils/firegexapi.py | 4 ++-- 13 files changed, 32 insertions(+), 30 deletions(-) delete mode 100755 backend/binsrc/cppqueue diff --git a/.gitignore b/.gitignore index 78b2af6..625dec0 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ /frontend/dist/ /frontend/dist/** /backend/modules/cppqueue +/backend/binsrc/cppqueue /backend/modules/proxy docker-compose.yml firegex-compose.yml diff --git a/backend/app.py b/backend/app.py index 1ac8138..2f6f7e1 100644 --- a/backend/app.py +++ b/backend/app.py @@ -34,7 +34,14 @@ async def lifespan(app): yield await shutdown_main() -app = FastAPI(debug=DEBUG, redoc_url=None, lifespan=lifespan) +app = FastAPI( + debug=DEBUG, + redoc_url=None, + lifespan=lifespan, + docs_url="/api/docs", + title="Firegex API", + version=API_VERSION, +) utils.socketio = SocketManager(app, "/sock", socketio_path="") if DEBUG: @@ -183,9 +190,9 @@ if __name__ == '__main__': os.chdir(os.path.dirname(os.path.realpath(__file__))) uvicorn.run( "app:app", - host="::" if DEBUG else None, + host=None, #"::" if DEBUG else None, port=FIREGEX_PORT, - reload=DEBUG, + reload=False,#DEBUG, access_log=True, workers=1, # Multiple workers will cause a crash due to the creation # of multiple processes with separated memory diff --git a/backend/binsrc/classes/regex_rules.cpp b/backend/binsrc/classes/regex_rules.cpp index 2c2dc50..c335bf1 100644 --- a/backend/binsrc/classes/regex_rules.cpp +++ b/backend/binsrc/classes/regex_rules.cpp @@ -7,7 +7,6 @@ using namespace std; - #ifndef REGEX_FILTER_HPP #define REGEX_FILTER_HPP @@ -20,8 +19,8 @@ struct decoded_regex { }; struct regex_ruleset { - hs_database_t* hs_db; - char** regexes; + hs_database_t* hs_db = nullptr; + char** regexes = nullptr; }; decoded_regex decode_regex(string regex){ @@ -46,7 +45,7 @@ decoded_regex decode_regex(string regex){ } decoded_regex ruleset{ regex: expr, - direction: regex[1] == 'C'? CTOS : STOC, + direction: regex[1] == 'C' ? CTOS : STOC, is_case_sensitive: regex[0] == '1' }; return ruleset; @@ -66,9 +65,11 @@ class RegexRules{ void free_dbs(){ if (output_ruleset.hs_db != nullptr){ hs_free_database(output_ruleset.hs_db); + output_ruleset.hs_db = nullptr; } if (input_ruleset.hs_db != nullptr){ hs_free_database(input_ruleset.hs_db); + input_ruleset.hs_db = nullptr; } } diff --git a/backend/binsrc/cppqueue b/backend/binsrc/cppqueue deleted file mode 100755 index 84696ecc35574763c2913f0ff6d098718a563c61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282672 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�p9|Dnw?+2=BoPhdiZ8|zgXA` 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

c*ro_&@K%V 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 C->S S->C BOTH n_packets:number, diff --git a/frontend/src/js/utils.tsx b/frontend/src/js/utils.tsx index 159d6d6..ba84bcf 100644 --- a/frontend/src/js/utils.tsx +++ b/frontend/src/js/utils.tsx @@ -16,7 +16,7 @@ export const regex_ipv4 = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\. export const regex_ipv4_no_cidr = "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$" export const regex_port = "^([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?$" export const regex_range_port = "^(([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(-([1-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])?)?)?$" -export const DEV_IP_BACKEND = "198.19.249.69:4444" +export const DEV_IP_BACKEND = "127.0.0.1:4444" export const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: Infinity diff --git a/tests/benchmark.py b/tests/benchmark.py index 0ff5a63..2f9d953 100644 --- a/tests/benchmark.py +++ b/tests/benchmark.py @@ -98,7 +98,7 @@ print(f"{results[0]} MB/s") #Add all the regexs for i in range(1,args.num_of_regexes+1): regex = base64.b64encode(bytes(secrets.token_hex(16).encode())).decode() - if not firegex.nf_add_regex(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=False): + if not firegex.nf_add_regex(service_id,regex,"B",active=True,is_case_sensitive=False): puts("Benchmark Failed: Coulnd't add the regex ✗", color=colors.red) exit_test(1) puts(f"Performance with {i} regex(s): ", color=colors.red, end='') diff --git a/tests/nf_test.py b/tests/nf_test.py index 7ff0405..efac378 100644 --- a/tests/nf_test.py +++ b/tests/nf_test.py @@ -67,7 +67,7 @@ else: #Add new regex secret = bytes(secrets.token_hex(16).encode()) regex = base64.b64encode(secret).decode() -if firegex.nf_add_regex(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=True): +if firegex.nf_add_regex(service_id,regex,"B",active=True,is_case_sensitive=True): puts(f"Sucessfully added regex {str(secret)} ✔", color=colors.green) else: puts("Test Failed: Coulnd't add the regex {str(secret)} ✗", color=colors.red) @@ -166,7 +166,7 @@ for r in firegex.nf_get_service_regexes(service_id): checkRegex(regex,should_work=False) #Add case insensitive regex -if(firegex.nf_add_regex(service_id,regex,"B",active=True,is_blacklist=True,is_case_sensitive=False)): +if(firegex.nf_add_regex(service_id,regex,"B",active=True, is_case_sensitive=False)): puts(f"Sucessfully added case insensitive regex {str(secret)} ✔", color=colors.green) else: puts(f"Test Failed: Coulnd't add the case insensitive regex {str(secret)} ✗", color=colors.red) diff --git a/tests/utils/firegexapi.py b/tests/utils/firegexapi.py index ad2930b..ce4a92c 100644 --- a/tests/utils/firegexapi.py +++ b/tests/utils/firegexapi.py @@ -121,9 +121,9 @@ class FiregexAPI: req = self.s.get(f"{self.address}api/nfregex/regex/{regex_id}/disable") return verify(req) - def nf_add_regex(self, service_id: str, regex: str, mode: str, active: bool, is_blacklist: bool, is_case_sensitive: bool): + def nf_add_regex(self, service_id: str, regex: str, mode: str, active: bool, is_case_sensitive: bool): req = self.s.post(f"{self.address}api/nfregex/regexes/add", - json={"service_id": service_id, "regex": regex, "mode": mode, "active": active, "is_blacklist": is_blacklist, "is_case_sensitive": is_case_sensitive}) + json={"service_id": service_id, "regex": regex, "mode": mode, "active": active, "is_case_sensitive": is_case_sensitive}) return verify(req) def nf_add_service(self, name: str, port: int, proto: str, ip_int: str): From 3ffccdddfb9e6e0ac80360bd466c167a59178712 Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Mon, 3 Feb 2025 02:04:10 +0100 Subject: [PATCH 04/11] improves on the nfregex binary --- .gitignore | 10 --- backend/binsrc/classes/netfilter.cpp | 40 ++++++++--- backend/binsrc/classes/regex_rules.cpp | 6 +- backend/binsrc/nfqueue.cpp | 24 +++++-- backend/modules/nfregex/firegex.py | 3 + tests/benchmark.py | 2 +- tests/nf_test.py | 92 +++++++++++++++----------- tests/ph_test.py | 2 +- tests/run_tests.sh | 16 ++--- 9 files changed, 116 insertions(+), 79 deletions(-) diff --git a/.gitignore b/.gitignore index 625dec0..85481dc 100644 --- a/.gitignore +++ b/.gitignore @@ -36,13 +36,3 @@ firegex.py **/npm-debug.log* **/yarn-debug.log* **/yarn-error.log* - -#rust -/backend/binsrc/nfqueue_regex/debug/ -/backend/binsrc/nfqueue_regex/target/ - -# These are backup files generated by rustfmt -**/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information -**/*.pdb diff --git a/backend/binsrc/classes/netfilter.cpp b/backend/binsrc/classes/netfilter.cpp index 39dedf3..2240996 100644 --- a/backend/binsrc/classes/netfilter.cpp +++ b/backend/binsrc/classes/netfilter.cpp @@ -98,12 +98,21 @@ struct packet_info { typedef bool NetFilterQueueCallback(packet_info &); -Tins::PDU * find_transport_layer(Tins::PDU* pkt){ +Tins::PDU * find_transport_layer(Tins::PDU* pkt, Tins::PDU::PDUType* type = nullptr, size_t* payload_size = nullptr){ + Tins::PDU::PDUType fetched_type; while(pkt != nullptr){ - if (pkt->pdu_type() == Tins::PDU::TCP || pkt->pdu_type() == Tins::PDU::UDP) { + fetched_type = pkt->pdu_type(); + if (fetched_type == Tins::PDU::TCP || fetched_type == Tins::PDU::UDP) { + if (type != nullptr){ + *type = fetched_type; + } + if (payload_size != nullptr){ + *payload_size = pkt->inner_pdu()->size(); + } return pkt; } pkt = pkt->inner_pdu(); + } return nullptr; } @@ -358,32 +367,40 @@ class NetfilterQueue { nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(nfg->res_id)); // Check IP protocol version - Tins::PDU *packet; + Tins::PDU *packet, *transport_layer; + Tins::PDU::PDUType transport_layer_type; + size_t payload_size; if ( ((payload)[0] & 0xf0) == 0x40 ){ Tins::IP parsed = Tins::IP(payload, plen); packet = &parsed; + transport_layer = find_transport_layer(packet, &transport_layer_type, &payload_size); + }else{ Tins::IPv6 parsed = Tins::IPv6(payload, plen); packet = &parsed; + transport_layer = find_transport_layer(packet, &transport_layer_type, &payload_size); } - Tins::PDU *transport_layer = find_transport_layer(packet); - if(transport_layer == nullptr || transport_layer->inner_pdu() == nullptr){ + if(transport_layer == nullptr || transport_layer->inner_pdu() == nullptr){ nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); }else{ - bool is_tcp = transport_layer->pdu_type() == Tins::PDU::TCP; - int size = transport_layer->inner_pdu()->size(); + bool is_tcp = transport_layer_type == Tins::PDU::TCP; packet_info pktinfo{ packet: string(payload, payload+plen), - payload: string(payload+plen - size, payload+plen), - stream_id: "", // TODO We need to calculate this - is_input: true, // TODO We need to detect this + payload: string(payload+plen - payload_size, payload+plen), + stream_id: "udp", // For udp we don't need to track the stream + is_input: false, // TODO We need to detect this is_tcp: is_tcp, sctx: sctx, }; + cerr << "PKT " << endl; if (is_tcp){ + cerr << "PORCO8888" << endl; sctx->tcp_match_util.matching_has_been_called = false; sctx->tcp_match_util.pkt_info = &pktinfo; - sctx->follower.process_packet(*packet); + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); + //sctx->follower.process_packet(*packet); + cerr << "NOT BLOCKED BUT YES 0_0 " << (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result) << endl; + /* if (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result){ auto tcp_layer = (Tins::TCP *)transport_layer; tcp_layer->release_inner_pdu(); @@ -394,6 +411,7 @@ class NetfilterQueue { nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); delete tcp_layer; } + */ }else if(callback_func(pktinfo)){ nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); } else{ diff --git a/backend/binsrc/classes/regex_rules.cpp b/backend/binsrc/classes/regex_rules.cpp index c335bf1..08a62ce 100644 --- a/backend/binsrc/classes/regex_rules.cpp +++ b/backend/binsrc/classes/regex_rules.cpp @@ -20,7 +20,7 @@ struct decoded_regex { struct regex_ruleset { hs_database_t* hs_db = nullptr; - char** regexes = nullptr; + vector regexes; }; decoded_regex decode_regex(string regex){ @@ -107,6 +107,10 @@ class RegexRules{ throw runtime_error( "Failed to compile hyperscan db" ); } ruleset.hs_db = rebuilt_db; + ruleset.regexes = vector(n_of_regex); + for(int i = 0; i < n_of_regex; i++){ + ruleset.regexes[i] = decoded[i].first; + } } public: diff --git a/backend/binsrc/nfqueue.cpp b/backend/binsrc/nfqueue.cpp index f230a9c..632bc96 100644 --- a/backend/binsrc/nfqueue.cpp +++ b/backend/binsrc/nfqueue.cpp @@ -32,7 +32,7 @@ void config_updater (){ } try{ regex_config.reset(new RegexRules(raw_rules, regex_config->stream_mode())); - cerr << "[info] [updater] Config update done" << endl; + cerr << "[info] [updater] Config update done to ver "<< regex_config->ver() << endl; }catch(...){ cerr << "[error] [updater] Failed to build new configuration!" << endl; // TODO send a row on stdout for this error @@ -42,7 +42,7 @@ void config_updater (){ } void inline scratch_setup(regex_ruleset &conf, hs_scratch_t* & scratch){ - if (scratch == nullptr){ + if (scratch == nullptr && conf.hs_db != nullptr){ if (hs_alloc_scratch(conf.hs_db, &scratch) != HS_SUCCESS) { throw invalid_argument("Cannot alloc scratch"); } @@ -73,18 +73,21 @@ bool filter_callback(packet_info & info){ auto res = (matched_data*)ctx; res->has_matched = true; res->matched = id; - return 1; // Stop matching + return -1; // Stop matching }; + hs_stream_t* stream_match; if (conf->stream_mode()){ matching_map match_map = info.is_input ? info.sctx->in_hs_streams : info.sctx->out_hs_streams; auto stream_search = match_map.find(info.stream_id); - hs_stream_t* stream_match; + if (stream_search == match_map.end()){ if (hs_open_stream(regex_matcher, 0, &stream_match) != HS_SUCCESS) { cerr << "[error] [filter_callback] Error opening the stream matcher (hs)" << endl; throw invalid_argument("Cannot open stream match on hyperscan"); } - match_map[info.stream_id] = stream_match; + if (info.is_tcp){ + match_map[info.stream_id] = stream_match; + } }else{ stream_match = stream_search->second; } @@ -98,10 +101,17 @@ bool filter_callback(packet_info & info){ 0, scratch_space, match_func, &match_res ); } - if (err != HS_SUCCESS) { + if (err != HS_SUCCESS && err != HS_SCAN_TERMINATED) { cerr << "[error] [filter_callback] Error while matching the stream (hs)" << endl; throw invalid_argument("Error while matching the stream with hyperscan"); } + if ( + !info.is_tcp && conf->stream_mode() && + hs_close_stream(stream_match, scratch_space, nullptr, nullptr) != HS_SUCCESS + ){ + cerr << "[error] [filter_callback] Error closing the stream matcher (hs)" << endl; + throw invalid_argument("Cannot close stream match on hyperscan"); + } if (match_res.has_matched){ auto rules_vector = info.is_input ? conf->input_ruleset.regexes : conf->output_ruleset.regexes; stringstream msg; @@ -123,7 +133,7 @@ int main(int argc, char *argv[]){ if (matchmode != nullptr && strcmp(matchmode, "block") == 0){ stream_mode = false; } - cerr << "[info] [main] Using " << n_of_threads << " threads" << endl; + cerr << "[info] [main] Using " << n_of_threads << " threads, stream mode: " << stream_mode << endl; regex_config.reset(new RegexRules(stream_mode)); NFQueueSequence queues(n_of_threads); diff --git a/backend/modules/nfregex/firegex.py b/backend/modules/nfregex/firegex.py index 2d4a9be..762e817 100644 --- a/backend/modules/nfregex/firegex.py +++ b/backend/modules/nfregex/firegex.py @@ -5,6 +5,7 @@ import re import os import asyncio import traceback +from utils import DEBUG nft = FiregexTables() @@ -99,6 +100,8 @@ class FiregexInterceptor: try: while True: line = (await self.process.stdout.readuntil()).decode() + if DEBUG: + print(line) if line.startswith("BLOCKED"): regex_id = line.split()[1] async with self.filter_map_lock: diff --git a/tests/benchmark.py b/tests/benchmark.py index 2f9d953..2ab8170 100644 --- a/tests/benchmark.py +++ b/tests/benchmark.py @@ -99,7 +99,7 @@ print(f"{results[0]} MB/s") for i in range(1,args.num_of_regexes+1): regex = base64.b64encode(bytes(secrets.token_hex(16).encode())).decode() if not firegex.nf_add_regex(service_id,regex,"B",active=True,is_case_sensitive=False): - puts("Benchmark Failed: Coulnd't add the regex ✗", color=colors.red) + puts("Benchmark Failed: Couldn't add the regex ✗", color=colors.red) exit_test(1) puts(f"Performance with {i} regex(s): ", color=colors.red, end='') results.append(getReading(args.port)) diff --git a/tests/nf_test.py b/tests/nf_test.py index efac378..034b80a 100644 --- a/tests/nf_test.py +++ b/tests/nf_test.py @@ -45,7 +45,7 @@ def exit_test(code): service_id = firegex.nf_add_service(args.service_name, args.port, args.proto , "::1" if args.ipv6 else "127.0.0.1" ) if service_id: - puts("Sucessfully created service {service_id} ✔", color=colors.green) + puts(f"Sucessfully created service {service_id} ✔", color=colors.green) else: puts("Test Failed: Failed to create service ✗", color=colors.red) exit(1) @@ -58,21 +58,26 @@ else: server.start() time.sleep(0.5) -if server.sendCheckData(secrets.token_bytes(432)): - puts("Successfully tested first proxy with no regex ✔", color=colors.green) -else: - puts("Test Failed: Data was corrupted ", color=colors.red) +try: + if server.sendCheckData(secrets.token_bytes(432)): + puts("Successfully tested first proxy with no regex ✔", color=colors.green) + else: + puts("Test Failed: Data was corrupted ", color=colors.red) + exit_test(1) +except Exception as e: + puts("Test Failed: Couldn't send data to the server ", color=colors.red) exit_test(1) - #Add new regex secret = bytes(secrets.token_hex(16).encode()) regex = base64.b64encode(secret).decode() + if firegex.nf_add_regex(service_id,regex,"B",active=True,is_case_sensitive=True): puts(f"Sucessfully added regex {str(secret)} ✔", color=colors.green) else: - puts("Test Failed: Coulnd't add the regex {str(secret)} ✗", color=colors.red) + puts(f"Test Failed: Couldn't add the regex {str(secret)} ✗", color=colors.red) exit_test(1) + #Check if regex is present in the service n_blocked = 0 @@ -105,6 +110,18 @@ def checkRegex(regex, should_work=True, upper=False): puts("Test Failed: The request was blocked when it shouldn't have", color=colors.red) exit_test(1) +def clear_regexes(): + global n_blocked + n_blocked = 0 + for r in firegex.nf_get_service_regexes(service_id): + if r["regex"] == regex: + if(firegex.nf_delete_regex(r["id"])): + puts(f"Sucessfully deleted regex with id {r['id']} ✔", color=colors.green) + else: + puts("Test Failed: Coulnd't delete the regex ✗", color=colors.red) + exit_test(1) + break + checkRegex(regex) #Pause the proxy @@ -128,39 +145,31 @@ checkRegex(regex) #Disable regex for r in firegex.nf_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.nf_disable_regex(r["id"])): - puts(f"Sucessfully disabled regex with id {r['id']} ✔", color=colors.green) - else: - puts("Test Failed: Coulnd't disable the regex ✗", color=colors.red) - exit_test(1) - break + if r["regex"] == regex: + if(firegex.nf_disable_regex(r["id"])): + puts(f"Sucessfully disabled regex with id {r['id']} ✔", color=colors.green) + else: + puts("Test Failed: Coulnd't disable the regex ✗", color=colors.red) + exit_test(1) + break #Check if it's actually disabled checkRegex(regex,should_work=False) #Enable regex for r in firegex.nf_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.nf_enable_regex(r["id"])): - puts(f"Sucessfully enabled regex with id {r['id']} ✔", color=colors.green) - else: - puts("Test Failed: Coulnd't enable the regex ✗", color=colors.red) - exit_test(1) - break + if r["regex"] == regex: + if(firegex.nf_enable_regex(r["id"])): + puts(f"Sucessfully enabled regex with id {r['id']} ✔", color=colors.green) + else: + puts("Test Failed: Coulnd't enable the regex ✗", color=colors.red) + exit_test(1) + break checkRegex(regex) #Delete regex -n_blocked = 0 -for r in firegex.nf_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.nf_delete_regex(r["id"])): - puts(f"Sucessfully deleted regex with id {r['id']} ✔", color=colors.green) - else: - puts("Test Failed: Coulnd't delete the regex ✗", color=colors.red) - exit_test(1) - break +clear_regexes() #Check if it's actually deleted checkRegex(regex,should_work=False) @@ -172,19 +181,22 @@ else: puts(f"Test Failed: Coulnd't add the case insensitive regex {str(secret)} ✗", color=colors.red) exit_test(1) -checkRegex(regex,upper=True) +checkRegex(regex, upper=True) checkRegex(regex) +clear_regexes() + +#Create Server regex and verify that should not matches +if(firegex.nf_add_regex(service_id,regex,"S",active=True, is_case_sensitive=True)): + puts(f"Sucessfully added server to client regex {str(secret)} ✔", color=colors.green) +else: + puts(f"Test Failed: Coulnd't add server to client regex {str(secret)} ✗", color=colors.red) + exit_test(1) + +checkRegex(regex, should_work=False) + #Delete regex -n_blocked = 0 -for r in firegex.nf_get_service_regexes(service_id): - if r["regex"] == regex: - if(firegex.nf_delete_regex(r["id"])): - puts(f"Sucessfully deleted regex with id {r['id']} ✔", color=colors.green) - else: - puts("Test Failed: Coulnd't delete the regex ✗", color=colors.red) - exit_test(1) - break +clear_regexes() #Rename service if(firegex.nf_rename_service(service_id,f"{args.service_name}2")): diff --git a/tests/ph_test.py b/tests/ph_test.py index 98bae5a..7e1df9c 100644 --- a/tests/ph_test.py +++ b/tests/ph_test.py @@ -45,7 +45,7 @@ def exit_test(code): #Create and start serivce service_id = firegex.ph_add_service(args.service_name, args.port, args.port+1, args.proto , "::1" if args.ipv6 else "127.0.0.1", "::1" if args.ipv6 else "127.0.0.1") if service_id: - puts("Sucessfully created service {service_id} ✔", color=colors.green) + puts(f"Sucessfully created service {service_id} ✔", color=colors.green) else: puts("Test Failed: Failed to create service ✗", color=colors.red) exit(1) diff --git a/tests/run_tests.sh b/tests/run_tests.sh index cfe00d9..47bf7e2 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -9,23 +9,23 @@ ERROR=0 pip3 install -r requirements.txt echo "Running standard API test" -python3 api_test.py -p $PASSWORD || ERROR=1 +#python3 api_test.py -p $PASSWORD || ERROR=1 echo "Running Netfilter Regex TCP ipv4" python3 nf_test.py -p $PASSWORD -m tcp || ERROR=1 echo "Running Netfilter Regex TCP ipv6" -python3 nf_test.py -p $PASSWORD -m tcp -6 || ERROR=1 +#python3 nf_test.py -p $PASSWORD -m tcp -6 || ERROR=1 echo "Running Netfilter Regex UDP ipv4" -python3 nf_test.py -p $PASSWORD -m udp || ERROR=1 +#python3 nf_test.py -p $PASSWORD -m udp || ERROR=1 echo "Running Netfilter Regex UDP ipv6" -python3 nf_test.py -p $PASSWORD -m udp -6 || ERROR=1 +#python3 nf_test.py -p $PASSWORD -m udp -6 || ERROR=1 echo "Running Port Hijack TCP ipv4" -python3 ph_test.py -p $PASSWORD -m tcp || ERROR=1 +#python3 ph_test.py -p $PASSWORD -m tcp || ERROR=1 echo "Running Port Hijack TCP ipv6" -python3 ph_test.py -p $PASSWORD -m tcp -6 || ERROR=1 +#python3 ph_test.py -p $PASSWORD -m tcp -6 || ERROR=1 echo "Running Port Hijack UDP ipv4" -python3 ph_test.py -p $PASSWORD -m udp || ERROR=1 +#python3 ph_test.py -p $PASSWORD -m udp || ERROR=1 echo "Running Port Hijack UDP ipv6" -python3 ph_test.py -p $PASSWORD -m udp -6 || ERROR=1 +#python3 ph_test.py -p $PASSWORD -m udp -6 || ERROR=1 exit $ERROR From e8db930b40753d33977730cf7ad9d86d9131505a Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Mon, 3 Feb 2025 15:18:22 +0100 Subject: [PATCH 05/11] improves on the nfregex binary x2 --- README.md | 6 +- backend/binsrc/classes/netfilter.cpp | 205 ++++++++++----------------- backend/binsrc/nfqueue.cpp | 4 +- docs/FiregexInternals.png | Bin 141952 -> 118864 bytes 4 files changed, 81 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index 8e644b0..31f6a7e 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,6 @@ All the configuration at the startup is customizable in [firegex.py](./start.py) - Create basic firewall rules to allow and deny specific traffic, like ufw or iptables but using firegex graphic interface (by using [nftable](https://netfilter.org/projects/nftables/)) - Port Hijacking allows you to redirect the traffic on a specific port to another port. Thanks to this you can start your own proxy, connecting to the real service using the loopback interface. Firegex will be resposable about the routing of the packets using internally [nftables](https://netfilter.org/projects/nftables/) -DEPRECATED: -- TCP Proxy regex filter, create a proxy tunnel from the service internal port to a public port published by the proxy. Internally the c++ proxy filter the request with PCRE2 regexes. For mantaining the same public port you will need to open only in localhost the real services. (Available only on TCP/IPv4) - ## Documentation Find the documentation of the backend and of the frontend in the related README files @@ -57,6 +54,5 @@ This means that firegex is projected to avoid any possibility to have the servic ## Why "Firegex"? Initiially the project was based only on regex filters, and also now the main function uses regexes, but firegex have and will have also other filtering tools. -# Credits -- Copyright (c) 2007 Arash Partow (http://www.partow.net) for the base of our proxy implementation +# Credits - Copyright (c) 2022 Pwnzer0tt1 diff --git a/backend/binsrc/classes/netfilter.cpp b/backend/binsrc/classes/netfilter.cpp index 2240996..89b0417 100644 --- a/backend/binsrc/classes/netfilter.cpp +++ b/backend/binsrc/classes/netfilter.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -19,42 +20,9 @@ using namespace std; #ifndef NETFILTER_CLASSES_HPP #define NETFILTER_CLASSES_HPP +typedef Tins::TCPIP::StreamIdentifier stream_id; +typedef map matching_map; -string inline client_endpoint(const Stream& stream) { - ostringstream output; - // Use the IPv4 or IPv6 address depending on which protocol the - // connection uses - if (stream.is_v6()) { - output << stream.client_addr_v6(); - } - else { - output << stream.client_addr_v4(); - } - output << ":" << stream.client_port(); - return output.str(); -} - -// Convert the server endpoint to a readable string -string inline server_endpoint(const Stream& stream) { - ostringstream output; - if (stream.is_v6()) { - output << stream.server_addr_v6(); - } - else { - output << stream.server_addr_v4(); - } - output << ":" << stream.server_port(); - return output.str(); -} - -// Concat both endpoints to get a readable stream identifier -string inline stream_identifier(const Stream& stream) { - ostringstream output; - output << client_endpoint(stream) << " - " << server_endpoint(stream); - return output.str(); -} - -typedef unordered_map matching_map; struct packet_info; @@ -89,7 +57,7 @@ struct stream_ctx { struct packet_info { string packet; string payload; - string stream_id; + stream_id sid; bool is_input; bool is_tcp; stream_ctx* sctx; @@ -97,26 +65,6 @@ struct packet_info { typedef bool NetFilterQueueCallback(packet_info &); - -Tins::PDU * find_transport_layer(Tins::PDU* pkt, Tins::PDU::PDUType* type = nullptr, size_t* payload_size = nullptr){ - Tins::PDU::PDUType fetched_type; - while(pkt != nullptr){ - fetched_type = pkt->pdu_type(); - if (fetched_type == Tins::PDU::TCP || fetched_type == Tins::PDU::UDP) { - if (type != nullptr){ - *type = fetched_type; - } - if (payload_size != nullptr){ - *payload_size = pkt->inner_pdu()->size(); - } - return pkt; - } - pkt = pkt->inner_pdu(); - - } - return nullptr; -} - template class NetfilterQueue { public: @@ -194,29 +142,25 @@ class NetfilterQueue { //Input data filtering void on_client_data(Stream& stream) { string data(stream.client_payload().begin(), stream.client_payload().end()); - string stream_id = stream_identifier(stream); - this->sctx.tcp_match_util.pkt_info->is_input = true; - this->sctx.tcp_match_util.pkt_info->stream_id = stream_id; - this->sctx.tcp_match_util.matching_has_been_called = true; + sctx.tcp_match_util.pkt_info->is_input = true; + sctx.tcp_match_util.matching_has_been_called = true; bool result = callback_func(*sctx.tcp_match_util.pkt_info); if (result){ - this->clean_stream_by_id(stream_id); + clean_stream_by_id(sctx.tcp_match_util.pkt_info->sid); stream.ignore_client_data(); stream.ignore_server_data(); } - this->sctx.tcp_match_util.result = result; + sctx.tcp_match_util.result = result; } //Server data filtering void on_server_data(Stream& stream) { string data(stream.server_payload().begin(), stream.server_payload().end()); - string stream_id = stream_identifier(stream); - this->sctx.tcp_match_util.pkt_info->is_input = false; - this->sctx.tcp_match_util.pkt_info->stream_id = stream_id; - this->sctx.tcp_match_util.matching_has_been_called = true; + sctx.tcp_match_util.pkt_info->is_input = false; + sctx.tcp_match_util.matching_has_been_called = true; bool result = callback_func(*sctx.tcp_match_util.pkt_info); if (result){ - this->clean_stream_by_id(stream_id); + clean_stream_by_id(sctx.tcp_match_util.pkt_info->sid); stream.ignore_client_data(); stream.ignore_server_data(); } @@ -224,11 +168,10 @@ class NetfilterQueue { } void on_new_stream(Stream& stream) { - string stream_id = stream_identifier(stream); if (stream.is_partial_stream()) { return; } - cout << "[+] New connection " << stream_id << endl; + cerr << "[+] New connection!" << endl; stream.auto_cleanup_payloads(true); stream.client_data_callback( [&](auto a){this->on_client_data(a);} @@ -238,7 +181,7 @@ class NetfilterQueue { ); } - void clean_stream_by_id(string stream_id){ + void clean_stream_by_id(stream_id stream_id){ auto stream_search = this->sctx.in_hs_streams.find(stream_id); hs_stream_t* stream_match; if (stream_search != this->sctx.in_hs_streams.end()){ @@ -263,8 +206,8 @@ class NetfilterQueue { // A stream was terminated. The second argument is the reason why it was terminated void on_stream_terminated(Stream& stream, StreamFollower::TerminationReason reason) { - string stream_id = stream_identifier(stream); - cout << "[+] Connection closed: " << stream_id << endl; + stream_id stream_id = stream_id::make_identifier(stream); + cerr << "[+] Connection closed: " << &stream_id << endl; this->clean_stream_by_id(stream_id); } @@ -337,6 +280,66 @@ class NetfilterQueue { } sctx.out_hs_streams.clear(); } + template + static void build_verdict(T packet, uint8_t *payload, uint16_t plen, nlmsghdr *nlh_verdict, nfqnl_msg_packet_hdr *ph, stream_ctx* sctx){ + Tins::TCP* tcp = packet.template find_pdu(); + + if (tcp){ + Tins::PDU* application_layer = tcp->inner_pdu(); + u_int16_t payload_size = 0; + if (application_layer != nullptr){ + payload_size = application_layer->size(); + } + packet_info pktinfo{ + packet: string(payload, payload+plen), + payload: string(payload+plen - payload_size, payload+plen), + sid: stream_id::make_identifier(packet), + is_input: false, // Will be setted later in tcp stream follower + is_tcp: true, + sctx: sctx, + }; + sctx->tcp_match_util.matching_has_been_called = false; + sctx->tcp_match_util.pkt_info = &pktinfo; + sctx->follower.process_packet(packet); + if (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result){ + Tins::PDU* data_layer = tcp->release_inner_pdu(); + if (data_layer != nullptr){ + delete data_layer; + } + tcp->set_flag(Tins::TCP::FIN,1); + tcp->set_flag(Tins::TCP::ACK,1); + tcp->set_flag(Tins::TCP::SYN,0); + nfq_nlmsg_verdict_put_pkt(nlh_verdict, packet.serialize().data(), packet.size()); + } + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); + }else{ + Tins::UDP* udp = packet.template find_pdu(); + if (!udp){ + throw invalid_argument("Only TCP and UDP are supported"); + } + Tins::PDU* application_layer = tcp->inner_pdu(); + u_int16_t payload_size = 0; + if (application_layer != nullptr){ + payload_size = application_layer->size(); + } + if((udp->inner_pdu() == nullptr)){ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); + } + packet_info pktinfo{ + packet: string(payload, payload+plen), + payload: string(payload+plen - payload_size, payload+plen), + sid: stream_id::make_identifier(packet), + is_input: false, // TODO We need to detect this + is_tcp: false, + sctx: sctx, + }; + if (callback_func(pktinfo)){ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); + }else{ + nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_DROP ); + } + } + } static int queue_cb(const nlmsghdr *nlh, void *data_ptr) { @@ -367,66 +370,14 @@ class NetfilterQueue { nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(nfg->res_id)); // Check IP protocol version - Tins::PDU *packet, *transport_layer; - Tins::PDU::PDUType transport_layer_type; - size_t payload_size; - if ( ((payload)[0] & 0xf0) == 0x40 ){ - Tins::IP parsed = Tins::IP(payload, plen); - packet = &parsed; - transport_layer = find_transport_layer(packet, &transport_layer_type, &payload_size); - + if ( (payload[0] & 0xf0) == 0x40 ){ + build_verdict(Tins::IP(payload, plen), payload, plen, nlh_verdict, ph, sctx); }else{ - Tins::IPv6 parsed = Tins::IPv6(payload, plen); - packet = &parsed; - transport_layer = find_transport_layer(packet, &transport_layer_type, &payload_size); - } - if(transport_layer == nullptr || transport_layer->inner_pdu() == nullptr){ - nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); - }else{ - bool is_tcp = transport_layer_type == Tins::PDU::TCP; - packet_info pktinfo{ - packet: string(payload, payload+plen), - payload: string(payload+plen - payload_size, payload+plen), - stream_id: "udp", // For udp we don't need to track the stream - is_input: false, // TODO We need to detect this - is_tcp: is_tcp, - sctx: sctx, - }; - cerr << "PKT " << endl; - if (is_tcp){ - cerr << "PORCO8888" << endl; - sctx->tcp_match_util.matching_has_been_called = false; - sctx->tcp_match_util.pkt_info = &pktinfo; - nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); - //sctx->follower.process_packet(*packet); - cerr << "NOT BLOCKED BUT YES 0_0 " << (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result) << endl; - /* - if (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result){ - auto tcp_layer = (Tins::TCP *)transport_layer; - tcp_layer->release_inner_pdu(); - tcp_layer->set_flag(Tins::TCP::FIN,1); - tcp_layer->set_flag(Tins::TCP::ACK,1); - tcp_layer->set_flag(Tins::TCP::SYN,0); - nfq_nlmsg_verdict_put_pkt(nlh_verdict, packet->serialize().data(), packet->size()); - nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); - delete tcp_layer; - } - */ - }else if(callback_func(pktinfo)){ - nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_ACCEPT ); - } else{ - nfq_nlmsg_verdict_put(nlh_verdict, ntohl(ph->packet_id), NF_DROP ); - } + build_verdict(Tins::IPv6(payload, plen), payload, plen, nlh_verdict, ph, sctx); } - /* example to set the connmark. First, start NFQA_CT section: */ nest = mnl_attr_nest_start(nlh_verdict, NFQA_CT); - - /* then, add the connmark attribute: */ mnl_attr_put_u32(nlh_verdict, CTA_MARK, htonl(42)); - /* more conntrack attributes, e.g. CTA_LABELS could be set here */ - - /* end conntrack section */ mnl_attr_nest_end(nlh_verdict, nest); if (mnl_socket_sendto(sctx->nl, nlh_verdict, nlh_verdict->nlmsg_len) < 0) { diff --git a/backend/binsrc/nfqueue.cpp b/backend/binsrc/nfqueue.cpp index 632bc96..d961807 100644 --- a/backend/binsrc/nfqueue.cpp +++ b/backend/binsrc/nfqueue.cpp @@ -78,7 +78,7 @@ bool filter_callback(packet_info & info){ hs_stream_t* stream_match; if (conf->stream_mode()){ matching_map match_map = info.is_input ? info.sctx->in_hs_streams : info.sctx->out_hs_streams; - auto stream_search = match_map.find(info.stream_id); + auto stream_search = match_map.find(info.sid); if (stream_search == match_map.end()){ if (hs_open_stream(regex_matcher, 0, &stream_match) != HS_SUCCESS) { @@ -86,7 +86,7 @@ bool filter_callback(packet_info & info){ throw invalid_argument("Cannot open stream match on hyperscan"); } if (info.is_tcp){ - match_map[info.stream_id] = stream_match; + match_map[info.sid] = stream_match; } }else{ stream_match = stream_search->second; diff --git a/docs/FiregexInternals.png b/docs/FiregexInternals.png index 065c78d8e3e764cad01d69e64c2b2c95aa38a2dd..6a19f3c050822458de2d809d389823ca20875a02 100644 GIT binary patch literal 118864 zcmbrm2UL?;*EWogqt57n1r-$(#Ss()L!T?pwe$F2O<(r$@piHQyLC^b zoE`rgU+5J%Fll)^N2I)h+&O4i$B%1lKCgD!5ea?V?8MmVA^*eXCwCWqPhNK3NGFp3 zFDFf@J5ET1ueXB}zXj-}zZ%i@cCzzva)g~fcV0>M+6&>nEs>|9Ro+BcJPz zFlE*A4i}x36=a>AE?$tmV5eXwYp;6lqO7x{ijtz8>P5#34$gl!wE4XNxYo z`}}i@fbFTd+j+t@r5?#TIyu|7U@Ja2mug?R7H%tfN;o=EL9M^I22IdKOcd_$CXh4tu=K)~! z{H6c@w7lR{{=L5c7|$a+um3s#;4wA6;Uc_E5D0gewwjNcv!P;ZJ)0;W}w{va35m6wXS^9rfiF-!^i!-L3%#D z3Jog%o;@Bh8#WfSW2^FLvi&Xj#{wrVJrAUXjB)UEL({80N2Bpb7+mN}$M1@?4ib{} z6&c(Zs_PyG9ObX)e-nwHF22F{ehtTZHhALT+x@ll@asdd7W{Aj^*}&i&ojf;J(sqE z&yRzD`3}bT>rpjK7B_ht^u+PU+XVzJKNxp`o)i%H9xHGJc-d@^@n$zI=Pl4zzh-R_ zxU}b9$ZO~q0RhIz%{SJF<^SyDAXN(bML>YN`9{Rx4FN&uGkY)S{PFP%Zbnf|hl9{J zk2gE{X$=iWKp;ze%LUNS6=Q#$e|DOBbF+mlf4%VAFx0ewz$LBCd9`|+_(zvJN5yjg zIj=t5f6VK|=BHnO*<1&OC(x=0{AFRvVU81+Khc^a2yI*s2OFZGTTFwb~ z4(Crtdz|;pym|P9d}Y#g1IIfKDTB8pm0yOhIN58a+|rZmOMJhJ(P#?2dv(+3(r$|q z+_W4LBo0eWtwb-+-wbfcy6Kr)G4ta5rxYcrfyCdc9IqH0w$+w2Nm?=$d}ungV!$3( z1r0pT+1#Mcw;tU?gu9nzWUl9lC%U*zC7fBF5Rvw*4(jyPz|OczdzMH{%CvtlaGru*qPWU8>GAHNw*lZZR_he1={-$YaWN` ziBZAY0S3B_(r;|y%=6}X$)MT)?258_qf@JHmuH?j-MMkn(~poEZG$`j z8d3p`Mi;44wPoCt13PJ!7D&dh?g%A8Nv~1j&4$xOUPuXt(ZtqL0!==)zsxs_&hlBE zMR?KU#HHwaq8F`g=4FIMaVx1>RTa)bj@WKDKj+SJ@edU<$^Is#YX&t%YvZ~j?#`W5 z$_VM8u_5mOG`w3@{NrC1ZfH0V+`9AZoSx&5Yd}Cw{h)Pp-#f|4bPFWv6vhUrdU!HH zsc|t;snOZ7GdQTzhEz2~2gG1Kjv%e+sGgw&~$ z13I@YD8Z91V%EfCIkQF#TfQ%CiQF8A@x$;IRC=us{oCi&v~`Z{88p1^OZ)~J1yUVs`hfJQgqeaOl??ETiLLBlJg%K2K_v1 zXt245_zplisZ>&gY%xDlRhS?&D$hzd>whcJe^4GaBCoZ6$87bE-^D;UC8a3gp-ODN z)eXY6GgRbQ%ZV`@a_>qiSF$Ug_k#@cFwPF?XkRjKOY-tO1Jj51SPuqFB{^XR*Y=1*m!&)2c7^6VdNH- z8JP{rXpNneQKKXt@c+8qc8nH6VNz)4{UmK-@%I=9=B8{)yl`H_ovLM6XIr>U@Xy`8 zoIw5cls;{BM3L?5!wS@{FgfpLvi)0axIzI|tp9ox^>h>i_cR3-`Q6Xn-BLCCO`Cp$ z)jHW78qh2LTF0l*5^lEhUhNL_*-yZ%P-M47Rf_uvU>+RKER zuhK%6E#w{481>Am`Snf*k&@Ay@|0&6%FzldsSW$c#v!Z_abm3iMnY-r8)?#|PWi2T z2HI`ndr(ttyWME>PfzbhB0p|oauZhvD4UDdb>1(b}M+?fFKbbdjc+C9wxig`R zJ=#HS*`v?FczkbQ)C;LdDMfU3Eum`y8wZQswRdtTtW0@ABAy?o4K_zL7^0jFM%DX9 z5w?EkLl0T=^EHfr+8ov^moWT11r}0kU`CRFjY?>Zo-I5)hkb{?ZZ>|}&HZ&hF_JLQ zP8j%_D&(e9TvgG|tP{p}-SeHFS!#BB@3K~O?MvU;7#nPcy!yua;&_zSYJ-2cfOTEY(`zYU>B|nvAWA7!e`XS$lJ<_j_(esNt88vH>DvZ(dy&wsT z^_0J&-lnkFJm}}`ZmCdGEas*|IA2s1wmOl?R8#?re9M&|9sYa>JM$h)=kVb(N zE3*VsTi|5;r}6@hhFtS3EVOE`U2)6m!1Bna3yW}1FsrOS2?&sUnq*h)Y6(fdm*h1$ z*`Uk9w>4EoyS-F;&j>1o(59+e( z@4RDJ^jV)+kP}%+Y!AF~^{G?E#=dT^aQ8iD0*e||T#N>Wgy+84ms%S%Of0o_H6#U< zs>UwPPy#z`aP+T@&i)~RtmJTGD&y#Bj?feoFYkBkPn9Vl`x>608_6cI4=d{-bVSI%OQh--ywap3wNL0b4FA) zG5X-wR-s)^!lBzLJf^rmL=o~AU*b4@f)8h=cg!mdh{fH&v+Lg$Frb=@=OLjR#ZzEz+W!ptdq7|IAnl*zvt%E*`LEa zazL~6d@aE@8f<_i*Z|=b@V7&A$hX~S&kuLOwt)W*&CyZaK6rOEqYG(LX`OpJ$=|Go zI-Nss=Y~4v#@AbD=z%zMw%|)NCvt-uO{EJYz;;t)b|ed9IvFJY32X|?{~`%*ca?$dDV0Et8=1kIznq4$Z1T}&e zgb!$%-x0>hAdtS8PN+poV4KZkLPt2kZ8U;VnBGF327V-nrFuu%ib8?Ozm66;)Iye| z=VriEFfwU`xQvE&0Qa39=Oh^DuM89{6o^d{On~50bD_;a2=7CE&_B!wH4Y;$U?~ zDCIiBjr39SH8?9E`_>%xf$LjB4hco_DS1#*OOTyuo7p=Nd3EXPs2lTIiqX32Fh@AV z28K+CgRxewWBNM;W2x97l?R5XV`EpNC=z<_!U-q)o5{D2wj{@RS8|%k)%I4FvEc+W z_Q;i+?k5_fZW4q0AlR}A>YJ-7BLfMhid~K$UciQhF)DQjpcMfoznD#s(Ewv};-2c7 zK)$tHvM@mT-D-i8+)GSRobn$q0BwnzzNv7PSAgr+yPCq1S7hSE~1})PJo5}TEie6VS|-ccr(czb~!T?%eQ8} z|3CNZaE`~;>_z85{m~58C{ysrC~!%af9PX~J3&12#h8Dql>tV@ZgNbjio2g$%9rmT_q1PY74nZ(3n2z@Im>fPqclNn8KSU zf~cmWf0(e(xH$5d&Dq!o&8|`Gl+eZq-P5f;^|uUl=y?_6z<~ zrZn1b0amvt>f?t|55Enzd^SdxrCRw^?9DwwHuyNWXYWCYFYm(%c3_;V)A%bb%$*)8!ZOq~Xb{-aV6FMobvskbdDZl`;Bw;F6 z6lI3R0ZdwlN-d$YBb$IsZ-FZ@NT*={VJsZ3==>e$xHuxQs1%gjF&4A&wVrTswLk{V z1jaH7j*LIXbcv*oiJrhJFf5{OE2>15i|VpL2yPnswFdpP^*g)}+JgAB@ad-rHy)ps zDC38ReTb>ncN_~g!L<6{I}pUd({4o~7}6a5$d5$OHGQXxFD41Zh8xd7>)_nEe%2) z$KF6@4Fc4#%IpBz^NiITQc>ak`{WT5Ll=o^qxQayMaX4(@w0CI`bDY9D)EB)4vow; z(milev7EC}jDTF=0Tq^T)bqDPa7Fo&FrtbSSWzqpXWeLJNtnEsAqv=-I!myC5%6Lj z77762uPJicdEwmQK+Riqs%g4dUJ;2k=F+`-bfB4>*;9omOH2QGRex$$bGQkfZZ)(r zk!UpaL#s_&evmaR*{Re69dS5_s94TO&n(Qy^@LQx0R3&Ay}t1qj@Z~0N2}T*ZXtBu z|BQib^YtURaC{{g`P=cEE|s?*iD1mtA_?so!XPY@944?Nw3#-hAAh+fn*cT@lGK$= zP|O%5dyyoq3T7>;Go*(M6ZC3o2yUggtVL&@5bsm1xtx39z5!lO+gOc+u9&r!(sJQ8 zZF?fIeTdh1EF2rFX`KI_L7E4Vk8CE?ioZcbzd1ks`<p60gZn?EzzeoaLuHdd4V-!tOUwW}NNC6IdML`&rno!d1#m%&hQ&>fKMWBK z+1v#v`u)kzfM2`U8EqR8crYBdTB#-jj_Y%Mu#ExXzzX}(p7nQ?0rC))KL=b*id1SR zD}B{H4+2w)Xb@q@&2V$JhD9aj?6A?kGo1DVE~O*_+OxqqHfybrjIG0rDt5_2Sq>OV zXD$xm^|*H3xMBDYIP>i%7=b5}D= z*c37T3EhqMJBTUHCK!u`y9f&Y7Fz1U%lWTf%f5Hsmbk$Sm);R>UNT@Sw~!)?NeX*$ zLqiHC?_-Fvxeu0o4me)Du!DSq(a_E%{J4@!$XWC`oWm5X&X5=qox{$av+zqkt!DV@ zsASls1*JN%b?a)@ju4f)Aj+!p`J0QPV&gr@s~XwQll!h1nh0a0Krd-w?+vD2>)PKt zsal+!Ja(((D9Vyk^E+6OBtBLWZ_NNNcW z{V=BNq6mUCWGFv?t162i0FQbSZPo&ke52P#rP@m3J-3-`CPj1GGCucp2s%(+=tBkp ze2FP4Q5Qni`RW(;yze%z3TXj-3sh+HccV<3EL;^Hx<4hrA`OKd$G@I={@qQE;)k97hyZrbD?0$Z zP_ECSsCyo;BwxM@Gb)v5-X!#Ga3R{Njn5^Y{#24@^F28ITcn^ALfNE+tP8xc+bp(l z^(=^hAnd)cK$!_+@>8HmrnSIpsL1@K3ghHM%9^#wft`NTbQyIwU;=9y9TQl$2`q3E zt)AK#QGkZ$R$3@|5(~>ti?(v0zsF*mXV7&$@$@46ef>fOJ5zNDxAZU23I2hyt+Eo| z>wam5`be7tn_j?i2M`39K1ktHPpij>IJ4ISlEp-WHCVbxo4->6PRJxH*f8l3Kx)9H zz6UN9q>n~!XY-G6wog}Xm#F`DH#+mS?J?^>V-69j;~t0eH~j68*99Ang)~h_FJH-G z_AJa2Y&1BEOR86Da|yajBpl+_y*auGsfz+DWLR7!054d$s$t_NB#eoT7cz#^WS<3e zs+y-E4O7r03*ZFEz5s>fTA(r=MKICXgn^D|7vPRRXrmS0WG0WZ#~RYk3^kg85eTUA zYZIsXNzH$NwLl%luT@*JLEPW_c}(R&F2Qv?3oK2iPq$y~@3)09!v*1lMaf8^c&DHm z01{gxyC$8;4?*@OQyZ0loGBroZII=RRLv(d@*x?c+t@8HGi z7Z8pFDa6;A4#BxNSSV=@OPSaso!DWUexpt`);Nvc^Pme)q-3bWCp*$(ge?K6Q=tPu z#2`t-jLad+i9GDvvRvX{6_N(ce)-8}zwP*l{@|C=kHZ>LI{TW*GkbHKp1J=}Ucx2t z!$xs9Fae6v5(&U|TrMFy=f*qAd0ijTlf<*B$9`>aPC_?+V+N0x&iKYC02@adLSQW# zxbKsJP&^^}`(YNsEPSV*JG>ui)Q36_;Iqg2Z-vXlhj!oD<~7Nlez}$d{BH*Thyqvf z+!R&a3nGJiKA~dr7qEVny}%VH_mE2*9BaD07hZiIfvbr4*dUJqBKQo_&rr{&tcRQj zZJRGf^xK>>3x`)?1n62TU}KU^_1q5!-{7$B`p7j0V^K>*XM}Z2EMTm_Zvl%mD7rto z#J9w$!?+XDGSiLIkdlKDsy`1WG&uqu zVU2#PTgk-Fbd>D*Mu&CVdwe-KH3*!@dRCI^IAKAo5XxW3@1@b119uHgp))LuS%^Hm z0??u20w5CFEvi~pKFAC;HcOoMpJ~p|44gkfUd-3xe(ma%sce)1~JEsO>(BVQ|k*H9z*T(EJ! z(IF6qZ~egsPhaDMCv%v_eOr&<=3^ohmOuGN-@{)8xFCWeq?9E$u64HAAnUoKT4?2 z;)WhQzI^P`a1D>e+ptYC^a$fLTnsj0?(SG}=-mhgDgAYR_NSj5)^I9lvR&oU>W|&}(X`w9Q75n?cxJ#*uvaPdXeF;~tIn2xJ)ko~MKX;p zhL~_TyTxb?^cU!dHX(~n{B!wQhkq=ekwWqu3FBp*qlqql5WEHl`xTbsP7(F-#b}93 z!OVp*#S`>HB#%l!QFMd7$?hR%HeD^Xt03NrtK~+c7^#aRs^+BJj$mQ3SVjuz69sam zFm24jyKDkK?K|lj5k}BqbX{1qB_;3OiaOao7q3U2#=Uv5LPvY9zE+^dsxteTA>47T z&c?viAE7d4r_;oc68V_#9pThcT>%dnFM-7m+>N#IvHE?*+>^gNSw-p{3z*{9hkich zo7ZpgsYm`mZtuAx?=RF?+5>l(KytzhAX>3Z>|X&OAo8^U{*^4j7hk&o6;T&;aJ2Oi z@H{ zR*pzq&*~UHrSce$RP0_l?D3p4v3uVobx&)xs_tO)zdrrBp*bSIi=SW~f=W}jUg=Px zyMu$Jb<%|LYRu=b(y;az;5JELGjR%LL(u185UH}AJsDzhK|=9M5DWa&&4M6@mBKVY@1rm4EXP&WZcVz)eKnR>KW7`%+SHL4!4d z4cw&Yv2@W8&(*|xp z8V#}#!0ODycJH^%i> z$VZ9H(yz)epD=_C?g6zg9i!y*`Pw-A>a*C8m;UE{?#q{opk4~FgJ+)0T+cTWALlG~ zk0wnRjTsn8&&yb>Z((#l9j-r?m~fDoYb@G$nDOn@-rAC}jI?kCcq0s0WWz8~jO|Uw zA$ITA*VdrlE1HxL=LLRw?+^rbe7g3w7q4W+hWHE$^!tD3Z<>OxM^tS>@NLf8d#|G# z%vwI4WHD6x0L}$mLr3&60G0?pDv0O0Z&YOl?T)kk{h;mdi6`}}I^5piZb9)D8=}53 z>Ha7P0Myh~z~O;E0S^xG3D~BQc?r!Q5n$4OCzzt+a&&mV40*lCTfBZ@<@Fcw5RJti z)zVNv9gPhm8fHoY;sb+iy{=BVk29&IA>13K`RY?*F}4v}s;HkLkUN;jF*%l5$d_=ZWkETbuPU7M{G~d@P7k-pE@d z0Hak!qY(I#riQ4g7J8o2J66hM%@5A5^=AewMe)|nLtJZoVCPpiSS@}ocLz-=ZOyxyWw>jo*$8pp2Lta* z`r&H|kwlp;hNzNL60ToKm~d497A&>9iY0}f!yoXT)Yo&EqBfsK6bTyWg+P>FtWlj$ zm&%fO0v%VU>$kxlq0a0PODbJ!JgWX-0XdH&tnzw8Tk1pyWq5)NsF# z8mEa6T#ytql>0T{r482n8|4d=9L1(r`t<_td?lC9%>kOam_eF_$(Q)^m;mjd zf_mX=60f1+oxu|Y8SrcxfkJbD`q z1$G>hnBi}?o$A5G#R)= zkZ8UDd=2>TE@`5KNjs(PG#>Ir=0y+{2dBYpexe;`s_u^8#SP^j;Nsnve)+yQbb-!k zYg}JzsjVvPFEr+2cyyJ<-G?;mLr&N|gPe>|)5aTdlhpQAqx-mK4xtv)}*crMmw35Z`K3dspy@xOGp+|?4mw=# z{(=^_(lIA2Av8BuCu231+c>larH3PB^n9UO-Xi{}8#l(AI;7gn%Jm$-)+UE7F)3dM zj%Qd|&brQiZ1DEhLKAkI7e5{vu zs1#3&7|LM0zHChO6_o`P{xwX{A?Tt*K^Fszh>h=? zRgFea!bVX2^f)T7p7!m6qggxs+=-uAaVrZ0KLZDQjY72Qj-Qvm@vA{lburW6P>?U} z<$^k5>}@7Zm{@x6PfpvL9W`r)M^co~R(Ls+b==8bsaWPhqwV?|j!f*Ko-eefYR|j( z9?PE&6}abUT_13Y%~)!nI~#oS%y4K>>yKQUm$lW9nV}F#+{I6tqNt~|B;Lv^+R3B5 zXEb#bqgyXYF8v4R&&nI*&-qan7yJvxL;C!8|5zw(uzE}b91~v*`C8zM!xJPoE9vBy zQhzKlp$vg)YBawd;j3C&kZfz(#WOj(YLb_UFW@e; zkpSpqMUaCzX|_R{GoFDxM#0MtrJ>Vhqb2XQ&Vm?~8TgHZd9*UlOB$UlH0L%id?KB~ z0x}0xLgRE0i(M09cl}%h(2o8HI@<9LhQEC7N3z;&H$E@)2d@ zQ~hbil2~<6zhpaSZ*UmnLkF~)4AyG#>J1-CjAQ_%;VB`q6%X%Qs_NT|HYQA!2fH?l<$wkiEFt zOz#bV!CCsV08>yllFd3r*)wT1C2&rV9GDkKz`?u}-Vy+2T~9sd_ii)fpsaIW9k|2; za9fPyTE{F*3j9Gy5#}%#@7CF^d_E9|716nwsp>(A3}HVd7ODmZ+ZZwGuKX-kv6UT^ zPyg?oEV16TyzIQhpc|Ri0=R z0DxtFg;mqIQNyi~SjeGC*0O!wrhY!Tdh}9_$7mnRVy$7?ZMWU^-?Bnho5X*oj-MpQ zn!PobXLp#N3}wo4Hq=SQdl(IC+rPIT>|~(trX@!2V$vlc`ONc8xnX77)3s}LPnxP; z%QOJ$Z4A&Mmn&i$AjGhhy$0qZYYj9A%}_17-w*?`3PXDnsW%)AUBJT~b)pl<EsDG5OnlE+@@b7Iw>P&^9U> zf_xa9vi|m&{eKpoxg?O`wec>oP$YDP1Z zf&VEU?g5VCsR`SiCJIF&6X9}uCna$CYg(NB) ze)W*@^mqhokKrEhp92^$^w)-MAkkn(A68)@<&O(YbU zd-hh8Yhzn|(ObutoyKK03jMtZFW?xi%O{&}DvysT6=7&ituaULmbqPnN(Vdn8S$@H z@ilrbTCy$=$YHU+TFA<`cKHCVY5WUc@8GH&p)B;c#}-eJ$@|&vCDmWo9N+QG=&*6Rxje|h z#!tvH7uUwQc_|91$h&t4hf1mjBzmDJsJX3!}_je;i~&?bjb_}U6KQ32{w%I6D~BR#U#zfM1Wa# z&SeI(IsU!zJ#rcJPMUIQWXD>6gxX_fT5ZhkqZJa~eq*EfxLc=_3=skufj^39mDA$u zf2IKbC1P=YWs!@c?NakRQJmVG&Q&9~ue@v&2<(db4PQF&#*OpDTAQEW-Q^QBkB>Ag ztuHcqs)E0_zdqy7E@T8;(gLTgtW*q04t4`#hWQHcGSQHqhtxa0kSqnxKC3-^WDY1C z9zI$YS#whXdJfX4y8G&n9?9^%N+u9ed*Fwet}n2^Y^rK&fU>z|akk*BNU+2~^H-75 z5gr+|2aSJjdl?`*prQmT3*rtPpx19q5`vi&kh7-S7Qfs2VEp~@nBnT^8&y_n4O=Rk z)=9y-k3w$EEZWxvhOE&e@$$)|Z8QGk!A%Qs_$`&=39$)UYi0Kle3|_I%`ljyEEY)& zHtCT$q1F#X5JcqoeR+V77}p_Pj-MIul>b|n*}Uz5H&97ereUjogwAmN?L4eo$n&rk zvNX^Hohn}yF8`1dR&@G~k#&FYr(Jo@(b6`mqhTk|wdk>d1+8lLf#bv^X%H4xDpc`= z`C$%HmS_+pu=w+GN)1|E*>~cd{f9t_g$XE6S#dRjtvSvp@!ZULY!O=Jkjmp27(Ice z-r>9a$dA7_C)#el{*Au5O|?GLPd2v3d)_!W=DjGo{uvO$<%sWwm^x4=YLoNU0hJB_ zy5E3A{peIO5ap_?Ru>;4n#FjW{5;fFHZVbbVLMedN{K(u!TH}CI{hhm)yle~P>x*7 z6x2T%==aXi2P1;E&nGPZ*>-a=srI9pP8ajroOHFKxN=jXT{6bn(Aj5kLdaYNWCBjM$VJFf%uZ4UJ&WgWx^M5rRn3b=a!? z$XCD+0;mJFtO;y)5v^VKSuA0#bCvrm-h3uxV~IGy%K-50m9&h7q->I&WxKW@e*uo) zLwyVpEUUa;=i`(G1T2f9ltrMoD##{igQ^0kk0~)MI!Dfh!qPa~J12mG;?KMN=wl-G zK0w|QRyJTCT+@qto!aS0lJW2Q#1=oaLN50p4|pBK6oQ%(fHEw>+8%OjONBH`F<#!S zv2UB-Lg4!D;Ga=Jh7jhO_@cED#|c>@@KDrXXDaWh{@B)8<1ftBnW)nG^*bfJ=FsVH ztFUt4NL?Ik$#pqJb|Gp1gzv6r0z-E9$Eo;O6l+mMClRxz_59IFMb8AvcK&^S<}yd! zWhASb_LRH*JI1t>!fC^BDI}U9mBnc-v0g9HBQibiDKoi+31?1ho>^Re0`WxVA z-bXG^s0GwFX!LDLp#F86^4uzKI`P=>K>lv(B4F(n&x8W@`W8U+cg+=Y3PB}K5$%Ur zb@#()^XctaglvsaN>VA13IbJAO>vO3#T%WV3Ho|L4^Z)eQWTJ=h=iP#&`OLu(EQb6 zZb*K4Yk_%wEn=P=>^5BMVq+sk+rn6RP2a7xUPjZ}-&61vu#|TM^I^X3gud`MSM-E@ zLMFx*-PZ9mNvO%FWy%*3EId@u?pFiq`oi(3%SYv75SAGSxRKMeU z4dRb=R%8eR{d4vE3H)xkkwR8HW_^&A`$C8^y^+i)?2ZwS1%MwR(17ke-?)3@$L^4G z?za$Mj6N^5RGBWI5Mz=exJ0l=08ph0Q-Q~thEG52gLQp z9mSNOuVJsOPXH<>+&>^~JumX8rZ9A?LO==WAGHy0{PBaY<|&__+)oWtFEe4B?H~nH zM^`fiYke`pJ3%c<8`E@8w3RQGL`eE~<#q$wf_#5eafXZgUJY1OZ9p;$p+P~S1W<*q zcG?>h2eC@>A2hb@Zu6hcB6`r6N&x{GVA?|R8*{r!TqLzg_#66X2~n8)0vj{@CKkqC zZnOa_AK30JINw*mw!@uo02}2L0|p0tu7FQ zvg)GUP_@bTSjZc#t1Ps56EXgBsD4c_>!?^`3MKQ^XeUBoarLt^UC!=U?(>w2O{`yZ{IM&Is^3BTyC|3u1Ra&`h6@04f;89Qfbj zR$BuGZ z3kA0AtR+PKMg;9r)Rp?cb`ioiPHzZr9nsKw37)i`saE}kqD1D-s66f}xw+`eFMOY0 zEYnbIL;N+x?$<}VuW{a0m$$G=ZouZJqtoQ z{aK>D{Vs)aTpO66l(fGxl?daGCRfuJ6P^MI&o__~%G_&#hk_|ZN7Gyz%bvH;Ufa+I z-lN+!@YnGFn!T|kg+Vj(aWhYW3)BWC5AxbOHWOD_mV0)5H-BZwX-EbI7@68asR}tf z$c6#}s9-H30r!?9&&lM3beveTuJw9B+xbb%GZ^fAeLeqDnSK=mxjZr`mILP}fMGFL zrRKG+ZOi$5oCnh%BhUDGtNR@sM{eBw-`TFvz#M;iRqX4%s z&=EvnXtMp3$(Y4^8h|kg`0aG3z`>MNp$3I;3mcC-1EYY z?$0zv{va|^Ao%u6B48$|P6?~wAFn!+IxL&&^nsQNq)56@xd|ws-9gnJ$Vyo_NQDPV zFMU>L=hutPlh$Tli5NX&-W_$}F@S~xZ>OtSUd+*teN9q)SWQhpz8U6cP@ql%1i7)G zgx|97fTlWSW4)Sr<6amwPin3B`v$e9v5ONq1%T+q07vs_BN#+lpm}pNG%X0xxH7cd zg7tpHem;l@^zq;k8|!ENbF%|DO}hv+ZoQPYe-{tQdygGB{@CvkxtNH?tn_H{g2{P2 zo@vpo5}TQ-L0E$t=je~+LR&7e>~aIPXQD%I3?KLBhG@Ien(w+K{H7q9Ta|RQ@s~#0 zA|P;W>t^z8X~VCZBCQNyy|A|_lW}+>f0XMNxcEq8D6d zM(tSY*eyf1uQku z{~`=(U#fPL?z5ft3@O>^UQ?QX zbJlnK4Wyxa0VF&Q+B$c`#DKsw6x>a))K@}k56#hE=XvWpB*0Ym^&YtnG#PWPKx2~# zYfq_aDH>X?E0@*W2WO3ts^`C&`U)R3IT$J3{v2rgdcrcb0P)kF>p4m`JwO|;^|9cz zfU1$e%ettGhc$EW|7AYMv(w&G^!SPw>pyW&M}Qk+UwrmBZ0gc*h;z+=z&OHo?;1&V z^aYh{AkF{^FQA{g^o~KI0L@MTRAW=9OT3mrmv0Qma=5s?CqVqPWRXDi4af!OkgDbN zMHhu18graBJHH`1)jhEL6^tLdFO}0S^kV3)%$Ic~;XpYKj=HCZ0SY8zvJ?ghs1zjf zF5Dg^w|cv3_oIQxY>`Uu2=vxHs!$jM9D%iESHYQ?{!RwCd>S74{CDK`cZWd>>eWU)@y(&js@Nj(Uzk|pZStvKwbG9faFeKdxiKCJy4VvJQ1Wh;Y=7IeXw0?#{KZ1 zWD_ERuUVu5Wt^2^(P%P|(Ua+EZ9dINg&3ug$fXzVTqyPy6;r-xd4Aw0oEcN_^;*#(9Ir9O^ z&2(9rV=6RapW)rN&Yow%BAt{8%RSJ%50FsZXq%EAGMInF?&fd1o2y&>D{a-lU<63| zn#YZNq>E-ZJ@B*Z!#Ong9Lzc40&!ny)UG%)Ah3eOi{IgP2cSD4AhLdGCcoH4yZZP) zS^!T-=|?Xb0txofD29a&N^+)ePq)KD(6cU29CmKKY!8q~@u>lD%VQd(X9+^I$adR& z*G3;`1nX^KiR3H0R5rI@%a7r~Ke(aA`yLQ;es`PEl>wP;c`jd+RtI9fxpSK^zEexkLl|DbwI-(VHs_VrxkASx|Rva5rSuOV&Rym!N04~y4l;;!y*Y#8r z;nK+eCjr(!l$jm#u-HGxcb(te*P*f9x8xsj(g=g7`5%Gt2;`cgCE1tgcA;NAe=O`Ed0`oih#b;@l ziq`>5@n4n(PPVy9N1^15>47h7p%wk~;-HpyTIOD&Y^yN~lpI+aRvI{d8dfTIav%i7 z>zdxb$QMmMK6Ub5=3%S8O~>+`@_Bj}d$-LgtC2Xjcz&gIMCYc5ME;jTA1uJ*uy%6}F&tl|i6pa^H>`kgVXtPAB1;guG+GjDo{kAJ z<~)Zqs`syxPQ>m9G$bESogO$E*&Fh6=gz_x<3MHH$-mG;6p=;)E3(waxPh&53sVKA z8@h&x*k;;Xyr!jsb(Zl_<`r#t1>X{dOci*!2836_5q3RWvz%>Ro;%;bQF*5YT@u)G zT+u98rFL)=y=589&b;gJ8HJbLk2@07pZS->0x+R!eC5pWC|Om>=~?QkMt-frQUJdQ zb|HssH2p-{-f7X1)l90Gx=j!E=-&m{Y(rm6I}oH?U<%^p%^Kjj1h=sR*F}3PZ6!dz z0?XfQ0xAs@^7-NHQfz70sd=s7ZE+2I4-b3?IzO=gz+wSUwW!P>Wv*I*+nXhlGr&`W zTa9-uD4y7FD*nn>1>HRa(SB6X${u(SBswP6%M8??^iyeg!v$Ftxa4N-oD-FLYyGeN zbC{2L5SPpO<)D>kZeE9{Z%8VbNdrFlHsFbX&YVwdLR36RorE!kH-NhV!D(AUG$X+7 z)RMYa4D#+&w=ZrQmRNaEz>I_z_qD5w z-c-0@ZO^8yUZy;E@(GdB;kzbbjJAx7 z9CnR`GjKQB7;|T1=nOYCR8;bBm1pg*J1s2I-W08r$6vmz55*Uosh@K8k=9DUosJTv zB|w>hGLv54Ycq%tdsj>7@1SDSZ9#zn&=Ml=837MZ;ojwZP~?3vo2_59V{ukYUxE7m zn93^N%E=EizH$viY>+)aS@NJ!5=`XMnS@}&Ols+0zOBWB(Mne-vF!`lzw6G!9lg%) zfNB~Ph=EM_T$I$$LDPvxC-=czYpqOj#&M zzpx;Tg8GvgbSp+L_^gCX7#6K28jj6>hcC9)hN@S<{mgVkSDCTT4m6&zpBeuidRx*xr9S10%^KiWBd`%r`0 zIxCnUR|KphF;17Up82I2E`tu%rWE-ZajtHTdn#cF>kK)Htj-q>OYGf_x!MEM?YoYQ zVT_wp8MsscnIk}sVYIa7Q2zFb-JIUU*t%3h4|x{vVFv6Hq$NOw^qstd0G#RzmFAXC zmmzt`3EhftY;DIjRqg16;O3>Y-z?uCa{|#tpz};iK%mBFL=?3;zrT#ti zuNsKcGIF_3p`)F9#W_bqq(IY(Kls$>(R~>SB^*bydmi(F!{Bag(OJNBfem;9HlQBd z`jeNsBemZCW@G(oSQX-@H39vU3Q5Effd-DPHr*TVp)vpIKWVwvNgj%K;&15qR|r60 zd!$!7OaZC*S>K+2X!mLYwzbC)lZ5^wDN&@o9Vn6`yuH_Ud@kImpPQUgqFo277Zo0obO-7JLDa;NJud9#0@8A-GKM=+~50t*YD>a7sRI=9rm1WlG8?MmaBm_S>1f zO?OkB%1HzV+)zh0kC$VLvPYzQPEWwQHhaVrboA+UMOWf7Kz{^04O1AvQy36o(_iW> zOCH$VfT+uAlP)E9guadUBC|fmof|*RerCA4b>7{=9Y*fyHPqJ-p>?t z8|s`00ToY9x(~63!0Ojn5wgn@FaG^$kODO=64F-An7{eEwe?T{mZ|)&= zf+2j^Bfj3n_*-B?4)^3`OpVl{lHZE&l@fb%?F<1#BzH^J<_-WEQWlw*rLj<}@sh5& z^LBr>!11&L!Rd@Fxb1-FfFaI^K~H11X3E)~8&`tIoCHIki2JDRo>Peb8+}Nj#HU42 z?P4r{%WAIA^sr~W~cT05vX0nr7x;bwBfs(o^U8#JZMBP;e59)D8qCnb|- z`O@X*G`Kne zu&o}TLt2^^40J)gi@GH>zf7+2FzEiM5Akmn~kpuV37OlKn0W0~2r4R(}sLxs`1 z8xUb+koxY=%;@@?i_7ju!WIPqX@Nm?*1$~JRm4NBU-&4 zaDhYn=8nu(^^i3Ama7Zuym?>!uUOIqA4-~C92t>Eetvj#x_ziZW7nGDsPTCWfFxip z#}*@@68fXgio{ItFyx&a_W3gZ(XXpWT;A93(wB&>(gZq~i9cW9ZD0)!9dIkPIVp4A z9-y>a8A$`mgZDYe&px=$t>Yyk?;-n-iycApUsoUmp5JlJGwRzFIqbngU8yc^$R}c1 zrQ*7jje1EWvi*Abg_c`+tDE)0KN9z$ zN zY)9S`u5572PBH%Uk%USQB~+0dz{yj+5(h1GznyQQa5-JvI~lk%%+sznLe!Y{Zefqks{CyQW+}xEBZB_ zrCU4_h?VWQ+cZ*}7sxgW^2H9o=g^Fy@Nvjt3)5a)k;%T$3cOMOi`08bs3*+QnTyQkyTD^8FXmK3qO>zP7w zWIjf7v^n7s`9mC|VPpaNbj_Os0%Gql%>gNKcCM4`dFqj-S8_P$VtjyN7tqF}!<%d% zdkPYFJ`46H&P9rncP-+2s<3Q=vV0%-2IOdfy>ITatTy>ltvrEb33Bl~6p?E%xPg%6 zCI2tjj2PVdW7zUD+cV@kV43WDwhEgK+>D#Z;R|a=-%w_piTN>PG7?7aJ@)x4Vn2KU zTZR5jDJM$%aI>GaOmJRBmzB*Fm4#I4<&Q394~yqcj#amyMc#c2ozWcec;{n1$Xszf zg$18Dax!FuZ_1%kX62i*;i{IQaU&|GQapS46mo>uA;x2Pvu-l+G{2T^5 zM*ZLaGhM4FcYRz(IoILYQ*p9}vK>rY_^sC!yyZMn4~XWnPSRwc4Xy9t7y$#WJQEt_ zU3Ix{Xa0SmZ^<-X-2CJJeuwKj2jzo*+xT7AH0#&g9oBvZsl$2SHGKLowyQ4dLFeI| zAdo!upTZ5Oq3dVJGxj+-42O~?Od_DZ@kgTY5rVtP;*!d<4psbRHucFHqxjJ-+#e!-w+p3e)@3MFJGxt zQ=I%KCGGI%&eO&X978GR!6fDfJ)vHh-umMr7ZG;1SkF8cr;Tvhwe8v|n&zgxEwYk+ zsy^dXQ>Gth`jhmuri}DkrWs9{nN4XKY6s!FW=|guC44K@JPc{{Yt+0r8#@C z2^E>inSkk}?Vf?s%~m-kKBLzR?M5pH(<4AB1+H=I+ddrX&5F4A;~AyxjVi;BneFm8 z?&B}pHB(%UpFMeK#~?)vp9{EH+jWlvC3!NQqh?PmxTSwinOgC=LM1ZMK>GpegxDaV zoAvekTNdvYINm<1F1V2=mA`lL#68L$@oAIofjABf>moNU|2aHSoNOQlReVy|N zb|#usVzp8{rY4_x0LnGQPGOPXBvGRUCy6~>4|o$u@8 z>e_sYX4Un9rmxq9KM2JWT0X**yYJr*p9;J9(8R><$z!8hX7_7clh0pn2kIp!!t89< z){Q*=-Y6TxPSp{nX9Or-F~0lB=l^)@>}Tpvp8^#^lrhiYyEnE9t%JQ=$)_KpoiMN! zL|M{zilQ`~tU(!%AU2wj2>}uakp*YrQ^*HOFV5g7c|YVyx;f%kS~x@!_Thugyc^VF zm`$~|wKhFsj@h;-W=%sevk&TSwK}rpWY;TLSW^~dIYcpMvAOGGnRBab<^Hh8-<&Cw zWV?-+%hdeVmw<2D4deF@XHe+RcuCnL|H3hI1+ss5?hhIA#8!o^t?~Okd%)Tmh$|qy zHK|Bky?+p}k>{HeLzw_mwUSnxd|n9Kp2@%ncy9DT@5t`9{!QoSvCCIPZYXZ4D{l1U z3E{ip?He(ujge&T2gW4ZEXT|pizci+-RQ(c zWKQuvJHs3@N`hC`-Ly}1=JHO6gR-?^5&}14q7jL_l07_COA)FD#0~+gD`P~=1e-q9j{8Nz!t5hO9UB_ z+m9hdh8AN%b48j||GRzp?5lc#%84nC$DfzYd9F24eHO<+^AwRZ;V2Bs%?zoyglIQH znIV}LHVoU~B{vc6daDZp<1NeepBORs*2#zt@`0QCIg99H+|@qIYd+=V1NR+xnM)Hc zx37s^>=cMzoL*kTkR%r*mx^w>nzcc5Ma_^61gHn|Ffl$#!m$-<)dl+`B|d8Au_EQD zfTJP@DZnyWS502a69O8R4UUq}!UY+|l25RyhZp&YXEK5ml`fa#;y17yu_G~d_(}*I z^qg-&Q9uZn7f{`4qp^H9mr0Xa$gFMc20k*;2%$$bSJNxM%)k1-@~!H~gI;o#65m2z zP4~tfYhh$y?05dbKR4*VV}`7RBlqbS5Pd>+_pp7@5fzE0OMho%8u-Z68-(5z^Qf1C zYaAe*FtUs8zUISwqD84Op*AieJb%;9K;_2c8}+U_U;4MLh0&N}D9liYJ0CwWFScgb zqKkKtY8l?|_ehJrj!Z{DR0PrTTK;V+l3piIbR4?TXle?l2NHDAo0}FH>vhf#Lchc( z_AK>J)nife+PTx)xJYC|BliY7HC7psJ!8#S^5t_|I#Jo5}MmH=oP@;^f)NI+2Uj`RU zzRd&jHp0GhG*;lZoe);H0Z+7in6_trYV>~utNY{m!Z)&$B@rjfD zhqti?1wD_ji>CSCRh53!+_tJpM4>K7`R=I-glVHO{-Q8sKTDST(v!TvuI$z zw|^yM#OIT=NU+>oGv2y{?pL6R@9 ziT~Cc*#Lab+@o-m5j))PUCGM4P3<7eV09y!XYT&$EyoqFHOr@SVqW@a%ov>qGBdi5 z*cyw_w773h^EJ4s)5?{;ZiqbcOI)IV&hZzZSmJ_=-W}ixUW%XSxam+Af8lZ*UYxm8 zfIiPoD}Hhr(U*bwB^*x+{kc`d1tih_p_dphL5vrn$d8@JjjoOb8G%wHaQ@zKjo*{_ zFFRtPnGhsF8y`WsfVuqs=h}Vl1S|Z`1r$L{h@<(hJp&f4AArWVgN;7F37u<)&MC>P zs#Ll?ZffH4Y9r&Z@9)yx?j%-#d#wN09^d`kg8PBX_O4Uk6}eM%lfagCngBgmKq$7_ z19^XgAv1%Q5e(4?Gd%gY04;Z&Rg?mQOH^W$`t?5Rzu1c~B>rdbXG?e0M>-{*6=(lW z_;I(Uz5Op1(O0mqF0jWcMaL{tDD9BS?tvuCW1zu{2p8j4u<&nIb#D&1nGeRvktF10 zTD-dRn%n<1teLL!ot39ARi9h#))lgH9kg7ON$ekF0;e#CskTS_uCV5GvKc(SABhkK zQ>E(%|Hg@}B1Fj_Qg+LvwO1vRQ7q)q?>_IMZZOgO`(N6cVO>Y>4HXpQKVB&ion0q* zvEwya*Pp{W;zCC09OL`>?pL*M31PW1UUT7q#p!RR`Ie(xi+nE1z>R~n%n+4owqOOOjtuTiyLR!y`V+GpW{Z{Lj=RSPC zz{)sm5vfdphbjxF5Of-Pft5>AL<)@@As`=G)_4ucmnUESNAE|+`RFbRbQQ{F?vBXo zd*3v~)eXL`SaFKSEY~v=RMl2858_fc?VdnX?J-cyrbgSEed0uJY=I7D$UW4Ku=Z`B zz!9-Aggj4xQnf+_JdlGZObr^t>*s;o=(Ei^6Af%0_^;D2ePYmemgb*a?*D%Fnq;&B z!`$Zx>u&eExmp!gH2FGj(!2B7l*{cO(l>4k@-H`cpPTV!ItH4E@Pmh%_)G3l_ygiO zZRd4aH+3aV(noJq-O1T05uOx6Od=vf6uex7@-TfJ@O3)bt?KNle-2RQ)Gmc!cbpc1 zO{)Fl^JfVhkJTWMqk94B=t3RVT)N7GaV-%fkIy#C7K*uz#Yy!`o!KvLFVL`>?)mb= zxPk`Im`W>~6=3jO5|s+k(!~9>ze2a9V`suew&KT$c|dWQfr3d8t>LREHN)M!e~A7% zi1Z|NPxAk>SnS71kl5~hIi`tc7GVDnwwzT)O4_bfUr3F5-ZvBBVuf*MZoLcRl}men zGb0`P6a(T$R~Db2KL?4e@xUae0xZ$zmCWQ0Ox!6Lk}8`p*sQXO;zhW`7O`A9Jl*UM zGsLYUCp+&1y#03ynJzQQIKo4}#^CO82n$JxLboi{gS?-XfDHy=r;FjU0ei6@TgH2R z8@RZ3i-*%fnNwQ3b0p64MfDLs6mFRJhMRuKxU1BUInBw??>7#rJNDoou*Bea!qFJpsxeq5 zZfMhe&qeb61+sEHZWhgRD>!@of40j3Sa+3<6L6$PB?snQca3_A8PXYzAyVZK+*2mT zU}hVB!iX`Y{6q2t;zQBn-FoVis-)J*(EkxM7$MT9mxygyLEHpZ7AS9=$*O<}KBhg% zNb;YWoDLdKbIUqA`9Hr4z|!5fAMgMZc_0nG!I4dNTA#)X^p5BX$Tgi;i#|Vs{Bzh@ z|DDCxfUo&Z_jC~feOW}%d5PEI6eU{guA(GhtB!^Te!2gm%hSOmHQeJ%AIWh7*(Wjq zkWzHw!w~U#JCu2lh&BFp_;9t$VZSrD&@FLHT~g_!$^Um=?}6(b7@I>h5}>P*$prUf z1T3ms)bfZ?f^2_iQ~iIRao>b8$08D=qo8x(so%?p#P32x$p^rbTh!zV`?@$^0kI}| z3*00{GuwMwwG&Q9O9rBc*x+9n!8RXVqu?WF~*-iFu#V!HS+a8nX$0{F_aW z_|om8AoFF5`glU#|2>4y`1ecx>N{=B)#?{Ncp#-Nt6{3n#j<+NT8v#rMnSEbkj_kk zHxJnipI`fGL6q6-fHXV|CdjgdY@gTndOExvkAyC@L8~vvBgT@S#ea$cF63zIA$l3& z^-pl@m(m{mL#F4*c23#`!sCD+e8lO37pnta?3lPWaAda3|9Q0o?*W!UK|}rcJXXzyH_vLiE@k2uNB$*gCNB1# zOl;Y=#P$V^^OvMKmq3J=^zw0i)4>V?O-xB`M^US)3(#0P&iU>vpm=@zrrT@X7f_#r zWarwSgCh?@2^aQpb!ET(os14Ve@ErU*xra(|H{ZYaie5BQ~-t)@*4beHc877Lk?N? zadRV^m0GsD&(HJ+G39x|4RO`v#2O^+DNbEd`s79OVR~}kM@h6JMVGVMcqqNpw{5WZ z6TLJi*J*yDF1^WJ8)h*sFZn*ubLKNr;3xZe$kR}0>B~mmqu6>eSA@zWcEc%gaBk#` zx$@V)SQM}&pp|wkT!JY1!5i;B$@>C$y_X4o zr&FVkHlAqHIImxKR(dz-dKg$;zIN*gi=ZlQkN!CsHO06?SpE)`BGf4PL+)B64O_J* z<+SRTjQiW&Cdu9nUA5ba(Q<3x6WS@?%QF$1jid=FogBep7iC-97|!R~PBLBh5#|6o zJAE-K{+@BSnBpW*;xYg;H9ii)E;-`e#SS}!ZY(%oBFfv%VG59S?<;p$53|6^$Xv*X={(vk*nyRUd_Gd-7~dc5>!X4 zK)=}e(Va=!TqD*IC0M6f51XhgVx5ar^X?$(>u2_dx)_U}PHH<1>`M$*k}<6j;b{6y?#3&SNj2wGQyqHPfo(j|+*j=}I)fdr^k-)L{B zQ4VrkbIf$F0$m`HGKDJ7({B!*W&1`o0NKo)qQ{quA{I^vTA0-MiEz zGW$VhZ;t8@C0C|p1$uAi3?7fT1jj`>WW#ULSJBUX$r={3X7-6Z&n2Z!beNq0iqmWe zgSC!0e_(ZQk3%p3@MXLO4F`@aGsb%r6(_Tb3i80aQ+>+&`_w2^gm@P~?3&Cm@-(}z zL$zQhxxD7cI_pCbSP*n8lbs5Tn)dh^rP&>riB&&gPREm(u^AFeH#vs=94Z8M+{*%C z(lGvEW*0xT-Wu%be!QvSYER~MoQ`0QMi_c<31tK3-EX6oX?dnw8GtV)z?i-)u`ghx z0_L%0du(oh7lqkDW0b;yx4RX*LXH_CQ^{zmub|?~#g2vCbOLk3w(q5#TDANc{C?#0XxIR@YZuf6pW4bc-(8@kaH?)Ro%9v3%-;pWNB9;n z(2Hn{*M|Y(U0b|+*z_RmIDiq|z)($LlxiqSe+^_|7`54&KDNmR2@ws6G?SAw9dflGdI+8wOsdVVtN*k%V zCj`y8g_!N6L;pS@v9iA9%SI~-XWJ$GsC_F%;+k)+R>Se@d2(-O1%x{LrrU(!m7O}8b=*R9e; z1VqCW&x=R{HZcG)#q<#+dx45P+w4KDA36MiHvNkoR&H>l2hfS1C`>YlhBsq(k*?L* z-F@6%jECpTpD~mSlbSNv8BcV~n)bD~^fJG>;akp$m1Y);sx%EfmsFwH9XFh?);8=Z zov@>(u^Xo#+wYd^h#Afr($AyM=O@Y?9$zLq=P_Z%i>jQae&R~@5 zYrfpZ-q$O{pLK0DKYF8K>3Uas?^Fgm`a>+5BwUj{WaF8CMKoVqh~`D=(jx1oL6*P87;y#jxhvtg7V1uGZO$u7{#cI~x!b+Cjv&E|Md?;8jGVAh?HtDEX{$ zY;O4&8iO5#W!l4iF_n$Bv#~zHg2b!s*50$=%k9vmk$vs`J-yD(jku#1Kt;48GWtUxdkD={v4kVv5{9BXKJsc3( ziZ5p@a_m}mdzU}BbQ?ueI0Y%U^qp5YV<)}Y9n`9_^}#z1zRM?<_MOPsV?yfbD<3%D zCx+TDgq@rgl@uPu zC>~kFn|i~?kbRB0_2VAlZ0WW;Gr51TuC6c-wYQADJim4wBsD{0*wwU}ZOm9|M~6k6 ze;X^t#(k?E1U8DjIlL(w?ImSqj^BAnj%8<-5iWhJWJ$z19){zEIyBex+j@zuLd2`r z2FT$NAj~WgG*1qrAhVkivMl!=<67xot6NWpNxk%g(kt=Y^ws%$s;jp#A`4Hvqh9}c{{sBS{Os+W+iTuY zL;SZ~9!bTDMgGAoaJY)}+dSJ3aQ`x~>u$R_Hk;0J;<(qcv6UWn^A6hkGDtQ1QsRhc zDDgbr-b=xa502B<{yYFA6;;A#_*2Qe9ns48de3HZ#SA3BeQux#`V*@NsJZ7V%Jl~x zy3q!WQzHuqI7G9RGv0ODT?klHnAd1ZT4!i z%53md9h;Gp(~ENM2^Ny`HWV|~+F&Hv0O1TxAd=JD>Guq{3E+_{du@2``VcsI+jfncayf;#P9f*&8XOa~z*_R@g@v z3UErTZ(GT$9fYH(ltP}7>d$X>CBd$pK1rf=P3eOpJmIMP;O@|B;*?8276XNP>@ zaGGQFeUWm(#Fv9*mCy7avGx&T4<#$bu(`e z&l`@9yx)DXCGV2DtA*Fs=Icpiavt!l>$mHbSsjPWzjD86_U<{K-Pdxa!MZMBR~xT=#sF2lY17DtjOXRV&v02CLj6{UHIgNEFbKj@gnl-L=jTN2uvNTHh zwi4sDbgR#J<*B5jU86FbR$wE>pHI2MH1dT?)5pQgD9K%tCbDuhk3u6x8sSxz|CP}hF&+qm8*}G=$0@;7H)Ft-uC4zeiHH!_)utj;dhx1^1 z624;%$S2?W(T(3FC(&_@sVBUZRl40 zV!(fwq3S){Sp!-%DR-j7GdSl1ha;cK@p;zOD1>{g$JK2E*;@VQb(-(W9YYKs_#Xs~ z0KxvXY+W_d9gnW;kIaV8{Vfx%8=F`|o{yH^EzolZv0_#z^0ce@6Q$Wi?XfTJCz@1+ z>+co*UO&&0!XeQs;yN3Y^N4Cyhu1*K|-`rEfbm%4ztvwJ^TarpkR zEJxMe2dVVR3J+WwinJ%YepXhSYP?zF$vDvgeCEX;DeW!YHp`cNe*vo}hqYv_)Wf`e zvK1}Yy`_|QMJs$~KULw%z0Wn)9}RJv4`fAf&-W+h!D6l%hIVD!FpZu9+gXh9ys%Up zRF$qX8g{)!#7WX@=>yh@d0l#Ln7>74zGQ)H0Oj(J8whC(W!J*btY?wn&W$XaL=ik+ zR+Cp<4=io??-K7$pd_sfux0Ic=aYCkSGP$S+;U0EsrYmyq2KunADln!X$eYh${n9f z4*AJ)rC&G{)%#-wE_Ap4mgzs67wTsxY4r!|_=5mN#fqH6KvxC*$H4i^Kzk-;xDIw} zDl8zVF9M!99V3phz2oIdRu}DRb-spFRj$(wSIX^A3G^hDsa5`gr+%wS2jNPv=fev! z-ap5=S;{giqqShXRQ@y2OKZR&^H19wEhIcrzJ*>lH*0PrjNYLaFe@&;jUVT{?~HJ$ zaR&AU`^mIamz6(l=!dY*Y*a#%G5UY0;fz(ELFzIHoQ^-Cs=2xKX<#R z!+=fjx{s}TaOz}?ScJjH+&??G;=Uet)p3#SbB`t=4)M5S2u3J7j*#pq<0PECkmR(kD0IY zrGs!)a}{MsM1`U;Rfkq;B}K_FW6#_EP%0>`g~g=KAh~pv3qd?mw3M@EM|kaV)<3*9 z)>v|E9-y3$SffCj=J_kF=#OWUnmk{`toGVjcjn$=K((?)>s`Rj8cUeHeRQUJ5@56E zTQ*FqgYTm4U2LG?e6#g)ERZ$7s2zSrMcU9h&xEsN!^ z`}Dj?p&6%Qt4OVm-@LW$#%dxEIOE28UEch>zpZU`gkppsxa@{>G@&FWF9Vtft0^~&`B=cd)aoMnmaN4Aq8NUT>S1j z@o2r)V(z$X zvuw+5dH_euWf_q|1=e^6+fQRWn@Zt(Uv@#XB%GH#CtD`38f7a}c(zCHChKvot1=gV zh|kn1EC(&sRRR`fdT12O?$h|Lq2uSdq-tJKRlr`WxqBcJwmgs;3}ZyBpjqmycgWy& z?_R>Rk-SB&7#vv%Y|G_NGKJfhkEG-}7&idRhiKkWW4ZqT7f*i{MBxJKbH&jiNSfKb z!f}QiTPn0KX!rJEIGwb*+#APcr&|5GwU*&$08=kFGqaip?gQ;7tFJZ1AMVy~7yBEY zY}yShJ6Fp^Yz^!)n_YL5zWu1fV{VD%9`MQ=mG4?flkoyJF~!8x)clxUhC0tD?Q#8C zc~QT{#`@H^+wSMy0ZJf$G9+>uv9ZKou`t40_ajx$1D#g~jFFQ?nbNQ<{BsgLbrOe5 zC{329TbhuguPVO5bmTvhNC7u8%8gBdq?P?9lxRtb`~*nb@1sOX4X0HBkUs%gQ-6?v z1ThUD6&5yQbU*7(JRzk=RJC?s1*!>^5_YQs9(y|BP}k19?sBgEBl+|#*49m4yG?%; z#Oc%fiQ`2F)%}fq1*;g;z>#Q-5-jw>_>oK6e`Nce?uO0%gwK?nr)dbbV!aNt%4Kr5 z)$S%O@y-xhb6ea?cOGquS2NNS+V*g(cJ1qGaOL)o$~o$!*s-a)=MjBOig_q%uoAm>6kJ90L7-?v$AOB8t8E3Z+ERvxz7&ffqkv7t*@he z&1(5%?-?C+Qr&`Xq9~dcsbOp@QzLX_WCbBbTl!AwfQ@fhL}=2YG2Ne55s8*ez!x)_ zAw?|vOoS7wSf3j1xiJE=?-d~8m&}kt;<=CDTwZa9<}{UD5c4!yhRlWuT`7M(tH~p= z$!;kUeL;qbvcpzj>y%qdRIw?hS=Dg6!al*U$uW8C0ym0t2hceu3_mQ4Q!Pg;@1;h* zMRPltbv&?D98^{rNk72;=%N;G9^)C;wFqy~YD#Qfflpd5p4O`_t>vGK zjHfyCaj^fVrZ=7YM#NBj)J*-lU%lyT%8pBah78B!i4-pwZGhFNKULKjR@Yv}>E-01 z=XKBsSfaWW)x-fAQ6FAI{ngfeuR`UTLaB|#KmUR;C2NOwVw+?NUU_gyUFI;6bFz>4 z91yQIzgu7Lqn%cpj$skhEAeqqwWXx?theNxpNh=%v4)GPTq=OX3uxKpF#Tg?c57K4 zyoS=DU=0vGACAv^w^vx{$`$(m0AVIT?)iJ^8B3euxlR4?n+9KjU+CiJegR%G3`3tl z0nU~XC*d_vCAd6=H?3XJB~{S&4sy0j%zZiEzLh(S+0I^!%q|mAEKpp&pQQBx-n+aI z{%R%KJxc?CdgB&T6Nih#kp7H%$-?3%R}F)gE=}nMjJ+-s-f_O$0IxDCHs&37Fj`DT zYThWvcI0dpN^4~ntO27ndoE}9Nv4BS_G#zr7>SbO^TtCr2njc-5%J^Khtjjtm#R&p zt1lw0M=kMIH+#U_T*ufrtlkK_g8klnh*0fOsIbDycp`W81LRJ}{u5~X#8Ps*9}J0b zMqz*xm#PG15A=^$Hw1$ax~x5AXaSLp66rFKjmqJVCh_3V-7~Wzijb^OB<9a_S|ysxxI z{u&AkH)I?Xp2em7B5L0MX2K7A*p3{0hS}yyq;a{x=lzoV8<~v^_L7oaRiaCy>ReXD z+3QVC!YKDg6<2ptDds|r5^WVES3cX(UIZbdOL@b)(KvqBOeo)kLHuS@V*^0oEg8+z z+7*FEh<0*s0bx)Jie%Kl8ZXZyTvTfaF(!B&aAmX-GJ6Ypz&+jI4G#r>5usoiN~j}L z#$}DoCDm+-eM5<7g^S}{8|=U($Pma9-M;@Mj*DLKumqy?2UsBBkKI^V=6-s;Od__5 z=gHcxm@b&39Rh*5YrCTIm1UcbO2B7KK-tbVc8IVji>BO_lIYCP<&>Be{zbf^Ro_|l z>h|P`+IjU5#};Y1UiR{|f?wTJuPI8~!>9747uF(w6}Bnglb`XL6Mt# z)v|=5o9`+(q9o@6o{b<-!g$_tfR9a~)^zlKj`U!J@$OOND0;j|Vzvd9P{1Pa z^io7?-b0q>mI(~GVUyJVneApMqqK?M3fA#k+8|7E7ciYM3_|+2IC81?_n_BLxDr1- zyhbinydVP_M0$r*|0;%C)eYCbW#LjAcQ1^Y>gp6?Wox0)o|4jYLR6fqynUdWeYJ_p zK1&V-W~eGXIYRQ5;wIBFW9y^|&3dzV9?avX2CcuZhv1Cr6tT7K=?rs`73IR$%B{yE zQX#Q#tSx*Kl}uUW9i~@6T&lcV@CT5RF~b7K=QEt;bILS2xxKP=%2Ev53oIv|zQ&1T zl*^g=D(LvLl+b;6rb^|wc)ZD_QNqb%^S}cyY=Cf^FSSswZOmDm;LELe=#fud@Xsaa z>|s&QKzUB=7Hj(;3=N3Jj4h}#SLqlz*t=B5ihe~5rMk)}y~3C{Hn$?X2_2t_MNk6< z$ZB*Ut(KTQDMIc`@VWHtjfN!ihlf5#=@T2jL{>Y5i9)!E0`t{9bnQWqA26dv+u=_b zIz8f|_`=yJE9@4FoOPESt*#i1o!R5p`A;oCNAyF)1W|8a%s1zLMKZ&6gWjE0)#~4) zZJ0uvX@UniMAFyq7j3TQ+)`}ddqT0Zq(3Y%9G9p&_@zp|DM~1<0RM}lCbc^r&=_(E z-kvgXbj~&B{ByQQG9#PQLn)6C#LC-3AN%{Q2nURgzpLV>J{bH<=9%|zlwfULKve!I2p`V#}m_1kx$?*;%of>B(U zkaa5s3@^E^Dfes*VGs}`8=|17O(`{d%P;Gjc}OsF!H^w|iAP~7my{OgDjG&EMYUfY znkoOG?M3Or}tVyu3U?0wIGg-M$eE_sxysv+Xbc8m*k>WYMGycO6HW zsI{xD7)kf#?0#xQvu#i!z0xXb47(A5R`Nt+ zE`qh+3Or0D@z#&Q7K0lk+>VX=*FHpJUT>VT!pG<`OAtNhINBH&R*YB9a-T^$@c6o; z>=GL?J=Gd5Zuqw(UTlJgq%wKjd{j00p?gJs?x%f0(=$nRq!PA`=u6z1zn`DIh)PA} zNR1EMzj@<^<1*Y6tMN*deEpY3)}T0yQ-M_MR=)P?01;Db-28BH=!)}e_`)EXYL5TO zsgA+VQ%n81zeX~AH#~ZFqDk(c=QJ7a`2?p@;CNkV(yv%_<(Fub8>RE&lc*?Ubf@l->jUa=CTi(>qRlaX#ZOc# zfA(_YmutdT zH%1F$J9K_x9g|#Gq>Wk)UU5=5rTW1!=r0U1Mq;jyw@V_p34Kd*&DTHQ6!Xc5G?Jyc zcwiBGSq3H@QfN?pbHhjRiU_?sg}1>ywe3%vD?7!%ySF#`#prm|w-z-Hyh8uDw*LOY zU1r()<&8ExTQ&W@5mV_lBzFCU9BkIiv?cOcFiq%EJ0-;DSQJ8;<(~F`?Vr!cK-c@h z6Kxfxc8hOZoH-gW3^!!{X{@xJ=rYK^xI_*Ru@;`I8>~|x@%d+j*qVrN2^%99ib^`R zByZPW9z~c! zAqF*WI3KIb$6m2FPjx7G#VK6U7^)V;RYU>`pHD)<>Gio&Sm> zffc{|I$z*+8-qus#n?Trq;{W!C#BG4U_Y1!iPACi0!_; zYrV(xI6S9*!}yAaBN3uAPZ=dAMuegw8J8nNURPqp3zsXweS!K!9DNihksPG8;H5y_&byxy~bRa$oUL9RL9q?Hua9< zQGp8O$4}pOM5W|g4STpF`HVu`eCdwOPx<5NC@s3aP54HcAmq+5|3%wL;1ppR z9=`rp#OLMk#GzW#OXGMQnyGSI{cHD-l;@j60BL5Xil|Qvs9iT=e(Fwmqr;AW6!JP^aE8#7X*A(!WVK1kqVg=XK`i2rdlOBR!5P*7Ldx%$` zi`czshjueL$+a=M)?IKcFQM$~BC88`G;#(OQmGwbidAsZJg~QeMCj8q2RI_%pxu|DT*pkB=yQ>@ z2b)v<^$+wLKPA;-yxu1a_w^c&Mg4}vK15tK{S#qU7i`5C61XF%cqS>ZS)Z9XZu5P$ zu}c~>MBLeu7Us~=!m;!!9xXLU%gE-eux$=745v%7?Wwm8uvgxu>FT+f9}vk|rj0$g zu)<5>#S~8M-H%gD(#+Zx9X{Q;^>`fk>q{GdWucwwS? zrl|~t5hRlmXVHYg29vcQ-`pBL>}K7H>+Qd-%U|?fO}wQIeM?L0ZiSd|w=$e)q)P4k z5vqD|V6LL)#dwJsvKk5y2LeK_}a9%voHQ zI0i+G1n<&rrPLFuy+@w4&d{;RsMYT6so}+l@T%Y)K~Bn!SFXJB9##Q4*x&M%UG(266-AI6+n!q>!T#)bm+8?{tGJxu>a;ZdHGrZQVK%iC-Ug&liRAt6yhZ}|TB{C;)$h2bb3z1hM%`b9a~A5np9g98JXv2RqE znPgB)ce6~F8ZQ|`6`LtG?WkaM{0@(nuU;Cpg%nhEgVaLr~0=)UKRnaU5%}TlYm2}00yfsRnsLYhh zsE{}2h6{}fm7Z^(HyQ5%z<^UB@->@HXU#%a0o|=L6q_g_zl)1Y>oR~t96KlIQtU2n z-QIkNuAkj%dF=^qI{;0ldF0-`|7WAe?@HLd#JlJG9)pby!a#ZOtw!udJWJ;4coa!e z+z~fjDn8@tJRP;TmIxrrE*9mrd-Z2LqEAsK7>OX=bLE-j`8`ZQb{6%?f1>#MGn}cu zc2{fU*0y`*PiC&x5kh`gnW{tDcpqd$sx?su>fiJlRuzm_tk(jcMSjQ^2w2JPVpNm#(sSb;TL3bD|hRs zuAeV8*)Z^K?|QR{dAgSGc)wN=9D>MFHQWt)`u{2WCZo9tZ*JwwD~QB9l?3-fK;o`a zQJma2n<3oS+`s;Kf3Ip6$M3wW>%1rG{Mblw<*L$LyVUb#1ocEL;$|cAJf6&N9*3&m z@KFk;xvf)@_UV%%;j@H|_6E$lUU62rFj4vsD?>)4MY#91kY5EgNs)Zg2N619Yg{~J zWI!{HcmQhyK&VCX9XPUv9tOALrC1ZIE4rk6lgsBk)bvCO*j+Nm*f;2ki6Oy(ml+t? zWaRwxUrDzYM|-7luUW`bB^Aa?{H!HiCzwiahjOtP3fhSmKgib=5@qAa-T)!ufSqE+ z>Ta)h4gBUJTj@x7ff?J6my`j;X(v0T8qeKw5i5ely(lCSg4OX4=4ZO0QihU2Q3RV~ z5bfgU&TFC#;VLR8{;S|v=KVFReb6BIX$gU!o<}5>^n=1kokD_pm?3cVF~pDa@b;<# zugP4mqGt(EtprgOmT@DN%uB==@fB2!vveEdrg%o-ER`iow=$xza5mTjIa_eMb947% z8)NL&t;H#!z>wPlmD1kNL>ougRRHYKv`8*yy_ndgkIpv{4w@E$CbZhNagP z9#Hu^K6D0zY!x=T*Zv~0g0;tY;Z?w6%n}#cT5b(&Q7Ns~DOk%UK!|w*n2}T|?+exT z%VBaBq4CnoeOny(JJd9S=XdITC%FO9v$a99eldBb>|KM$u6Q$#2--{n~t%QOm%twK%v5Z zpjA&1)R|dNY*E?&J^2ch5mqA&$x|oO|GV`3@#RWf1V0*+2x?I?FG~?eJXT2c^t2pq z7^Q3hhU|;A8cp5>XA-iV^}SC4N>mP`%U@c%BUmXQfiPQ44UBUhSP)>5np=vAf)ZMh z2x=Gs>WWbY#pb2Y0Okcm3JMc(Va3+;80@vh8-CM>63zT8^_`ae1<$Ne?)&%g!T9;U z7_SxNi|6N(03@@qwhkDLB0)b7SpWXc7GM)AXv^jK+67`zZXE-lTm(RH6#YuegT)qq zmB$?z7}Q$!#MD3>03?9lSn|YI2M*d7Hr>RFjZ`^=4di*D#p{KoY4)FO|6UpHD>~SE zr=8^ySY>;R@>NAW{ZzBNRbL+Oo^Hdoe;?Qp5k4fl%TCI{t?SZ-bw+8zSw{VT&**Ka zrpgMP@do`9-x~7&f8g~k*I7C(eVv!r+xDJ6)?B&>Z9hJ))%a84_L z!YA~{Awm|SB)0Af688efbV2{-=d8iG)1XK1E#Tcg43iyWZT|VBM`C{BgJ-3ERyJQy zPMU{=ygpbD{6!Qss?caL17YfESD_0)T2Tj;K4_1g(cF47O0?uaTyb@N%m78{cDnr6 zKLBAK3T564i5Fa4p5NSmKT-_e%E5kaH61x^j5_;&sJiZWs{ikQjk3y?%1W}5QDm=@ zl%0}IDP6Kcwu?f`O4;k)21@qcN=8{%lszI`d+*EjJ6GfP`TpneaNqCuYn<0P&+|Ov z#nQ;OO|gI7-gbl0bG$v@JEr-qSSwG+VM+f=g>OUWg|-p03d16~8hH1(|O}xwgrPn_1@1=)pndii>0AN*t6aB%;P=i1i~{b12%8cvPpY-<=l4s}6qe zXt_>~X9=_+wk}`150f>(aw#}@ph3$4?^SAVHjrU+Ys4BX+zGd4&-jUnFL>rpe4%Va z_9mas*#9YsM7dXq&C823Ez&w3BO4KtpG)g4)9o!F}`#Y{SR zF_&j~jV?Tx5f)8cx`m$No=@6uaeSt=j!x;`8IP#Dof)m0^)mdJ-ECJ|@1`0$%+Tm< zdV}n{KZ;d-&2ihP1Id|DqTuo=OUCgtW0lXH`&0V$94?AQFx;81I99gqsf3&thb-z7 zS?h7e*#1@}xj2s3+ME$FiY|NE?0b1nGI!8czzRS&>NQ38VKi#4Spo0!7<@u06hS#Z zj9q9v>$!q|Sa@0CO!%fkx%+z}gi*Ui(H6Psl1e$RpQ<@|n&)w?>L8@RZVb*;mC=tUIq1i7IDVhK#GbCKXW<`i$QTgl?7 zeekY+5ox%>u0jyLnyx9LN(=w=wmCr|u5v76lBrwt$_9x9K1)D9J#r)5ZtVbT0KZ)e z4NUmC7W8=#*VX-7v;Nz)W2x!_yO-QNhZ?lMw^rJ;q7crt2sf~4r!!TLC8NSvCs2nH zQDLXu;BXAixZD=qnL;j4qULUw5ObxgGFve7M?5GwwE#<>mi-BilUR3qlesk6M0K8C zw-xP7U1z6TZ4OCz$D55oj39{w(ohD-swP4#wtg-H6D`hPu}Le!4Jp%R)6e^GKzG5p z|M9`VinF%9{$jKFj4#XGP1jGFtwNozJ+E47P*6~`-eu6r+Y7z|rw; zp92EiszHF$pU8IZRn{IOwu(6Jkw_~BUK$#F4#@M>h6;kal^whL*98^iK=%eA(K6oX zTL2+xu!_@Z*eld)#0tP=fRs1Wu#i^%eQWLUR3PsjQLxlZHQ#5jvoD9j+tFO{N~tq% zzyk=}98vyIh}BP!_I;a~nfZz5d%8Z)@r^UMbDC5bHmSSEW;rIKxyk%~%9U&&%6vAR zE&O`0yYJFIozkmEltC6ubAYt8^x=M9Udc-}N=o#>kEdndw7x9k8^jmU_~?P?$M(%T z_kA3!H~Nu`f{RL#nr=s0YQx@I(P4TVPvG7=RS%y4->WZ`7t%*&XW!?)xW{jpRj@%n z&hn950`Y~%s(OS(K;UnI{u!tfI#!d*=DxZg9Xd-?3sCfUwXCH#;bl~-lOk68?s=s1 z+NR4Hlq5vOvz;ojS0JBYyAnHW3C@>b2QgsU(e4I3k~B}#z=6=x#kLG>e786pnTxz$p3tYreG*$@@7+0Q#-wnXJ&5le60T=Yp3C5a5;dy`&Dri zy*7EXk?Ddk@fZovzq-myr+m)nf0T(spehyQA~~-q$}M9RY1W|-Mox-zBwnM1R*&E3 zgYLX|xu=Q`)W&VGi=?iXvNg=OJb_$4!XPq$C@n@>z()-t=?=MJPXnCInm zII{85+=`Lryi10|TB!wLCjUw$7Q(k$Gbl;>wVo$XkFv2$vFYHdi`)k;Cd|>K=%)vJ zKX``D4*i8LYL*(%$ZhuW#g*5d9YWUxu)Z>P1VTbG=@Vv zR|P_0jlFU4u`r%Q8i33ZW6oEadZVf@d*eYI@;{ID7TPiOn4x1ekAjaOI0EKJC z@$yb-9T&n$nzp4|M|0E6w-TY4ki9_)-yiM=@(yKWm9Qjp$wS1t#)Ni!MiPql1@tn| zK))|6DX&>UY*ib?fs0qmz|PLTQD$pWc%tL(;_oSc87AvV(-H<0#lWUPgTW>)jLTOA z26?%|3CXdjX(pS1K!%iuecX6+qCgkRFT2}^-Wzq)*M6J&k*Q*rZ{I=P+O;Zo15=oa zA)5%P)wPU)pisPr#`ex}*X?ff)6Z`Ae6h8kjfw5cj_oDAU3r#<#z1ST|7Q%O2XLE* zfd*9t7xC`vhDQMzgiwR8Q*TMwE3u1u-e{|2)SUt);DcQn(7aehcc8FJUq%ih&9>N= zqQ00ACkVzGPYL3K>z5W6Y_6|=)ZN{Uh?lfm*zL~t9$8Sd%pX)xy5m?MM=5@gV}-|C zG{%VsU<_mD8x!POn1hKy{@z@Hj5!WFi>n-=)a1(TAjc&r!6^^ez&C)?zef)2mDz`B}l; zF={O54lVSr`7-lKrtXc77{z~O+KJCjK{$0CTBW$1$%D$`Iu&t}*%7B52Wd}Qoo(v3 z>zm2B#63<%jYFwAR#6BzlS}CLuFa<+iUTJ}Rz4sv&+tgg$~r5`hl+Pw!7V%4TQt}{ zz0Pbx0P39(E{r|_1$KMwP6OR6i}Vsu0Ao!tx8|eni?;pz(LGxgQF*I4iz~NSVj1C8 z_adm)$=B@T!29#1PLtAj*Xb@EwQ!k(Ts4byR1j!vv&VSb{iNU+9IEi`31IFVI%y2n zko~kB03Yc*V(ECj*kQJ#p_1Sm zq0X`C>m0j#1sm7F*H2TfJwGx%**d6t`$)~p5NkJXxC}Hm7L0#dSr$+mMZ!qdL1+th)ffRs&oML|9Ste<5r~8RsPVO;c`)FA?87wu6UFK-rauK+swqoYwpDDdrX-<$q^4K^Jn=f=!v%51+B2hPqIztyqTUkFi^F5brhE>?2D)AG)!qhziS$*WwTN7w7 zr|+>l4j=bD=o%=pQC%FVOW{Z~zkPtne)jeDo+-{Y$}fAm9uT#+)70n>9&=Q8{#(O* ztT!!q8*1m1AQzBWO$b#^#6~8a%L;}ZRdkY&1QYh7MbSmURm^>D^J-WRPGEc{UwPg| zbP{UGpmsJNiB@8S;Bc@ID)mChAwMP9bg)|a9cmo4Aeco z2DusjcIN$8^Ij?_ezYgs$q2V9GVAU^$(CXS=RT^K{ zI)KrL6gchDOI}6Ma0QgWmJOP7f4B-o8@N|DcdfxIyl57Bs3jqf?u=h6^SC5TnPNy* zjXj;n_}H5(XuQ@=*m(`7_R#M6xXU+db>%A)mBuu7zXKx1sr)j3M|WCrD*B5-+T7)- zvdf2qii(Tp*KKrKZ>dwl?W=P%mUwWwVm*`$Wr%h&0aEBybi}y+Yc#&Kk_W0!qLL%~ zh*n=`l-SqA%K+mSNvP0EtiQutAtW|T z3)oe|@+GOM*za9PZ4cy$N9xiyJlc4%119eSGAwo;of+U-E>A}?Km zw1lh~!JVimMmO9D5DlDJ(-8T4Df_vSGeMhuFou!sMAITynwpuScdRU*>r37BzUYHZ z?hY1ykf5`s?>80AG*oNJJT=IdwLEh~q|qFA972GIFTboZMxOG=vi#R1oPLxk6FpS% z3E&2_Pa(Gd0opmWlc~RuL}L1tJ(beY)A;6Z#9mNUNNBP!+JmDftQcX|WiFkccTwZH z1IR;`Ftm0~QbvdAgZ_sa&Bb@Sn_HfFBmCDRw?S!m=JPcN&;0!PJdQbamTV8AN9KF5icQkzhHfQs=`A7?DWeW?b02X2jzZ^S#kpiKw}+O89;r zzcrZ*dPvc~gXRV1lH`6KgN`p5{jb&K(fZJ%VgVI^^~dGwc6*{45)wlI_MQ=S9BYhv z8q4%^57b{-LHzgsN?sdn?DGpN=k%z%C(QyX;a!5(aMJHSwgR@=rkuF)(U_EwHaL_{1)Jvtc>bN$GNx17XI+ zZ*b2KOx@EI;6DH_H>c+TpHBQCb?va$S?`&oIJGeJL9f+DkRD*ar;C%HH0<5v2hoNhBiG{I-qG&o2uTlkWT-rZUQ(4XGj!xyzuXHjcEr-{;#RPTsO@;?MrU+%j>q}BmwA~6o) zccOCLDCQi%5;CuJG<0X!g=8vjP$V~AeA)MmBI!mly>ftjG&;naa6o4TawrQ9f!m*73Hjt>k9(tu zja?S|JOQ5o%RPgNc3*?K(bP8(M#UY2{UQMd3(`-inpmp!5s2nJFG*y|z3!?uF=Oo9 zeyP`|3DBAF`fPXMF*q)hP{F>KmA=@AE6L@@Kz92Mz#^h3zyc8I5cd>-wKrE4gZGNC z*k!bL*b526NQ3 z&TFNjaF#>@AR`!h=*7fij z*HmgPO?`r+EF{e?=j_e^AYG?gRV;@*6Wb1)6O2B~1?cp+d~71{H%N2~NVfw1R2zOf zB61MtpA{d0yC${TL|!)!%_f>20(Wvky*t&TmTDYee(Y_0oyXWNv1y-}cth5}k^H$! zSR?O1Z}ih1WT+FU+I?O4eht6vxCppj=9RD4gJJo{{Ku1itx7w#OlJs$7rdLzpF2sI zBtsaA5G~e~iEDK{+G0pb%%>BJlar0RXW^?ivXYf9SNcjd?`-_YR1{YR*;k z5p+aN_Rro}k_IqsYdK-=HH!uA@lDm$?I}oeFz1s}}$&L@>;P3X?s%N{{>ySkK z&>$9QO3x&=D?=xD>q!upd&C_PY^M|a`%^2$MqNTS$gKZyQ6k%8laB?|%OGEgC3bkr zQ6v&&&mheZEA^=vo_$KY|AgSKI~3#lpZz&Ow3|IYh~U>XoV^yPReybAgS7f4U1H3p zii*fdT*{rf(4UWtQ{Uwv&XD84nxdM<6pF78U5=w@do=)?h%nqcdj$<$%yd$pw&62^ z+bUv2`O`JLJx#2dfZ>!*if5Wsm2-WVZ=aUuHoN@E?69mY`Dxeo5*7R<9x=zvZ>KFj zKAxKKZnSWLsCgjlUr6+5mPZK{bf=RzY#-#;-^6;@4-T`yDZUn>A@*KcBL)mtWDCmq z?nADG?)9yVCQLxNSZC5%3-YndfQf#{;3NL$m@H~8GDgw8Sp%)qKH7SJOLMP9nE-T# zK^zvgdo(i47pp|xcB6o1Sg>(-A90=vzzagYdmJ3sQ=l}(TBoi$`B~lChWt(p{pC5h;wFz+y(n|x##$+j44&QY`;3eE z@kVKiZNhd z9dY$ueKvyV@#bN>ElP4l_Zo9{bJ5HdfylgKyH~o`*I&0)y1#nae&{yVJ9}N+Lj80i zpG|zKjOWY5T~A*+x&r>M2~5&zgsKWPGTc2?V8z|9)MWG_$}Mt!{EsiIbmNMDgK$udxoW1))?fXrbIW7m5#t>oQZ>Tc&5o zWX-luQy^52E0etovDEMP7`9@Pe1Hg5zw||he`{tx!hh*->0G{z3tG!)-N=0||A8yo z%y1o%UrZs z6x9EGT)@V2-^kvAy$pe`i2BXtx31t`Y}xhq;|flvzn)ER;=fkBd`;?wK5ON*%_e=P zS)pXSCY;ZXScjP{KFfhIxFH$3V@Re4lA zTz?^3-`0<7->(H9JV*Rs)cC0io0>=Tm@5xj_?>IfU2ufY47j2@G!yXs+Q_;5sx6;+ zHISR847^Xw{W+ITQI_hM>8Fpz6~)PpA_8Ia#Tt{&>o*@B)?$(jM1)c&$~3HiJO{St z&$-}0w&tw2%CKb}=up%3(v=3b%NHw?GK|JrQ6jDbB{&j9EE~yD|0^PDRCMt|%5}uOu(<3H)+x0f2fFJ#1BT@A z*bL|JIi{i@#6cBjYMQ#eX-MyYqizo;e(B%hhKjb zI6@Ohq1AneRHCttIP8q`QJtvHB)r{?0bEg(`MOMz>Q^00<9E)9Cl|x+;}q2-N)6B& zca-^`DUCxHe|XJBf$Mc)ESwTIO(@$03>+9bV8L@pjV!K7te3u-bU}w2t;c%Zv#Ip;a1#+8t zYi#-KXi1KGWzbm?$8-HxtC9Z2`E=qLBJ?A#b`fV#wpn?k?X>VUDh2iEF0pNM>Q0Su zMX7Je_>4n+X4@nbh!moBFr10``?9yAXLdkQ+Uk|$HTUDbpUlRa4)-H(!J0Rts@8A0 z=r0{pS&j*Ii!Ptbua7et3713&u#g;8-j~HotrM4{N^Wg!!t{J6Z9yEmB|a}p%2b~= zPbtM-hT}byjg2x}H_F?BW%}km?Q%c=1s9Xo2a(OG5G9=`@u9XdEJLV>6U0IL#U41i*05$xo5C;Pg5PM+9Vq=SR9MV+7FIEW!dMpOMBX=HAr65h{Unj>{R_*Y-Zvh8E52SDknD;MO~Uu5f`pmqTh8UXp$I|> zwufPlZ|{WGr~9}ssvSl|hJvU+XPc~%4-s!E1GGg1(gLy9fmI@1lm6J9H`q?Gd{vQQUqw415E=bDsI(WWWYFK&!98> zhE5*ZdqKUxz0(b4AeEJKE*W>`T$b@*XTV5+gx;q4u^BKz$A|bS0>(+DJu%Q-vm5Nx z9F^u*RzDH=dVTqQ)2Mb8P*b2})3fpHwo@HXP7 zYCp1kD^}@M$<`hF178Q(BG_UhxnlI68<@U(B8bAF0{?u;w951Z!4wcu`oy|QddB?8 zczj0w+%njT%=DZ&7jYFA^sllHtx_QjZ;kONe=dTXTYbtfVyE(dUa}i3YD=^l zJYL=iTQd7freS31xHfB|jDg4WqXFK@{+1dZK08*Dqej)Ld&gGJSx(J$SW3S$JiRt- zEbI5#1^56{dyoBQf6azTf8Vse0Z$zl6T+W?vMtA#Rpd}zqKvyVpQEdC(L$nS_ZB9R zC)X2>T1<9#U1GHaa+rasC>Rl%u|0Brksod|IQCeGJH6&$dItL1*Gf%Lp4W>Nr<3s8 z2B@&<)W(f-`Z?!ZWv*>|Qy{J}k{rD*l>M$vNzl(8Q>cbpvZHLJRc}>t{}inu;ePA_ z3q2;v;p(NZjr_yVW!%3{CVe_Ly@@292CtERpKwNh?STGxae6^S`rEtm`bZSIk!Q&4RBCj3}K%d@N$X+_cYYW#J5NG7^2%9q>l6*F5`lf+)4<(~$*U6bYYq&GClLNt05Ev7 zOoi7jf3X-xUGxx-z)}xoU`BlsO1-&Z*gxnoEcRMA*3dOrO9bL$yq)>{$xJ%P1ZV#9 zG`Fd*)3?DVro)A`FKAV9hb#)VsJ`_-px0ONzh2OtLAr=M4S-e)P94UO+?w+Kruw(J z9QNU)3z2VfJ*6@|C5ejzUx{k^!9TsHpn^d`+2(nT9k)Rp-mSYgtO9wN`$>+T-(K(Z z9IBNcj<`^gVs3QvNT;mUb2<9gcPZN%UC+#BY+NNS?3t!RavIa0i+xgwZY-BY9zr^f47N1_zdf{Gbzi=>X@s*7v=^mvyfX|ORl zuH@E1Wgb1xx{3hEk2Jd{^JUUqP19ujyVyrqMOlPpFQT%#AN|ZwatPk7C?Vrc4?|>L zDF5tShpq%N zdU%%6%^#6@${_0yP4yIb>b8=E;NkYI91xbjLt1iL4B7CMqESw8=p8g9;p$&y9v)^~ zdw|G1hCl8cv^V=Gw$pZ)0!5-M|DC-e+{(4>Nk`IqUwo~OX5;6x%Am&@-7vC;w^T-hJ{aK4$Pb)H@PU=)N>`pOg2{Gq!}uKoe(%g za&##!Uuh;Gvp=xy{#Eyjlp8C_;3h$w3V@|L zrNpSvKHx|A)={(kL^$64gv(MiJqed7pxfW`q0CY=^6Nk32W$qSGaDJ++P?KO*FE^( z3$~BGkZm8>Z7@4sL-SO4%L%!XI8<|L=e?N*qq*=+Day8I^zm-jH|k)pL=WrtV2wEL zc`c3q{ANo!&fZGTdD*Us9lH`SN0?K*98LKCEQ~5UI){zi8ed#H-JFy#A_`UbANlAp zB+9vzZO_9KEXE)Czqs6HVXr7y@v=LbicQBSU&fFmbi!1Mkd!l3s!*|0qp$ zJIveU+j|u@8(hx?DJ z82ztNIW#1Ebc&9bqp^6op2@#sZRCPz2fm8`P8F1=hdnn`;1JmUm($ z_w8l20JSoFxDnGzM^^VfwW#vr4HZ0bN^>Ecm8rK!mcMiGC&^ze42w!3yWf9d_$%XD zAi}=5ll%@%^+yIhs{?><6WFz^hk$iGcW+yemmzqLgqD@H@cGgWxu31_(^rr?6k zAm`+L0WA-Mh(5dUNXMc_F;!zuwq!{PmgDS~Md#^_B$Qb>m3v;^viR;? z0CZ(~{*rsUFX1HvIvFvJIO_1I93_z0 z(U?fl?RIF|OvK{9SN*U#S#M5h@>keERN=7iQxPKxPqQzh_7O_+PhI6>PgZ=kJ|*pS zr$}u|r~Fn`&ry-k*1JQF2VLR)9s+d*0XIq!s6jQ4jKCcj{wubw;2;ZQ7<_2DL3UPw zBJ&s@)gR0JJe*1#hrZ2IczCR}O^sq7Q37QeO_uKOw;IVUsu2gr$_c=7wKf z*{R?s;9))v2Ywv=ac}*j6Pq@nLTd1ft7OJ;Wn?xBr_fD5*!FR2pX7yUFgiJAu2bKm zJ0Ant|rl@w5&GA`i&Mugjx+oXs!~b)MUKgwsxaenvyrT-0R+|~WtC45%PQapWI|L5~4!sq_&0@7U1gBB@-)7JfuUcM|=Ymt5~jk_1` zGv9iiBz_-;n~t==Z1M7qRUiv=d1+~B+qdrFPVSaaBO)Jy1u5BV;>0wGckqlg3^=_k zQbQHW`1C)zdaE<(a3P<^Tkkf?b^qZHq!%N#O+?Z}Ke!*BPE-sLC(+7cn*Y7I7PS_tH z=0n*gNe^QB;k6p|?@!7(9S{Beo;lN&m#L8KOtE*84}gUQIaBs0C>VOA238(d?h6>J z>e;ROI<@!JEM}y7hf0!cVGtb^;>KxmYvT9GNMyZ37c{B7RsD-7ZXQ7fS!jTSavTE) z^#ULi7iqyk@`;VhHR8N8EF`_-nF}pjzlt_&;kH#GBm!0nWJ$%pXbpi9KIic!kb_6L zuVO-+$n+t`uS6!C(4t$N`oTg}v32x&uPdaO$oCGEQtv)ZT43+8+(f9B8xcJLQlL0` z`@-c8m4N-L|Hg+*5hW>8H${n$?S!=7p>d4NxkYZ>cL0j=-GCsJb(&v~?JJ!}tlJOZinsTMLPjtZUtB9{EmS3WFZS*fah;TgGq1NtSY zN(BS5?wT zL0y--6-WzajeO*%msnDjz-%nj&?xe0(S<76oFHo5U~ZT)nJJm?V9W{mU%go*S)brZ z!H&Q`PcG^-vign@=OEaI^OsO4xZ87$Xk_=Kc%1#oRGz>kkNUzZ77kB2%e9jcC?OM% z_&`Q_jD5%l9k{RbX*J+z1PJh-)lCQx?`vJ;EL1%2(Of7aF0a?yvT8CPBhaLF^}gYs zCpGP^KUio2A;~fLD@DJ?V4^ge@ zI1%ZOu-E9^X9axpwiQga`@^r~^0$+(u0EFBQ*MVrAmLS|&2p$V^<6&k%Bt?eg7P!H zfk!w6IatY83}^Big>DAT+bsKu6w_QgeJe2SvH%H5Z}ccL3w8A!;cp?7c-=#?*F>V>)f8TH%4e#(0rg2))WE%9$>?qNS&_a58?5t&6Z9+vYdyvrC9+0C#4X5NZ5xLmLKWN8xY0OCAA1iMHMC^wV8HbaVI|}7*jOcH z^g074?<;n4YqpVXkYST{!6^OuPT7=@{1_PzM| z9{(cH;Kr3n&y}qw(b`olcf40kr|>@>ebiU{YK$ko|IW@qLt2vF{TI+OLfd+v8$ksA z9ESU3_uubJc7oBkCgRNEUn#jzC0FohdoUEm!ghb|3@pBP^8EtGxF?4$l{6+DE!m)=-yRBc}o12^jb<>;Kgo!2xIN+A7siAf6?)4bs@Jn_T+74~_QZ)`Q|mEFM>PjtLkgTl#*?IXisSjh{07 z3tw!Rs7>>uFUd{k)yrM-d(4wqHx8K2Nx9j7tO6*kAyt z?$<#FP%=VKPQi&T8?0_cxr5f?orZ$RrOW{ z{%vXy0A{PIWeFRvSn%BcDci#`WX4U2Jd?q+4O&B}yRiv=T&&f!E~xmOLRq!X{t>6l5EX&ZQf}+pE=|9B#!l< z%q4A8pA5zUqi6)GSJfjdu$>;0`F(D|bT|e$4U9y7s!l{I#j5B(8Bfat9H+5TTzxUz zkVC>QYWHUpp)zL6HF>_H_v>^=oRwFBu?BL;Avj=vNfLXZh-FRm#8`3dyZn6<%f2-xCzzig>_cB; z6IpyAW{05uXCdw!XfwX!`Brm~LEPc<`{54(kIVKQa9QTcZQNaEA)sP5S4~Iy>|g1m zZSp=SDSYX^+%q^%H)<@Rfz*)5J2VZJ~A#T!cvKv3lU< zKu6})wf@sMJVOU28slB0TNZCLhLAn2-i{V?b=(g3wyYjHBJQcyEYdtN9B+5cn4L}fXW&eAQtOiHevb0Z z(F9r$M=9}F10v^!C58`nrM{=bJf+wjw;5h!IHpXKzkDrhf!Orb|5_TY{oBdm5}e4t z0mr$D=@Vh1xPqRR{04-H8`S&}!jAx3wM^I5h{96{y-Z`tsMN>GXtIBjvfSi&xdw(L zuAt1Xd5Dt>h{nUnRu*a{JdDTs@7|-ciyQW!ZY=^Oqv_#EevRCkyJBQH5WNL^f1l`h zeFeLZedjt%d>P&WSs|!KSkW6EW6)*52s;9J<|?lH{o}KQw-FKwf%~|a{=#{5RTF8D zlxc#brM*WA9X?Y$eIlZ!i3bBB)@_-cF%qm=gT=$}z0MNJ7O%f#3(*rma}}YF=5y;2 zfh9mhMGFSQ$(J|Fjh`mGwCG*<^wUy2 zkC1!wJahU;)uCjL2YTezO+B6`)i#cBflc}+I7VZBUd{0zwP{EbX;Y}@ON*?U>%U;5 zH_A5OUYms$y%>2<=Yh7>)oX@-t{Ofcf zQ!K_S-W(Bm{H71P>YyZgW9tF)uzlCoK?}A)w=it%SrVG?)axS378=Z)>?DGu1-yxx zwK991O(0&`{so59&tD@4L?VR_xNB)+wZONE5 zc4t1Ju104CCSU*VYFd$mf~jEZUWf#?eYU;;&2OvNFkf|Kx1q(Q=J8vqw!6#WDcF9O z^xvS^7`vE|!+X(VxsVYkhsRm8j*?9uaEwD#Y@|3(KHDn&rwG)X)Nmm~+j;-!h+y~5 zl&0^*wGscbHu!srN~K<_Q|;k-i~ONWIt|Y_q9l}uelf^c2l?6ZX9Mn*6{g(wHKvw# zFifkryU?i3`!;P~EW^vqhPMy2bR$rUfeYU$CypyO)hP*DZ0HkFg&vguz4bGybUCZ@ zM@JR0S%h)C>ciBDH00`80EtR85XD~rE1^ucjb)>6PlyCRWX=<4h*Y2=Gf28SH__4l zwY=-aixqi$_t-bcXM;(h2iuO&1f^Y6W+gE`)TiP?0NDEiWI2DYCJJpsgtSxVAI8;u|bll3F2L!+~u;bUmGJap6wB|7z3nssddOpP8 zoZMR61bpQFpyR_aM?gB6h+I7=zct*{W(P-Kac4Pu0~Aczd>xYxp=vw&!RJNdS`ZNC z^V1_;4q@)#3#BN!P#vk{c5X~2A3oc2Noh_?m#(BM?>WF*_ws!A^}4En>(QE?$pwPl zHhu-aUR~osr;i{|at18o>K;h)mE+0@Mnu1t226`T6_LClRDS>T=^SyFqu;V%IM_Mc z*+hC~?%1|(d>*3F6J9R=phr>`8EnwYi0yvL7VtRxq~JFW+)x6OS$f|H=9O(tRS%~e z9+P{pi}a4yTi;?HUsw%JjevxfH9?NMeKLw@;Zmug&FRo{Kw-A5y<4d~Bt+Y`X%eJ% z5hG7-?IO$mIn)aVlUb&hD7d3x^OqdDOm4l;!QS|6ar3+g6X^=-@m2P9AkDa~-LwqrvhKq3KJ z6M^iVOPNWP<);?revQaAXWp(_Hege^GZPx`glQk>fx}R)%pzy*ahtqR#a}WZlYh@@ zS3@ilp-E&)f*=BU?}Jf(s+F(1zM!)0I4jqsy;uk%&pA9zss%ol>gB`nq z6ak+WyOp8e&TACTVw?=M9kEmic{9)IWP7mBxW9MKGCzGdF}O3(`l%PlSCwb_J`T^F#kAHg?5=$2#@T$&8}`Regq z%B9XsCV`M{!BbqcpWUHQFu*SQ*ePYF>4OZ|@%h1?eQROsBH zmAKYa@(H=1bfEBPcy1s_dJjb7Z<|%sgi??o{OyGrwO$ZH3&0%g*}1n1&u_gxBaraC zrLoID%jbpmt}k*Xuw|IX;PC+?7U|n5ev?bAM*|GD=KFYLBBRs7H?OYO%5h5ERWCFpDcg9mP(gY4qJLI8ues4C z?mZBbAIHal_}kU%#hgR!VIoFWQoPIR_GH+s$#f=m$PYipC51JzJK;dcqnKD~Ub(dC z%j%eXk3eOhTYGyx@0w|TF(pNDi%b#qVO2mBpeflpIXHf-FeiaTIh4GqdD zbIRXz;CcNE=%x1-yZZ)KOw+XO=XtzO$seX^_AhcA+dn}=Qqy-_mz(r{RHCMm(DpO% zzi%Ac+xikpzsVrXu=hMA7o%p;{a3eI;=Qgf5^}D5-tNea=At7p&O#(@2QL&+UIU*= z7CT`&Y`_eH?{ag54Iu!&wnNCSWyzqGly9v3rx#5Qml!ZsRBbWZRCK}qrzzX`dOc6# zcITc{zk_FDX$D9T%p#{snzbm|{Ms*VJiqaHF$>(E9Hcqb zS%9LA&qwxF27FyXb{`|XUq||KISBopC{y*dQlJc4rp;1VA2NL24+}g~-y9%F#NP?7 zVylCnnvJ>T`ADNw{j>Zb;bNf!?(Fv8N3>SMM5sCf7Lj>@yiP6p)Srs7ZA_Lv3XkJh{Qck! zqM-GIFWOG?>37b^Onso%&EH;u+RVTt_Dx{Sg^%Rq);jAVN4ngE*FyBIG8vN@NIK{-tKFRO%MJF6NWf?a4Wc(y(*l_mmGqiPXD)+aO0|yS zTuteZ0WbXykeH<&V=Uo7pQ?Xn(dq@Flc;nS#LDonh94Sc#(~Wg(C*OF(@v+_((G`?$0x%D_uaZ57ry(Ck$AaPrGf_C5zf zL{4}G+oE%&F67W4J;_T3%zPgV6rDiCk{I1nJ5&hp1t0b6xwx3fcYwwkLgL<=cA|ip1#%utR02>^E9b?AJmWzS2!VWPhcg|v8@>02 zJ-ZrF#o|>d`SXKq%s=4ptkSzL@Qh)DzaxDogOpKb^ol3CQ@*T- zXC_vZi_pdsgAdEbX~VXk6Pj0Kk4Bk0*Fx?@mkQ1rFcsmZ!NY|mq5cpu!GP;YdR)(K z%4@RCge$xwpXoz{Pv^1n4?$UFu4k!4ozTCh6Nt3H%K046$`A0ixfI-3h&f05?Noyp z)pJAJNBJvAX%?FZZyz}D|Dbp^l=rc_c?RE*HW9nvKp7k4Otvx{D?f}gf>oL(CdB{{ zP>FnVWm4e~{%qXj4`1_-C0bXAi11C0l}|a(oOsNQRknHQ`MI6_DUZjrUqN5ry%(J) zTUnGQ9&+U)kcafb?eS)7$DUhunGC=F!kp60-h{{Jl5XSx|BG_bCC4)zAK?BNjD8v{ znSeZf)9ubr->*#b^fe26<|mEg9S&+%xV*W9_h+7A;XFvderjTlL}PUO}l`@N|V z%vr=#6q*MOgTi25_fe}iCsw4~794%y`H6<~Q6?2SuR;6zljI^LPw+_RG#+XU4H&SU zM_G2Pay>TjOz(E8dBc4}aNwq$hAlruf2<{GB2zc9*We%W z6*^ztdsKY-M*2b+I9$m#FWqYoy3yGx%@2A{R!ZDRa`*YRNvfiD%>B)RC1q^HL(&z! z`3!L!br4#G_+lpFi?bkz0>|G8YX;*zVTH_I4il}sa`SyF4cX&Ns$C!l7E0_s>UcA$ zWrok>N*IdIM9h8qAj3os(WBqF0O1XF=&$J;W<$-_;Aoj2AQ9J7_jg+mZnS{U!0~8#94e3RLz2R+}`4dIc3pPJ9b2V)BMuNOUuFWMW zwTvT*duz>H#n|3lLnVg_76^cc$T;_RI)zo{SCiAU&3+vtP497I5_Yt*E1vSq>#{SW zr8rh510R9t>~wMSosy`3DkU3~Z<>IaNlF7Ci#uEO7F?zUZoLntOJXmy(WQP2x@p+Y z{K;eBOuaaX%CiKD>yE}V!KCVWpVS~(ql$Dz1g zLLt_naZbjoXl_=U#QHq1^-DYy0<${s5`Cyx_?oTX*Y@lyLAvLPGcJpWwtXGy&~YJ^Q0_2{17tO*5LK%m_>ry{f!B#hs>D^DgAc zMCcWBQaymXn7oz_;}U|Lij^Pp*9jaR90j%SCD+GAkM6LSha$9>B-F|`I#m6#H1;FF zVLqcut!i%-OF;w3b)m`2Us#u!t~_52Ii zwc};{Pm8`cQ! zzd{Ajd$2%1yJ5u=g`sp=Aj_wY2#Z0~Tp6&Ebh;T?xFYm9tZDMpDftwx?iHHZtW?z; zP_u}L86`dGdl|w(OFexT$D3H$AQd1j_rL*JWmGvC+IC&dwN5b8FMOKVcyaGBxQhoJ zv^ z==wsR9yHe?LsXAnG}swbyn9eb4hwTVm@R8%$2~_p`S}>(yWZ7);Xx0>4XIvNsnaV~ z3f|~pg^x~QKS9($&kf?Xh#q(PA*PNB3gy-AdvQYG0W{`40Ji-s1OB@q z8O~n*3=uY@hTyyl@c6!9QyU=3UZ4neo&0h199RsXj~I*1`92eSShdD~*z|)DtGyq6 z={bAY1P<``=QF@>0zh1j59dYCYVv0a$P`EgYaj&M3Fnj#9Z0alFb$s-TZN9g0j3fG zndaYX^8=nkJ4G-uz=o$D1mOS4Ox%4e;(@&mvgiQ3khuV8?9hAsMlRDH0HA>s^5@3C z3*Xa38ki5#>crTSQ0q9wJ_CZbfp>BJO^B#KgSRX zLTWI317!uujukG*jr;4|Vd2ov{H8L$)k1I>57;6i1Ozg_=?IShW9f(H_yAYMao!`{ zpt1Q;byozv86>(({sB!q1Hkj_V?;3gnXKEZ0H}u`X+=RRy%rZZoGcGCkm;+&<5V}R zqsP+sA&U=ouChdO-L-w}WT@901tTUWm%0pK3t&myCX8vn?_z&f7KIj6sfF z>e7n~lwVHsME zel{1;sjnztj7YH4rHub-EKNMprxP^%;qutDR;oHD#gghw4<;|0*aYlG4cZt%1CWw} zjcM0>DS1XfUgHCsif8qTg>yx|wKa!UhV2Ljs@Vz9Th`vMrdywHATt@?E`wmMoKwPn zm`(@RD)Qg^Scs3@@|MXw)5f{%Of0E%eI4&A>)3BUP&I!fvHnhiJ;}AJI;0Hb0h;xS ziHxI$JdMFvygBJ&p#63w_Cwu7ea9J5LjGQb{>_0f$ap1Jh1?8RH^_g29*ZU%0-PH( zg#CwfjG!xed--c6P%q--Rj1wTms~{^$F$^GSs5g@3LVl(XT?P%7I$v%|TZ<&~YKS>}FBRfG!#YAhD=())+h zUa*_l%)HtyTR8Q5^*RXbve+trhYB+UA@(n)CxhMQI5;oFF%+d!-GeS~ZvJ&I+yG@Y z%9Gv`3a`@OapjKsimSB!5{BL z@uS9)*E`75D)~f__*{!p^ICqH0{T)l4oqh2G)AuUv8-s@6CqX$!6BZmcedzdxy6s# zP8@be-VE;#sX5QY-+n(Af{~CB%`dpO69FUz0O0>C@y-4t@}<3Gc@vya?p9*Rqoc8lzBk6;Gklmmj7JjnWg6x3d+9L1i ztlpeD(tSIfhvk!QhN@dD$W41|5Z^Vjp#!J@gtasO zH7<;hDX;QR$Yfj$A_a4aTH{i47G6KOC2hx0V*L+^WcgikZE$l;ROFfowr0f^x=6gc zN(0sY`3O;qT4(c*yP~D4!DR#Cr2bL3>&Fx#O+eN8OU@lx$NPPO1hO@}YD?c>9!t7< zZ#}x)9$H6U&`%jf^^$|E@szv!qfq0 zUiTBgwjR_bhwN1vLtsml>N+8H!sqq6qnbcvLt9VT;YV{meNl-JZc=Wo0_>Zg9pLqX zSRzV~)ZFb?gG!+XvpmRPa8CQc@%;d)HO86 zO1??w0GHyF)W3T;t=8<13GyA#$;e^~*9od4({{NCc8uuv9T(Pj{0;GLcVObdN_Pb9 z$re;xyVX2}2*EhHYK4byN%@zeRhI7A;`WW3UYtDzk_*0@9ns2GYhxC_TbDnt{8!iR zNWIh&zylQp?3l6EYU(r=yWb0Xl-pGYNMqGoW!P~af8@H}NBLLqD~_*L(s?<%3CURd z$Fg{56jR{pbK`*;lhuL;#k&tk+yaX1ZqbN4^-O`b?!J#Coq#|?Ix2f0X^NR`joT8@ z-mWlLd*SgHl|twd*|E_D!>Eaeu-q}yGG2F&FhwH(hZFQ4aC~_Bwc=SXqV`XO@0fN( zu?QK22j#K%hpNRbKbf_Yf2tB@3-z<_#!t<29VQ`-eeg&1gFYJ%KfKY-3sZj zMP*|{KV+LwK!sB^-yQh2c}#+o>iW)2l#()et65&p-Rp)ay9!g_Y~iRhK=SJW+N(J@ zUr}>&>gyjbcTge;!_K*=agE=sI*wD>sMvD5KJ+3`6FWkWPs|xJ623Fe{1PMz8CE;n zfFHJI9l%Cv;mh!|P^EVbDBGsnMU6Zn*!fdR<;7$_Kvts z%$xGwL&%Ew;aNd?8G7?aC5IMP+(+S;*Sz%6xwWblW>()|Gt@}b+b=dGI5>$&Ec7^| zGX3O0jrx7-o>fo}e+0Ii^p(XvQ4bnSFBFY>cP6Q9WGX2?m!7q{uYYr#)T#OH?@UsN zws>;L;~jm47lE*~n(gdlsD;Un{_~RzFNy_NX%*>JV6g$CcRp$g77kv!PM9kkN{4MH z0kbHc`mF)zUP2p~(q3us;(N!N9;KHfCZ4t1G8Tx&UwpNtDJzEa#L$Yk%}}zB-?r7j z7x(H8Xz%J5{^4Rb85dJB{8+QM-^xhcGkiMuWWk5ErNePzYz$wdfqMhA$nl2IfaGbO zms6wn-@|dZZ@GHF(2hbRnxUR1R+jq0FKkd={InqgPq_j!Uv+4X@pPj_tYkY5mR#Jw zaGjIks;UHeC#0WK`Q3YGS`U(;#YQ#PJg9q8OFyfIKgTlCgJk`iETqzY4N1Nyb1+g< z>a9|hPVP)M5J|O}ol?Uktvy@GZXj$y#h$q75z?wTw~tv1G1am@H{5U|oMN!?I^YbP zvp0QahHJ3k7``}mZSSebr4yM>_lpx0)})zZ77XYC1a(mN1ZL$jP^#yDHV_BCe93B} z`!4)!^zpE+BHHbEE*&ON+u&Ck44QW)XB05yDaZLf*5)f5@U9elf)Wc1J#@f3U$2(V zJkt{{ynogbw>4{k8-(3*qPs+Qh9#=2Ei;YVi35kwpzfc{f$bWnvXG@EuCd6KzT5tR z)j|nJyBA!ZRC}6?e-j)nHjY|AaRCrFh~OS10~bg`?|qw!AHZii-|%#Hug(jk<-Wsj zH?iYdZtpCr?t?ZoA3q^!PmSDGcZy~ikxxfYxv)0>!~t*Gc5mi+KLkzM@zV<7kWIjN zdvEw3mfwd+o0MRoUIYM7PJ?G3gVm_tTreQ zQH7eVit|2haauMTfWSmFc~q)o3cd?wO6|g3cl`OQsWSUke{d$iTP0N#L6&#MgIblv zvfPlFLV_}$ETs1#!O7mZJO$rC%4}gfl~w%h`xpp@HIP+x5HO; z0#=t5gyr{ZU8GB2S}Z)qMfWmUo+Royh5uBw%2m3xwNOGimz6<)zrP2Y{4O}UcrV{L zk{_O_s>wYTFxmE!ZaSLtPH(UdyV#KW^3Z{;MIWKT{rz!%*&{EvdMIyKq%fI7%eBKX z4k51B)WXRN4V6Z*C!%Nyy98t-jNCaNMI_)+4#Nnc>18QuiJlgoIbwm(Qai6s52Opq zlJw35Eh^BA;672C&3l194#hO@ekn4QFs5K(0q5_=V7ADJl&@=+Z=-RMX+a?@yi%>H@uD3jvi17T+M z-fu1w2xKT=)!PVQ^6|~Q%r)b0QTu&dvSZ2nIHV@_+F~Vg427wKyA>xBcZQ-m`ezK6 zmXz<3QBJ<+qoQS6faDW~Js!#zUNBzO;$`dZ%mD4?Y*W54Jx^baRdt~feN;0y*TBj3 zR`K4OS!nnptm{|HBew&s{Vp+g0fJE?8OCcrIZ(8kK?Oj_Z}>$-bM4J z5A6L_Oj~f|dr2~+=arJ(B``4q!!_sM6hprlb*j3q{me!M;R6ct_C90?6p5~ALBEbE z*4%@OxVf)hFK@KG>3Os0t0%FrgrRT`cVCW#uKrE0bDCT%$84x6SRlwD^E4%78J>_2 zil^~`Js2gt!0tIf+!IxZroeXuX%yaV!ORq8!{ffJn7t~z z&C%^Tx6`n1qcH-;^8V0WRPD)NbY0&PbawJ3%geM;J)DhI%^6bBjIb6~sk#RWq+r#- z{sP?PrVSmv@~%=15dzEyJcKUTDaDH2u3Dj`n9KGoWvcGzJ6aI^88YT}ZKu3k{r z3j{m0A!KIlISmS5y6jU80Snn4#jCuByUPbcE)}S=c1mTGO=qIdX_P10^ejzvauI6Dz=-Yi;B2o;cX6O+{r7U!2IELnDYhBY)?Ne)gfA4gNCHW%ZHFfTVZ0Noj7(kQfutQYp zg*FAQ^0D4;|KR|gv%aUgw+1UEq=)QXBoK`2jVB({wk_P|RGZ^g8+!?T0G+3Dn}-ti zn>u`fq(PWQ^-G-pB?sGvP%D-HA(@TL-T^63Z-@0UL;H#nvd4mDOYHI+zF?@R+CFnf z+phxU_SUyikD9Z&@NdgnDZ9lSnpm2DC#!6Sy*I4z#|C@vZGm+7)WcHwIYMpe-Xc!b z(y3pd+}U~H+*Wn8J_haV*0;4R>ZsLbsED;_Kfts%r0!UVHK6xU+~^v!_}(-e$MWXS z7i3O{^ScEz{TCZOTRp2VWLjNIDk1=oeKqy+klU@&Xv z!)pjfHHBQeKU^z3h*In?r$5KLGQuLvt2y6=4S-JX|=#WT=FmV-wRms|2xj9fgOO2OO$LK#zYH3beQUA$De_fqs z6TC8sBIof@JI7CV7Il7kf8y(c%csT`8BQO^+!Hx31J`=7+$yG}$#_-3&^|(97r)sw zkdkM_ZLpg!y=PrYSIg}b*Wy_g$_t>zohI$<-Qf1-tn32ToTRkclxxSKN8xy#MHLC1 zAJ{sVyxJFb`S_;+q*#@XeTl#C=eO?zx z$yqUEQN7iPw(gsV?ZzqIAh}GCUJwr!D+gjgUb{R_k*2 z=_cDV_sl?=0`fqjL`*Fa38So}u<%=kF`-Caq%O`L=~biBtQ@IgWQBEFjjy|#_0wmZ zI!6Y@QlPZ>^VxxZJ;5GNv*EgdvpStpvWw2w60CT>?)+7f=YvDlR7LVzoKjP|UtUIQ zXZn7UXd(pHMtNbeWAJ!Emy|;6M&VQ$V*1eBhJC5sRn4nxR;zY`ctZ;JzWR{J`$43$ zbAk!q%kWe;}avd2h{~P>-{Q6F0<0H)nE}!i7Uj7a#THtcRPUsUDl7 zkx~ow>^GK&W7J;;o(PG!YXi%oe12a%t~a(o!1gljJ+}$CUTVM;2%=_`lCb4)t9!K} zG-V|l#M5W~fCd;AylI7bt6A1DXC6&T9UeN3$<>m%cE4N*Uu|ewel>^OXXwT*0Kd2Q z6KD?E4yOWSD{eF;Z{bV;DLZXML#cMAvNvAE(YQQ8>61x7+bnsU`kvy$wb1jW+HjF! zC7~(a^IRle6JKK4p#wF0{#K|UI%Zhb$+6MDY8F#xeXS;DMh+1(5L~ggLRd>yHjIFj zs`9_XCS2W&DL6%f~m)vvGn!vSA%}HB>Aix$!ow4vr^$cph~5)y{XqcCOf)xrp|i+*3TUSjjP0(gkhxYUGg8T zf&q|c?*V-Sa;aei);Z26tAAbKvGz?ax-x=$A5qpDFCtW8TE7cmv?G-~r8YZ~OHIJK!4+PKD?fNRy8`_GnZ0#`TJC`Wg%UlB16 zzUDg5=cn;J2#dwu-)jLkhV9f3U>eT0E&fuDDUrom_(`A4L}Ry}uSmxPp?^T#OrT#c z0iOTlH1LTIKSd0viuOKJjqjE0<+B>Tg>$~toZ|hAuawK9&Z8sG0scZy!Cio_Y@7MMxo4#%~=qJ?IAX%hX=SJJgDmGAPV3EmSdiZ!zSwDu({0OT zM9sFn;;Kiz0n>0_Zs|%F*FZ_5>u|oj-cjRKcL1IKno!0r3M(KQ?tR|QP|l}(&d+fF zsZu^ozs)%q7!S>U*|t14t~^eKHyCWhl=VaT{=mThqekHO$!=%I{BI{3i9yXgDLyL%g?m&9k`x9N7oeXXQ^P!w zXRMPG8_D5zfO!kW8zpPwu(dS?JmR%-fyLxh?=C1|3ICL$tZ8a-LNGi)^K?@1l=9 zb$!&pXN;_qT3Q!afZZN+EUYU)y>_h4jjMZ|fQK2%pN>8pG5W364fh>qomE6Ds_=U-krxlcWg< zAe$3O+-Z(A(Dme&#bFtF%Rp++nU2zqxCQE?`s+M47Z#G|ew=;Aq*&vYcv6onyEvK1 zt5gw`m8#bp(6}^|EmV&65GjO|TD`q>BA^=W&ntzW-n~i{Qyx3xAqc9D5+S87taJU! zlq74xj+xF_k;1H?)*6oP@8)XCR>v}*#eqzo?nT#6yBpIg^VJ=#XWh)OO`o26SG}}@ zkw;8!^xDo==SZq`B;$LqJr*ZX5DAN|2%2K6FXsW7A0QtFx`>;S0C1%&BH>rTy;EcF z%3NI77h*5WC-iK!5v39+rMD+TF^;sy{_A$37!I|#CE#%yU2$Bwoq_^rV&v*U2 z{ckH}1x{!!upRA2VbWI2>{`UxiUs^&jU|emcM#TNt>Rh}r=f$pG(tbyhOVQV&Ilx} z-7Yb0si!fF#9gpC`%M-3F68^XSp?aPR(E#;t&0Xd>!)Hpe0&CIS&d#QmOGMPQVsC# zo6{X=JIdHy)txVJIc2hC(%;IB$}C(JBuPuRxea6I9Vj@{uy{@F*vMT;ZY{%lz z@r-~^4kpBV)B#k|_LY=`s*1z@ud%y_G_R^bU&ZXY5>3*S>dZ(ZH^$+sOf&C9*8ggR zUM~IV4#n-Qn7x-a$Mh;sEj?5crnk0al8GncOAND$jSFjBNmqk}EFT~eVC$#>pR$36 zSJXPTC7dlV!KW$Yx_h+LFHR^nTr}djJP*)8yj>?VpF|zEp*mP+qrMNrmrqpfdOlvY z84s8ql$ycirx~B|lYBz?{94N1gq1cueP`I=I!tjfh zLcWI7KK*_!qcP6~p*vj-*B{iu6#66?R5GJS0+fABq)N#M|9YbrG`((4vzu##=p52<0KOHxa(zune18GqCVtn~TT+b&D> zPhSqk2+OBZ;G~d$#(tHbkfKnDyI;ny`x33v1NH}_o$N3TQ+=%)XD{tkeCfR#CEpbw zvW6?83!c27EPLQiLm{Z}SG#XOU=JUVUmx0VnngeE93HRl%^38dpt9PgP_@?Odj@?5 zEncdsPe{$?18-uwKRy2FRS ziz_RpOnV`gTNGP$0qjjNV}E@Hy-$qR3zbo`#}^jFf)gQ;6Xjv`gC5c}m}HDYEcEWc z3md4UprBYoA5#)zvKHh+SS+jk(6g>m@SgORXds^!Z#pPSNPL&s2&&$ri_eGfCT9EL zgi%CPcJx!W`Ag>ycLP4^UViAx$45va$Q|%D#Y!%S@$#x48%)K`b`;a(To|fG<&&Nr zfe9&R9w&KX7#X?UIOZs+;}CSs6%oy;GL%!9zs^(t=~IbW{jJ}-wv6QHr_En5tw5|< z9Vm%7x*?(8)I|5jCCe^WWz6KSJ)CUHiM0(?S3Fe>K(XKgPXR^Z>u5ym-EBdEU9Zmf zYs#12>pGdpDq_xgtqMN4^s>)^%g4bsSSH|U=NaR2pFVVO{Rbw(08ei92k>+p#lx&(twjulBsfg2S&lYbS5>0n z!e6k=38v65SpIDC^DHzF;ann~+Fw=qeC}|!`z4BRS@kD5@92{XbtN|M_F3P=cLkqs z=w?a+*a1NBLrmt!R5C}raf+_Dk35my(g@IED1Fr6(S^RD30Q`X<%6g~edA*Lhs&va z8?&`_q7Px{qXj!RqQ6p3<1+fXZzio7lQb86$C=FKrgHgek&^ZXHu7J55ed{i&$EOB zXUm4Fe8fy<9>;8(2xZupNSeNzVNz_t3H6jOX1m{y^vxRqlE^ShboU$fLYbyrxmfm?jvP5JZv8_d!6^qe?x6XP98rn`ZQ zQO9@%yn!AMO;vGTowOX5Xy{~0V)U>v?u}nVaye=6wx!n|Y3_lCx!S7kF!zw?V5vwj zt`AolU`QX@Hp#kiNWz$n<;6oBn7wy;-%qJ1%!E5~teHo=!T-vJcQ2P`pN*p24gVD8 zt15pyfdevcEve$l<+KVyk@iKWzB7A11CqH_p8}Z2gVIAD*=dUz=CdjO60>d8trbfq zjM9nOTU(36lVPExWUJ~Y>~&Qy{bwp>qO#5e3ch$(_|l@&u?flpnU{R%bXP*KQO9{N{=lOF^DQ6C6l%SQW!86hT=R=RB&EUc7sK9dT!dZ#QM_9P73JI6RESfX8Dbk@}`3S}9(9(w&b-TN00Wdf5QFFj#yR z9P4eghhCBsGqql*M2;-%n`|9IOVQ)B4_Y04+`nvr^BZHscOiK}I_{yXU zhVueL4#q2;B6)+RLLSeL&+@}trmq>7n_k*4SB0ptM3{>xH#2pCl-$2R1c;A7)XDJ5 z>w^W>w}GaFAl|r>AbdgxZvi0HZ#BrQp%TetUg@yBaU<-ABXbSlbB31!=_Qm__g21Z z5;*y*=8r6be+_VV5DB-#)15~P8Vxe6tgQT8%p`uld+#-SqHd$8@@J+lF#O`**E<8_ z@M7nKFg9g(nopQDbyy0hIf$BKWcC8=tlC~NHR!JPT86#&NvZ@7juonM-5@)CeZ{CnGoenzXjAv^u9zY}xj|F|a* zR=HdAA=$WGAdJ=>prrnNukOO|vGI5g1Q^c@`O}D0$Qayc=dE;aLr|olimBp%Tp@Vq zm-ockTp)I$auEE#49k?mP#3A%CEnX!<1h{c$Nzt=+2d7e{+7%Sz_{=~I^{PhK1bXt zKW2@GockX)&shJG@z?|-W(L90W_?KA`wv2n~)hPM2IcN|1vw$-WTC4sX$zZ}bbBcmmJM;3*bp$VCpc~{Z zz?c5vhoOn*x!kKqa1Xy-{yz`C7gfEXD68wwe)G=%-Re=lkGXvrhq)epd+>iBJpBN* zcH|}KXZv5T`)qyPD^&FI;dhS_D@uFf^XnhhoB4evr`(Li1XyE|N?k3{%N13Xx)i@K z6Dw8*Fa1B_JV;yul=a`|7zNor#uRg6CDG;48&yZ5*ze%Dep3i#mU@Fh|x~-P4I9z3!(3WjL`WW=$b2$%7a2OI&XRa`*PmU==C6+Lg9xHoX?kxO*L@$Taedv!qLFNh z{cO!YgqzDg?Z(6hDAL&MS1#*RC8;^gi zud;s`sfFiXX;Fupy1meYkGtqsZoWbKawWPfoPR8j7g|UWS)Y^t__Frr(dj z>`Xc7J$#XLM{gQQos~72y)P^`f7c_ueo8;amEuc<%6DL09(a*{`#gyB&4Y6~jm*{s zrWYbfJ+}9Zz_vUt2m8OX^Arz?7x&wi5dB)B_ULH$lXjj8xq4sf`-5|U@mV0IzLPoL z&N*u{hos$SAS#*H-9+TECgqV;)9hs5_Jq19*22C!RBOG9T?@R^$Z`qtedMR8wA_Pp z;MR=lKH;p3obP&y*#D8rS+;H|B>c-ASVCL51)AM0QY^qOkgJqvdv8FPwsg)>_zlzW zu>P=n4ZP0TjIH|wBNtck>yIG*Iur{wK&V=emCl404wYC}k4L2(wK5^}p6K4pNM#~y zEUy?@#MsN`rEH8aB}-=(bHF@VywirDCa#;l11pp1sZ5w>(hRPD-+cGLR;w*@P1x2z zrBE?>+Zt1jlkXV8(ykNQWnT{znHcz+$_PXPNw zS}4iBLCIful3KA?g7laiW2Hp%&-ScMSH(O1T|92x@Fag@66=VXO{Kj@jL~~4>EHOP zBC1owEqKf3`^$n_id=Eu$bdLl&tm893-Xgb`yoTUR%vT12lbvzR`@Lx2t@In9u>Sc zHF=_Q*thtXF*2vYEy_G@SuV4^M%aSiZg}_jrCt{M-NtsQDZVGE^Tai?X}n`?Sg}8bw`%W@x)q{;N)iZ8KUtIIHeg)$V>j0&+*+zWAnvV= z#-jxO;v_9}SU{Cd-hN@@7aJ3yTpgU-v~hZ27~4LmHDVeZwUM>6 z&@T+s-wpeQ`zRC;=1cpsHooDp@)K=P8YnWoOX5#Vz~*(xS(xTnP>;AO%l+z}^uegZ z?_)#yu1Xn+i^16pHai(#b~$%k_;eb-sIt$O`&U)fOkBy)SNikB^%mK$1j()wsauMn zuXP5LRg!_kFJ3K7D^vAL|EVjVZ%$ud@Hx!m!$&nM9QebNSFha#!l7j$`^LE3a5Np= zK&`aXHQ}3#z=&wgQXE|PmVZ>n@{7r<*krxkA2DeQEQsBl>OAchu+s8;A;B%gDL?3= zD|1l+k~1hjx~lTQLld9LF6oGgPy2^kUe8`x`Jl{?^p|`hb^1F*Wvg_!U8?G7XVr=Z zbh!qIqork)n0(a9NmmIq`7JRgkBn*kz54wQB&j>skz~9(xQsH9Ar=>nNGs`b;fYSv zcQl-8;@EI?zmq{=IQ-*oF>PK1{=KBrBwUeR&I}VDyzn&j6m~70IItadZqJ5&!DMaI z&6?(==eA7#Y%#gWP@WExx^don+~aBNPba6C%xCbyQwL+Vp=eo~xfzl^AsU}2dxDrx zZ&i9sAZAVNIE*M-w4qu@M?^WOrY(0l6KE=K3 z9FErcFrfVVE~x8oo`MTD458AdKDEj*q+m<*e|9<$$4K zh*gocsuLOT^J~|P0qI(aXydewVrlXruD(0tKzxv^`jy)C2Znby4w4pYNmHjthfPna zV2aI-b_)hJ-mi2#w)}&wI{8>;*rd)uwe!a=oB2;YXA?P*siOGi#LmhkvrVFKp+E|0 z@7|CpvM*?%b(DLvJ~MXx9UU$Bgc98tUMKI;``KUJ=Nnhb6uUN) zC|h?GDk@_@2sbOvySJZ`2u!1VmnEh=hCje-x=S@Au&-2M5w34i%^v`L0MM46;=IlSvNe?BnhI*y6~`nXAl@U7EMj*{!v!zB=qcN{%zG!zxvNSrk5(;|rRIf`cDCyHN)0 z!^5VMr$8S`u8#90ckbyn~js|eaQ`g0k%)tnV9eqJkL}o)V$-ein(rt zHUH^20L2)utdLq+N&UNK)lc<#zrNMkUE%QwN-5)si$XJp7tG%32sc5utIsyF!OIrq zC8~ohxONxMz<2wp?}RPUyy4s81%&b7L!RwQcYzlse+&{WWvnP~=s*5k0o)hE9zyP# zA_J3JGRj=&_OPX2b>NfKKX=A01;G#+&2=;9Rg84|gzc~^t5!&AinLwVIloIz_>x%F z0H_OB9|g0xwZ#X!;*~+*N(qG+MNw8$(OW! zf7VXUM$B&QQ5$RiYT#Q^*hXgGueO&>|4NK29l*> zas^0>QUV#bVc$1CRF{-i>4zx#WY-;bLQ0tqTp#+h+<$5?KQif?4ga!7>}Tg~^3obh$ExWvk8u4q+g2xQs_E>=qUn>HTMJ2J=zT!vC*T5(=`0{=>kG2S@wb=RYdYKu!8 z{6$17Ap-09lKd?8ZYM5LbDzCj$J@)nY^=@Ubna_8H*tymy9?<~0#rY7r2@YbvXT$k zRvn;q_vIY>uqJ3eZ@qWoe!kOr!z3;9{Udd&`d4%spK%?_4B);pmU0+!C z|A}u-T4Y#Tl~V7ylnN?0aF0}6w{jv^$vuUU%&L5sw&UM!NU6Fl_mtGWU_TZTc7&(K zS1Pi(rDghCW2b|Ua+;W$aA3p6DkWAx#qetRN&zY$?jBCpDCnc|k-t49Os`!}Z&2t@ z>5nVq=+?1FsOGg`=Lztn`Ouy##McToS`XjB*v>Y_*KA6#*f0C+tI}tl2uMlS|F%^V z1a%X2=b`o6)5ub^vYB@M_06m82}tO699-#f&CJ&LS0?k2@7#9sZbOCp>^$r|7v_>> z$Is1(-b80s%c=26q+1|u7h>COSEM2&7e1~3gaqjE*Iuvy_HEdtC>l4TqTo8bO=SqH z`djSt1>agi4DE8(FdBujkIBDeQ70-jv->*iQph1Vu{$2(BGeF(vkd6R^l|Vom&Ij; z-}IaHQZh$*SfltQt&YBX^gQQPwLt;)?2U{iHSD6bXl>`Pw!MYfy>~MnUt6Zk ztz6H~dE003^~A-!(QMWjj(3tTH4~e2#K(I{8W$za_d8e-Kb}mNLx*>=hRv0BU_)fy zS-wlAnPDce=c81ttRzbC<~8>3fYn%D zX<~1dNmEiiv}`5rF#>P>(2ZEWT%w|X#QMg2rof#^2zAI(Y8tuAJ~PUv>^xFb0Uno4>>8uAGUFxr@hQCRTSptHTu5`m?K+5`W~S3hC}@*u`jl35hx5<6Pmb zV2MilO1cR^Oor#A zVi`Z+uW4tn{7Z9d$g+N`_;`nHyPYrrz6ZZTUNN=!>CtY=&G2~G>G9!+ z4Xs7#l09ICv~Bt|1$;{n&4GYnh&$7S+9$5w6>4;N>ZYRS8%n-SQ0IPOt#2cLCYOpT zvx;AOs9{TOwtJ5y)@3oP>tq?IXf16XDiPH4@!)Z?xz5m@Nvps+c!_FI-&p6hu6+Pn zKtW0(T1s`hXZ}a!>6or3oC?$ntLewBk_@DlY}J-fv(Ek$03NZSU_)zDiu*6+VgAnA*7kBiByhkign8L{z7N>C4{)ec zobq9pb1N0IY*#hIm#zgKwCA4Cxuw**07%tZ&q(cP`}aP-38r+aBYd?7&*pykrCvWY zW)iHVE6OCA^9rHvj#g@3bhm%xTjEq9YC*J!2Umcai3o5EtRu=Ba#~g2MGm*0WFq-(_ zT?<47c$rVMdM4_ez7)HEjP0W856l1D~c)J_s*-OaJZd8IXIBcA2{{Gk{ll$ z^*6El7sy&S9m^E_+F78!=x{3)7WX79|KaH?iW?=~5(fKx2Ozzj2_*5CT=Q=0cK+Gl zPK`Z3#7$#^<4er28PMh~-|k#Pi^(5=iCU9DexP2&U;=aO?lUoAykxmDZwB@_}Bx4tiS>g)k2TcAls#|N)La_wf|#D z+OFsKy<2>Giz+$83EVN!KRD@MfE(yAI#CZCOzk19if@k)*40q`iya#~-nJx3&=BEk zrIo;8#Ji zg)V2D=*Ttxg`q`IbEGsV+hFF^ey)K`MSrEA$n^00gZ9K*$6*~$wt=H~VmuG)y(Bw+ zg`G1LA_0beeVwq)6@O^(0qec@VdtRp@sPHh3h5Z#NM70i6GDc^PgcgJv7w#oGu@dY z{(ZO9+M$e^RPVN0HX>)fqcHYJ>4HgFiL3d1Z|)^k0G&GBDsm0&xBS4iO;SDWqFL1^ zU)poGycqfzdb{RDH{xuo1C`>7+_|-AEG4a*uH@vofaTTY0aN}{%Ou79$OgqvFz0h? zkqH;c_CsTVQCf+xEJRg9)js0l;qIygL7RRRQrxO_rLeyC zMz8IK&CnsQS|FG&`PfLUri>DUCmR8^L0RU@6DX)l;nD8cS!B0raDQ?ZiO(z{d)VQx~ zT_>^r0#zZ0pbex~z61P$q-lkk_Rouv;^zD#U(y!GTTiaFcj%O=3~o-R_vw_G=f01E zEfT6DeX9T*3>4|cPN?*z`&KwCdrsEiL}|DQ?yy5U|A=B~3vc`35@0Z@yN}QA1|?y5 z7jT4ku|;pImBGnOtaY+I&+v`go(_5`I#+GVlzMUh7!72x%uO|hK0ahmKz)ZV&ATY6 zb)dG(A9)DhiS{rlXHIH|V1Nr6)_!|SZ|z#Qp4FNraM9w%9GSN^TvRlniDRv&#nzpP z9I9-^+1eilEK?{^FCd*Mueci{AR0CvS-s!Mp>~(9rTUG5naXlLP444kEC=V>%G5R9 zF~vxpzR<9&m|&)@oP`WeCy8I(m!7E)6rA1VIGzSS9{sNdyAfLL`Jjk1| zNG#oKJv=c`;=$mtZ0 z$yctW@ie}OZ-Cfhjvxxi)Zi)>fZK2A2FrU)T<=Q}Gcp-^-wuL>|BtJ;j*Gha-iO}{ zNQjguDWNEclnT-%pfo5bNP~!UHw)|{B_bdVl8S&xOLv2Wgh+RHEKBDzi~9ZCzvn-A z?e5H+nKNh3oO8`}5ML0Ixl$~dZF@SUY2ogwE#r|99A?OqbDv6ZGxq%TqN zUY0nXfHQHfZ&RFFPL9G3=MSW#hlLVcb8p2M9DAfLGO~(3J|4IN%8$=X)ae)Y^CGuNchc6}AI0Ia2B5K&8tX7#BQeA6`(VhiS zG->9glzihlqR5(!MOLxE>I zo*EZAT{8`RaO#X7jAK*({0O)DK=8UvN5X@lM5p8v@*@D| zZ&)DoVf7Zt+Au+q`*xz!n}oI6={rd+#8w5x)(^g|aLq)Cz;}80f8&_&Cq11J{w*fG zPLy==(lgK(;dp9gNRu;wDAA8}*mX`;TPS+Z~ube3S@YKJM9JmH-h!cQdI{Y2bpExj0U{vnZ z{rwU5TJ~1R_8If3)p{~JPcrw50yChW(x`=-cXL}eSdIweoYWbdyv6VwD1^ufJ_WaCFDlmD*& z>`<>*@6Y@KI*a`k^_jxfaM8lu-wpoTe4S#2#wmgiODw(y1_q+iJ%mQ;O83TjltyaI ztBO1_pPXJ8n;k8)Pd_ywPo9eB-QTtz$$p863^O!|EFT|*Czn8wW1dQW{uzZz8Gq3)HNIu&~(icgH< zc@2(Vn0JZhA(Us1xmkAal;9=_#PeCxuPL6{I+qzQh@g($J?HLaoVsgB|D z5iZO-c<~UCaq&vl9tdzMeVo~;$xvkOFyr-jnmOUTJ<9M+@9OX~aG48;!T8G`176$_ zR=>_IQ2Fvl-Ng0gp68N22yB_yJK}nQlUUxwlkamK9%vss6GDRv4SN&)r!K3@%WlJa z3yU4booRdL0bx)SY-ph<@QQLwg?>DdEcWA8%}29=o?#jOjk3;beB>u zmgM?S?PY64?>6I=!I>n=c05PJ5sCNVhsMpO2zI?!mJ?gEttoNZWhza-8EpP!YyXaO z_^l>K<@4*@u_sM=a=Iyu-MRMu1ZTWIuD}jo-&`81^Es6FQx$`1E#4fHl$E~Nr$n(* zr3bHvF>E$BG0rG@xH%RTW7Dq}IfUEKcVxs1mhAfrn0_~W6iD$jaRop=X{}EHgqXq^ zgjYysW+`+PRS+En=UHDg^UH7G3@~M!x1RcRF}CCFQzjqE7c6{MtN7rUC8kfdek@Yn zoUj@H?#ian9YCsc#(@L%(+yT#SM(I^+A5^`k?2wUWj*b1XZgj5SG_Oc60`d4vp06V z5nZBk;dH7a50fWgGuayCJRTZ&a#WM}y;C8mZ)~^;G3; z&HlT0&o1szo>k#MYG0&}KPV{f!hB<-;ds4}Ok#Woh*Km1_}Fa(>C#$e0t!3qgI_ z-Ox<>|MyVFhIf-eRXgL--yXIeC6O~Igw885gj>Z^o{e3A0#c=g_?>LxQ2l4LXg}1D zo@-f#?fg_#T!dC{3SCD$Z!5pS$wxg0At$toP3R3@$KH!qe&p&hvpx8erT@ELtPUq% z9V|*WMSVK}tl%0OOr2 zaQ9s!%MDQga>vIFOqQ|IYMAwUP%V*{72z zow&Q_XqT}!zrC`mwUwvH%Al^t$U7BMi*mgF$-Ntm*XR!?$PA`^i|AVPI>%V)Xhw~(JC?nVFX9(Q*hNa z&-Y>-EO}Mr-CKS3sy&LM?5Mv!Q&y8s_Wx1Stx-`HI)uDSltkJ&UcpjT`~4L=O?Q$< z&4hxtG|Hp+pf5=oNg=t~8PiBlk@YdT*0AkXGraF0&~S5a0i7CHEjuJHuO{@N>Zm`n zbvVD$6&){9#+6-7`Umg8zL7AmZ`N6*@prbLOzT-F&=@%T|GM(oxYH^8Zqi*v(bN0oUJujW+-4c`=Y>3 zmc|pf^%$JTUk%!Q2Y}!njKVT?vVVFxhTnaBalX-bU&h$Ov>Uqr_`})t9sem`!q=&j zWGdanuO@yT-Em$YEk^Kn#d0rfXOOF+m%~PgUjbyfyW@WH&4z~nxq0(|CLG{wFqn}) zC#Qb2P|vSeW#56@0E&pw2ygSyto`>mAXFjrxrPPC?*%XEjONslJ2l|fDz-@XIFqeo z`jSQWqaMp$m-b$%ejQeGMLVrDF)h_B;&x;%=Y=)#eCL*7B|1%v_wKT1H`nHQh$^L} z>^*JE|L;UXLX9#oRiE_R{E;`o&9_HSHl*b}L;qJ=7DR!{5Wh=@h2zYyrGI3Q<8TdI z<+30tmcvUj*mNLE2lEf*V`pZFg_Pc9+uz!Fe|6%)~i$_EDuRT#?mAU;{+IH*Pb=z>jWQPIfTpX9mYMlhO zRyH8A^XHZEPm>nsd0e#Gm@neh&5#Xs{M-&u*K_o}n?p|KW&P@?5vHOmU>t}-Wo0KZ9Xl1z#veUa?)k>ld{~^<4H+)k*T8J-jZ1E!4D%M z+zzza{leV2=zar*Y*|8KisN-rPCY$dZ(nUDURI_D1L1v&R>pFQD}CIf!WT zE{NH4J+VXEKb758AUmIF_sC8tFsR*T8 zC3%$}W^FD$cp;R1y0z21t7Bg8j#a3L%2%0WMQM-|&r0O&msmpLZlF#JsK{I-a(x3! z5C@aXzRBU_y}65HJl_E;cO|z@<+uJip8Pz#PKS0!y~{6WX!W_H=2@Apob9@3s$+Ji zBwWEX`?z>xm0~&GY@9qPey4IXjbB5E{g+sa!rE1-h1juqM`*tgcr*-=@l-cSY z$MHdoW1x)I!OCn$>k2lM&AM_z&<#?-GWDr+VId{|`bRW~%2}P(lqW=Ts4f>iFD|qc zev-huymu&Pt5$Nm#>28Ky*zb(FjfwkgKB-SSloXBAyuEgrv|^KUi3o8 zu{D*=jf`a$)AyJu@XeKA&tqURw1Tp&KOJA6X_x&Rd0r8cdA9KSpoEBbu>ABBIo=|n zwjF5(`7Fz+&Nb+RC_a#FFa%Nri9f9zy3k-^a@_<7yqEe8&;>!};#`jAX$ifneYz9_ zNGsm&&h>@;DUs7zs>X*eu?inHYM>4-$BHX_*x$1QI8{b*@Q@DZ6hMSzm3wLnGK&hp z!rlT{)|gQ9leipVK1~s#q0E4i^7Vq3st=SyUN<=QmMfU0XLcp96!-qj=tork)2uGJ z?8oHER=MYIPnjFO12-=(&mW-e zjR@j63{vL{PTZ{e-m{CXX7#@=vPddRCdf<~tcGmy@Yh73St;00k zR4PF?c>d)LB^=fqeNHWCX^NJ6rKaA{R8t4ghr}4Y060fG9lJTEbV^65?2e@4_fR62H9?cG&E?%zeLeN2bSm7oZyd`%SW~?9FnV7m`Q{ z&T!mCEr4=;ih6pvb0#Zye-93cYyL+I=mYjOTr{yPK=eAA8rwnp(u$}gGKLYgaR^FH zG5v3tU%ra@INz$}1t&%jpgU+G(n zpbW^=eH|pRFsrCky{T_J#n~ z3*Hz=t0+PFYosEu`wel8hS18YY z5oy45y)Ai)NjaPBjD%SQFb??#0FJZe4qFrhJ_NBoLQ-5#Hnt|xw4FUZ49{OtYRnS0 z^E~Y&oie=&;Kvxb65;^5}CM1*d`@&+}LKB{}_=3vY{g4X~^2&8_W5D2MraIaB zgO-Fru6&BpQPoA~QP>|*=hTuN+zU6~;Xte(-a&Mf90|Ku^=M&qN^fu>|2YfTp*L#~ zA%&Bp^%Jw9G%E?LLcQOL9&k@#-uU-db=5-wy)29dNghBLrBul#e7(;0gy+-Ke2JW( z(RU0w{SWs$YY9?sh~veRCegqGIsM+9xdW`GD8LV5QhpE2DU3>Afk%vuxYXB@7c2;W zIWFwO_ZWmDUU8SilmB1y1#d$>9G|jOGSu19Y4u+RsnR4N9qsQ*t_8uHI&TVOO_9U` z9HRHJ`uC6CoxT0r0e}*9gt_ooqmMhe$Ehij)eogImJX&QO;OdG(u($ywQqDj)%V_6 z#pp7&%|++WUjR-uMB2tA*9k7WmtPr(G&lDS5~B&6RcJD=KEkR@4KrL1o!yEYbcN+IeL#Syw! ztGwp3K^rf%1!_{*pe)H`F0(zvMq=B2gR0hkCxZ|&6@tp9HPP|CfdDZrAWq2Xp7Uoi z5<4YNvxqI58=Yt|+)b=-zdO6!-%D9DVR`U=KEaqP19rHTjflhx{W(U@J|PG<|FAV; zgm>e2#c?5@cJ;v2hfKEM_yLjOXj*K92o>vu+wIxqaeMXtr1vMUrQZg*+}T)Tt|2_~ z&iR3!gJ+|H)SSid85Vd_e|QA1pN+O>+<&KV+Vr`~pqyZ0{Zjr3ehr;m>2psWA)$@m zL@@b1X^TC?_HXcJ66j_5d|kQ){u2hL&SFNRnQ4!Z;K<`!gPzS>(c&WY_YW3Xm2WZ( zig-nn;OLFH+}T}8CwX)}|7!UvwGLB5__4J=$4)-}<}{cxOqcJ^9e1LvhTGt_f*N_B zZ-PT|lXFm*4h|=QsnslR)S69Nt|1De5Y|x`uCvjvxoM&&a`!%t4F`$yhdTpo=g+BG!ta8zF>DZ}g7U&t+VE^qf zdmtpaKBnxl)>5Bqo}})nGkkRF`C_7*mHDK%mG>MaKTGSTYG4_qD*xWAkmQ;;REJX@tdtIIU||e>t*fs)TP5}N-u;cq z+I;NH3`)kHV70`q@7M5tY2C-(*5DMzZy~dhIA+0x#O?UCkZK*r8IB;#&JHSYS*Y?I zVHXMq`{@+)+lNTs{5^;q)ap-@^@BJ<=KQ<-^<*R7JI9xO5giuuinbIU?kj~4a9$r!4rw^SXGL<{Ryfjso~qqXe7q;Vc|`6tI!c+0!t zp?s?uU2u!)!lD+#_pn3Fl>ZX&ppo1OU~@xry5<4yII z*UTl`4N(9Jom4CuxAHtq8#Y6NJyc)}2YK0DJQJf)#OB@I=CPH4t`U#hvdbT-L=6Tw zCr;IA-<-iWpNxOrN1H@&hAfBWZ}X_Mdx}tHjCrotuUVc_?j=-wuG)-?x$+MS5Qx}T zbhHlf)l)CQ(|dp9rqvB}evA8?ug3Vn1WD4FS#LPH2$<)0pqk1PN~`fPIvdl0A^BnQ z&(@7+=^|zPTg!Ai%m@4mu9c`)vqX+F*%fEh>I-)d*PeWiW)=xv8kpW-g&%I5_%ADg z2SkFiGl;2X9UcCG9OKU-^=%(W>QlAO+PXfExh#xho5O{Sst_L$Y|H~8=#h(OOs0iX zYoYaLyx7QR8aoG_7qY#!^UO5S(d-_p3N>^5SOX-~Jnpkd1JVX~QP@m#CFpV%G|s})cDj$ggvrs`;`A#{1SY9Z(ANS9tk zir+jdEtUl5t$35;J(N$cPtC~H&mx9y?MsC6BvK}NZ*5`bfwSqV8+J^mH4LYK^#ffP zRA4a$@-nEeX9`KeM%%{nN1=5jvMt z<%nI*c4FpNMm1lhGPPFu!}Ca6#?I(XNZwe{T{54yQr`Y$!9|r#*gd=>%gjmgmsVuSe}~$!Z{UuN<-p7hVs2Rf7KbU|-u9W`2s!3J$*g!@F^aC6 zlu{>FP($i|DeF&K4oM-fUmu0%?Ml6mvX-hhqXoA6w)0cvz^E1n#XQl=AM@Ob+~%q@ zv>XifKJTJR4p^SeqXYI3ZHLI)KM&fMOC9%46@(}zx~mEiXYjjC^RIM}{i(CQ@cP0M z&nk`|Eun*Iw zt<@Vu&m4cYGF!3VY~tTRQEm{Hpiw~!_{t+xE=(vRDll6%>%01aT#YgnR?V&rb@_D3 z3%5Ke#IbHGq=DLeR?XuO-H6yZT|#BYu+y*K@;wXXF;hW3ngRhk@hzT= zkF ziE=O>e|9e4{n3jN!66ih{*#wZh4^h&Yt1w`8W-5Jg_EkvQG9@t%$`9db3CtV#lI#55i;5y+ znO7cou6C@4?PQjW^=`Og7Dq^2kBE|i$gA-3D$A+&1&EQglOoyp^tYg%S9~)Awp`@T z8W$bPH3fUz#icl`qIUMPIogC*gD2of0k-2d8nMr^l=sLg#zU7~NjR;dbCea46rGK* zL#3?Q4K`64EYn4&!u=3R2#SlpBi;p|T}?)OAB#BC*((fAAr7HWuE!N;A}`>=uiN~5 z-W~RiZ=O6<2V3MDx@e&Ph-LG;ut8nUlOg8{iFr$s#PWw{`@@m9XRnKW+?RKuD5za%%v2)ntGu^ za?bd8PX9|K?bfTEFh4+ILz0wd4qRhqv0>oN7LM%U=oV8XWA-9AUbb{7)(HsTR;5;% z4tW8~3f{dkJFsX9o&;>(!7e>g@iA2$L03#nOpmi1l7Q(=wiE1_-2aM)Km zhF{lifM-3;1|Z&h&3RoUCPQONlQKCwXp;@qPb_-w1co-rfp{j%b$)`4;JFLFwNp-zC*7aTn>@W?wV`wpDlC^+i@P-RR$fHAy4fLCsVZu0<9NyBizyow)bDH z35?KlG~NRji``GN-WgTxodwYr!nsY*3GiEh8>{91H1jf-UA9R5R?eN3mn+TL6#+_Z z%$UpRD|+yjZ;z2bM8j~|OblbsgsP&)y1`39<9>+FM#7y9=T}4O6I2o?!O{cjC9e^`D=A)Aba;8pVqH8!S0x6dcBTQ1gy z)}wuj@ck$6DrvYTz9{tSmMT}98cYXLbOO27Cw#(-`h5928-+DCrYDX_g4$Ki+-~1L zmaAOPTHA+{5c_K>WjxLY#2l1ybR>JjLnfq>9AAloh`7xSg z>^MPfh5LL-ynMOH=)EVOt*_E{7aN$)85&nSPUY4gBFY9KXYj_MQ>Rj?erVH?eUcCKz_`9@fO^`Tpw{kf@u{WNZ$;=my7@~i)&evtxn?ZOyb?4&msm}xqBCMdgcdM{dSTW zBG)+M!qUfQYfKm|U5;b?hp%Js`}3yU13uk-_yu6!~34D{KAIxy1R8z*Ffrlkvx%8}7HUmEG0A zBY~sfQ@H~vo*DlK%gwD5SAAH4)&GcrkzvB_#;|7xh_-U8?9`F_sK53HguaGf zR6mnn|By3XdgVG46J2n@VH*+XDoypvFnFz&p_*%0sivJ~ZkP($606}V`$=tTc#2Rj?wQRgMxl}9);5?t( z6&a(hBldzUdBGKh+0iv*c7{V38%F&eI?=7-G`>^8>i+FF_54sTe@iyHdmsv~#IGWI z{cQN9U(SQ65g(QlxcQDj__e#@6T)1g#8^ullRo<)bP&Xb-yO>@jwoV%B7%KbgGa)5 zpc7NT;j-)}y&{dM5ppP)xUjZ^4eqR0svAJSDh^P5BdK-7?$FUOSHfV_%Hl|?o!8j} zXzs9)ee0~X4y~Pvj2(@>+=mQfh4*O9z~88Kzv06}VU(8PQ4R>621y#UL3A!E?$>bc z*9M^h)Cq#vK{RL@;y@ErFc00sdTC8C(FY!$pZB#i%FlXG%`6PTB=9w`hi{I44B5_B z(F*7cG;`(19g;fOSvT7$>=pDJeHhTWvGH{rp=*Co(`GH?{`+ZyYAX801HpFwJ|` z@J$fxs3sf0<{Bj~yFy6{Uy+&(qBG{?5Fx$yXuCSKLp=^llB*@-wI#v#1zoWDGMxoX z=f1r!K99Fv6W9LgFs(DSgh%=#7TY1jPZ?`lDrC1Us;o%sj#lMI7Z#kwB-%SoWo{$V zE#Dp=O|@A|vzvbH%sA-hxy3i&Zk_hMY3Ar^TNH?B3&|jZ2`DCTyxu#ANF3vlK}Z;E z7!*zWb@6<&txlhx*3Jy#>>vWat|)!G?7-&v9<`(9&&)9ujnIa4Il8sNbyZE)rh}m8 zJ8sI>2lL;`+pH@PJ&zMqiw5xIta55z%dfSHm4jEQ$Hn7YJF@G3fC}C{Yu5RhJ1Ne6 zm3<9V(jy4M{aJ;{pGeO(`$7-QwNcr#1s;IY-T3jC>)K>eg8aK>U9IJxMYU1VGXM!v1%2^OAfP=rPdxIY4U9|1u>0@;@zP#5mS#k?7Nd_gdRee!^cq{wC@o(OmNwo!eW2UzlaCL5MUPnPjFx1CZ~)bDr&P4JBAdK%jRLE z$+UrxvHxj=9m9e#f+{0pY+ZXV>fgLZ&xm1O0k@G*6ye5RD&bQu{0EDLfJ1mBLXdwyG^TBkN zQd~bp6G8l-Lg0CR_kP(vI&9*ozJUmO`ll6>c#yZn4Ix$kj+T*|Q0KPr5JUg)stn z3PzMhPMpBAENrL~(9dG#AK9_A3~O!C`tMiXoHQav|I-As1G*6N6#oTw?WI5Cyy>+3 z)j@ijg&wj|`#Y_#cu@ww0-SxKX&~l@=QCS7DObARB?3)G{J}h6|9G}8C7&)vk!T9V z;QbND`z?fG@PKMD#0VG?kq;U1fG4O2A~pE1NcZCy{Lt&{-v9dzSE+1LdJ_m=0;%K- z`VIqW76gO^EdX|#bAg#{|F{a>m{M;E>!68SAZs9oJ#{uzL0EeGSF_hl`q#m+u{WP@ z-~Rl@_hrcq+_!ICndhmEot@!{V;s0n&M%pq5?%YN#uF-2W{4Un@_#g}pU$ICj>YaD zt#+(Vo;dIB1$SZP34`tn0gXtEcS*El*8xE;H`I7t{LBW&L_B(u5f%Lq(su>jn_`Ys zSB4Eg-NhTiKOxe0&`ARjSBEo+;N<3^96rQO0(f*efR~fD);rV9=I!H54Sgm{MgT*T z+>!~O&h>|C>p?%gV1o3AtrRKfjiAMuk?I8XxWVpn9WBFPZYN@udx$r(O4NXOW!uF@ z=Xi${R6qvxKr{grhQeS&Uwz;u2>YGd+LNy;Eid%dizK&0&<>e{0y0g=Y9L0ub`9fc z7#PZ>e?wVr3hm|=J^vZT{?y*DMBF&72j2*g#aTQ`iPO{CAQHb^`89R(3`1h`d37wP z8@=YcKn3}UT-pwJ(6xJ1DLQ&*+akh4;A&|ob+bIrcnZ)bw?Zh|>!28w=nw%KLsI`|M6rtXa`4OFx=q%R93 z1Dqwe$pa~l^=y}*7mPsM?iIXgt}v+fC73QMU}D5e)LHhYA3~(y4KvmDX4?MBO(>7* zZ?oM~C)wThbUrLpP@V!1F`1UKN$(B}*#y{x?gEp8Oj8b#^gblq;DYq?!QPS*6(&Q} z;XqMdz)HA)Mm|csCJf<%6vo06=!MeN1$b%>(^EKn8Q!;oB`0(Bls>*d4!ytyc4-Dk z>4UaN-`v7_kyWGYB0^%}=4_s#ek5EIzf0jM+*52Tace~ig*YspZ3|mn;v|{!e+AY~ zTxwnSD&psV`ZO5Vz9h23R#^!}&AaONh9<~Gu*Y*6yxHhc(+p!#oKz8>nRMD+h#nyG z^m%%V?m+pQ(aVsqdJ@*KVnti~J7MEJya}tb(;p>>0m+5b&Bgfrc4jE=(%&iBUvC)e z4%G1-V5t3-MW=OLu}w;hT6>i+t*rD{#Qnk)OTisf$m%nR{p*hw+f6qN=-#yiQm%L4 zb!Od})d$19S?O{6beaHS2fRo~U>c!nR~k3V(oG2G#YdtUw&oB9QP*Y7FlMDqHw5>T zG_9*)Isa|Eq{iXp;qt?JlGeNuypMgEA0&@D$dkSJS5X{2l**7k+25t!>^m{4$0pS` zJry1lHXLpw>o+zvRn0|irUZRMW>|TT427&LQ&nbs+VsA+dRTtQTc)}sj0W7b#1)Pj z$2(vxpnq#o&3^-U*c2v9cPSlL6e8}P#&8l3^!Fi(N0>|;tBe|SwE8>Z?zl=UeJW@- zBNWrLFEV|Z8qs=rS^f;PXLYpy?D+W=C`RaSd-8jyYKsTE^|Yk=UV;x;zgQ(mj!+KJ zqfs(}+2L%o`yTDqW?rs)RdXr|f^R)LVonrB$pvty99q#IN)YzwaQO7L} z4PW0u+Lh&We1xh^_p+N{$S2L%tYw4n6M55(N1D7!U%#N2*dCV;&-aMzJ!&otfP);` z!k1_imqrp@FW{^AaQETy9Fun3GYqAcLd(g@bZ#nCJBl#_V8gyB{7@z_d=Ziz!M*T1 z+g?BJWhi1@uLTgmE%d}%MW#Lr-R{DUGZTKA;Fl^=Ez;dltxprg;wd=%U9rlhh4+VY zOMca0FOSz9Efxh*y&)YRnC9@K0yDB}HL9W8_qM`_o>)mJCk?wzTyr*6v>aGnCc2#L zRk}8O0X3ik>4Vea+@`P(Fu;9$u0r3bJ>)XA(j%W&ww|V*iWr$J6Zq<#*m%8|P|0}p zIwU(D&xqDJ8%UWYv*YI(!HpJh3Cn z1uEB8TzAo_IThg)k2)UbV)rEBFYi<-cKX5YiQhX7*k`)~-35r6KTEaTmHBDm_&n@}I%{x3$y(I*Ef;@Nlvm{ZRik8@pI_Qr9ti9~vPUTh>J-Cf4 ze!nO>yz`<(982HFdv4r?751gE(b1P2pNbvo!)IQ})|WeJ99>$LcEnxWB94tBa;~cU zX0@5-*QQP!yfmFmkCtEgyu2n%V(~4JSJ1xvCL(F`R|#bhKq)Y({!aFDmk1$1RpV9D9i=z2lg!a{z^pEj<~ zb%IdVBs_r(V%i6N>%XhN5pZ69(F8}7mTi4-LefuW!q=BtgyO!E+mBnc_JwqwHfdCa zijF%iOB3NN4jjcb1*UsGX{qYFcNCWLC0+z`Mn-q}cwY9<`Ojob3lH7+k`8qnw?2v9 z&d7G;U2N=Eqal}D{YYkY|6=qsb#8WM?x$#VvYnXZzMY74^DhR{BM!L43Ny3^N?aMK z!q0l{jp}s#QtDIYAx(ZTKauw{{7yaEa8^F}GC11a=#G&i94|og0NIH-eQLi~R$X)J zSC>(S>DR_#|HLgRP)@iJ>f{~FAU0miYSs2bV9Ht~xWmpy_rcr}jj4;ax-_d0p|P zkxgeu>uecU+8!~V<4z^ah$qp6Q9NY*+WNlG1J)~3ruVjPNdtnTyAKDr-qkc?L1r$pV+>V3~cHb&ak=X8~Kj%k0vOvKu2bzkog z@+CbsEYT)QBx2IDi@-Q$+w4-ukrYr2*d*siecV9qN4a!gE}&jFnw`jk~nnx$|Ahb(i;nOQ&kdPdD`0()gLg;wnF}*siG+V z;jA-F5R_eQEMV|vzp5(mw%B}Yly>4(!7aG!_uiTG&A{p(NAoqYBL7yscdlSMw*;zH z_`B*cHIvj5SQnv&*{{p`;ZCp8FB?bopB_&PTsn2kn#=O+R8vF!zV(bIS-7V{O>{@{ z*2Pqh@^(r15eYU8$;y-Ct#9Sakv*We(jK3@g=MPnBSHPSF3j0@aHRomTd0`s$)nZ$ zWtsGBu5d5(!UGz$&Y`Xr&d0jv^BTT$n7A*`3Eh>(dB2A`fd29V{8`D@>ml9}%LGXL zjE&`!rIDXaL-(0~#tOu4;|0Ne=q!6Pl3WZuJRHfQqQJ3TZ2iLK^~5hUZUptj&wR<0 zH@{S;J=g~QDW9o}@zKCEe`RSVCKXbc0z&o&zXZlNbty<#)2mqV;tGqjzgI;4h~SMZ zs{C%vRsqcS`R?d%pq}*{J6NLkfV)FUAAC ztEViPOH2`_1W7&qev{yaMTP)3i%@xnXt(+QJkJ+rh!R zOv^B(JATbXt%>ywZg%3EhM4GUw}82bs6yO0943T1NdH>OA-6mg89o@PY={#N^?1IE zX~i~8ugKZ9rQF2|_ly=tM|bD1WnLV@BWoyNoL#@)8VJq{$j<&2*;;Nnt%TG0n9EXHrAg8f@SWh#u-#`r&})IKYjspTvf=`f9?yf7n)-%JAD|}0ndp~V!3Q~K3D>Hu*#sn@GpKpPS1lwKc zuF&7z|9Y`ErCVOa7w-xrz71}}CS~qw$B(F>7ywT^-?-ck@Tg5UEa)vSIPP?EHv(#i zu^}|(-i5J9R=U?H5|tpR8CaH{ok_oa`Fqe^u77RMNy;o9DAo58K(*k8!C0Q7cLnhP z7n;Y!6fpKWOPhhUG7f^?gOR=81a^t=J_yqN{q_7zE5#Iuah><$V|;x&w-u^=|MwOX z6CjrBmxv~(R|I{7`_N;2g3AaETne(OJIjv^{{WpW{lrnblG9nvD_xGAr`^>k4 zED;18#js#BZwVNZ-qvViL%rbYcRpERx(QdtrNO88m`@R-WQZnQC=Ixh=R@0ecPDkr zkJ}W*g7zN*7fYekS@HN0EfgdASNh(DSz1UR<9#XM3$mPkCV~_g{>mL-utjw4HZ%HF zx-y>r0v`d6((@@{#e9?p*gF~hgezH3m;d__K`e)GAOv~x02PwND8UXc#0&y{=i_;G z_W)O)`c!ld+so(d0{$&=?0ZXNhNuA>NWbl`^Qq9#;{S;fuoT{aJPWA}k1sx7#`o&) zuwXjQg#i%-LcE*wnqf#*%*7aEBZ!<4(DjL9}krPmp2NwU;W!RywbvWgkF!aOcj71jsE6wTBpD4>OhA=`esn3J7 z`oV8H18R!@etTXeH+*jd>%++u%V~`q*vvHRT;#gqP^0L1WVlwL%9HVQix^S>=JBoo z=s$F8{q>@T-qZyg@zp=!*pA;P!02y6wK!m&!okFmvbygv>Sz2JJh!8-2Ecg>f(?SX z{=Xe;PpKclgPz{F)LrD^pCpWs{AJ&G!^mjhI(jaWXgEkkO-ZNlxvr^RRovz`a|!UX zgO?P<&|C0QMc)pn!i~)(+r=jOe{K9Me@}8!tPo!#t07=Eujy*UcE;M)mJL^GWur6O zxtsTpcCWJuuj`%SF1GPS%o~>48zQBgrz#|n0*IQNOGF0hZo2G4jDSy8rvN@wCH*oS z9ejct+*19rqVASCM}BT;ceP55isKHE{SMmVUdqb~BO}euud~Y|3VZUIYkKa3qha>y zLP}b%Cyd{DC0tI1wb!y=gGe1gZ?hO%wFPd$0%B8B*hp~-`edE9644ipOCN)(Ui#}$ zw_NPlJ{L)gNRI|Yo&pc{)JWK>%qpCYI7MB6;mNZgv3grGi4BEgyjNnd zQFU{bE-KSQJ_WOoPPMyv8UF_R^y>QXw})jE{)EvH13p67s%-5|Whkn>=^c045c_Dv zcKmGGd)8me>T@~9Bab~+kNt%3R?pnfJ5l0bS%z{FDG@*MAZB3FW3GXnAd={#yM9a9 z6(a2n1F7cwiFQ`&GtZQDxtsI-D$_p7503q$Qxj-!(m2h}gToJ6cG==1?=`t8MPNJR zcN)$;2=0w;H@7fz0?+rN)`$vhAn_|;A~33zJd(0g56456)`EsDqoeGs5*H^qb2VP- zJ=IlBtIWOMVeFNu9ZyGd;Lg!bpFPXqF<)`V@SPLYjz?$EL+-f;O6?^-%x?%^0>fUX zzfXt6s{i6L8mjOJE6{(90A@4U)qBm6+S zvj3{Rh_UMQm+Tq2ql=N+9uCCj{NFwz#8F-?7RT6fJlCE|E>KnUOUiQ$%~qn5(et%o z+-igUsE|BS0E=kf1TjH*ap*4DU$rwSzc1sm1d%#RQ6;nSu8Db|cxBN!EBz|AxAvIhiozD z1;)7PRaMY~yQ9k_M2ZKVmVlp8FfC^72TDM1(SHrt{_;G|lq0tIsMNXrei0V!+@wc| z^){!keV#*yhg&X~Zje5fL#)!nVE9XDB0Z5@B>PiF#JI!-bTo(Rw`}Y7=kg1lZse#{ zk=(@5x659#R(e8LV+|{8%qF@YdMx+nyw=N-sje&|9HFqwoECG46dF*vf;Icd7g~yD z#xlqWxtz!gHG(4y5MUv1mANxVz(t_m?fIc>6L(R*QR2>o!SGx1X+JE|l{lQSo%1a< zkKjDPKWzYETiv!)z~x7!p)ROtqN5_~jzqKnitrXSpS76-W90mEkg#_!I5+AsV!bUN z8BlsOgD&%OBDFhtR_CjI^if%;OUp?{tuq z#Au224ci4poR`{k?3L!w+jhazQ6(3zA{*~Ra5M#8wtIAZ;Rf=nkmD<39?_%~b38p! z5e}`iXU|G_U5{-Nw8BNprDq1WL4D6XZrZ_P180_S=_`db|g25R&Go`aJJ2^k_5pYdJP z39tMSHadn3nWyK%-PvoMl3T|yDJ(?nix*6I#xq0Pr6 z3}5W7Y7jBR%(fj!4#&A2?7K}j1{CcHx$PqnqQX%v*M>?SD1Q7sY`D8IPRf)%jD@`& z0QWQ}ap}<1F(2Nd2`uRob4e-MO#GVd-lx6ARsKPzoT;w*Gx1FK&wjI(^~LO|mW@Nx zOHrK#o4qDE|0HNEUV!^h5nmO|@MI9x}j5-$QSz z-uT$?#o_i+1nnqzR0DHcIEZq+xL8W`M1uxSNkLCx#Ik;n`tyU7=(k4M%d6G5U*le0 zgAzp4b3mXWJtxKWSh`+e2%(lAVO zLgOO;$PLe97aAslZn1xc>eP;r_G$g>YG-MzC@ z;w`U>-9fFJxFQqmvr^qf5g~bnUMr88}pKWM!G+Enf8s65L^%Q)5#Oou=NR zP+&TlFxx@Pa!I)f&@f?lx{H}s>&X8wPvgjaI+Bv?(W>x^Fyn>D9=Hw**2JOqb!s#+ z&5M>_aV3zOzN(fm{r~9t>bNG`@BbkxDhf7A8X(fr5(89}7G-pbbWXarnTV9q4bnY? z(J=%BX`~r3x<|uke%J7MKCkEd$8Z0^z1{a!_jR4~KJW8B$*}H(kA&`&9;d2jbE@ZK zwKJa8p7%lp{{XIpXc*ML^;d86#%^pAGmL)i=&QRz2R#~3BI9;11 z))?ebxpe8ni@r)EO}oT4`ugg$%~pOmLym#>U0!2K#(o-nF04U4=vI{yswTek-T5$} z2_|sDaX>z*_#3-IQ4E_p!6KWn0=&j+=^Haq{^!gkv+}$?d1+{8#1|eo`euv3W(R$P zt%|%fYW)pdem~g_oufl@Hf^^yL=Rk5LPMq8$CNz3&|f%9l;paP{dtL>1O9MG^}r=z zp}LbK#@FpDhxNG?2Id2m-v6Vc_(e*<#o7MMzlS8*C6XWd=43*?QFBwO(!6rCfXYk~ zPWaoLPO@_Mh4~(Fm)C)91o{!63CLFI?%3&~+L?-JkSz7kx|y(kkIze?Gg+?O#e%rq z#uB?nD^U`^1p{iOsmuMCnO#oR2iR5Y8OpOA(E)Cm%6bh|DVSwx^###9%v-$mT8K|C zlWitLLQ2XWx^4cOmP6{-)3W8T-fp;U(#SW*8x~#2qpF{-s|ef*-gNWSZ0W)Rv+NB6 z{M!q0y1ec@w`(9!^Vc6+27;QAOJbQZ?wZ&?+-vT8L6Q63AH2$=o&hChIsP-2G50sU z2Ie8LEFSh=?Uo%i>Jt&6VueoJOPgzKcLPUklLBg<${T0*lt^vqvh_@)kTorHtOON< zG6~#ZTxWrY9DSAZ#lsx;$V}1l-Ec2Qr$UQW&FNdG@j&-DcL7bCIoPCe*bb?X^khYiNdf?B{T>hLD*BQaqch zPG<4ibu2H+=^fE%Ut%bvSf~h9P927Av_7F_0Xry5if(>pyZ+LN44IZ}&;8u^t>6AK z(9qw}59Qvyz0yRXzcyl@7->c)HZ>P&IoW$Np+_N8pclN)L8q<7hIOlCH1#v8)SP$i zhaKL74mB)cUea_r-VEN}d@~D8)a92f-!HNVPLXw8dA4x8cpcGWCehnhza1^=-Y$2K z2(}V(^9sCeBDzVbK%gaa?>mJK$;jd8YvOmc{|L6GF4nYyQttaMP>$O zl8OAA$8KNgO=&d1|Iu{G{ety8dGTyK9_lwgmPp65Vh2amy4`t zHcZ@Z(!E~J$LIoF_ucG&%XJIBciwiyEFfh1_ZB$G)@ZYVFGYVPd!qIfm zBn&T1=@ejn+KztSrTyQr`w8IC8s{OYKrIeHTY0ZY0Yy*t!*iFVq~nGv>HXVh*78jb zE^nrW@x1-W@v9 z*5S8*3NXe08mHT1afHip41N*uRMv*8-y;l(iSUcTjyZ zlsYmH|2Z=BLPg6z<7*R$#s_E?KG)xAu-wEdr|PD@c6L)Le$>_(m{Xo^%S6SLkPA8oGz{`rA+aR(!EZH3C;P=kk_u^W%W(t%Y`L)HK9v66` zg`QmA+zBzOet0=MX>L~clVV0XLjrVgwvx9r1Kxkoe1%3WxVL-jOgX>H&-$`Ur_DEo z7h5%7!teo!tCs;hi@=}QSeXl*WCA;dv$qdJc~YQ!Zabc1~|w6kE}JLKE*3>O)X6hE4Lg*0m|D3AAB z7LVKzQRI7%_ve+I9+R{xlkw6kMo>IYg#qLN06R%g!!M7cNpAzT6Q8dE>sdbJ{q+$4+P0zzl-}%5)|+ji=;Y-v`gDNP@Tfi#yk~?&sDRR=Lp1a!fee!n3QH|j^DvVi2xZ+)O+bwu2Yot zoi7_0rZP`($%>nmFQ=Qr<&E;%AbN?8rY0$GdGdYh6+RmS1=MbNwl;qC?tLefQ#SiH`Dv zJ7QF^+7_Mgp6-cIh}YU6!>nA;r(yP7{mx{C3VUTEljL|y+Xebk*t}SL()k;dbR`;I(c8S1jb!m%mWEFL5?(TR{%Oo2(J|9LI#U>PgQ$U-HH>ehbANyT4(R z_e@Zju&TU#>gi^6m8ZLQrtw*Vl0Oqv^CrJZ%^L#Qf(BP-aci<%u;uw8Hr48F2lGZU z?R-mRqbbkn9r)n~$D6fFe!bg?lCgJgCMaj7nua;^!o3)#wC34vou6#}(&NCv;XwtE zB$=09XGJY2%;ng^mknn7>e4>@>O9|R*E<~|ks&FSWnj@cc~+QlQ`MQ*)&A7f?YrcT z-Xn$%!h_cp(1+}gH)E&gAzZ73)E&IVJjREvn4SJj(Z^#Os|h8lL>cQ>Hvke)PL4=_ zI6C+0w{LCD#3{&?zwB|%3M(4K_OCmre)}!ggsld6nhI4Ds<{3AU%#^WXHY=YQHR9K zMCth*sk`v;eo*0HsjHxX@6~!43yyrc0Z{uqQz>bht7-ULK6TkC0d-i&Ml4wvXYYCz#yy?!zq|?8hjT( zKmiqR8O=CqsPxkbI*j`4cb{F5-xNj$)bw5^Y_O4_zRXTvl_7)E_TfWR;~lHl_z5z zPtPhfhzlMv;;vIt)`^m1c3<(CDIiv+iSiOwPFz+CdFa>te83=~v>LE2T>H(TJhZ-7 zYS2%lIJ_z$l_p0#u)19-dVSEVQGwQa)cU>)T0tR&wlrn6=?Q?NTJbN6pLac8b5Wq( zzEFSlUkp*QX(70#`4pt<@4^~YoQ=&IN3)UDeW3U`Qz-dTBSTjoTAve?RgVf`gL&?3 z2n6X^y+E681lY_aM&fknctK6CYDp$W9UUF3^Xaf==$cM6fV5U;IucJ{-m-iv?7_Wf zlrMC&zaXq-xut86cHm^+7_;yvJo1Z6s{SuVP7#IS!m-*I{F0VYs)r@)c< zkH=ZC=z@UR+@qu3&IGm_iWmz+lWRh(PGO6U96c?tWa)a>)$V9sA*sNqYqs;`b6UVO zZ;KQTt)Ek&m<4D7qO(wwwFs14Py_aN?2Hckdoy%K4&s_zFbl@vw`P$&)!flNLTfLqY0&ua=Fe8d0 zIwaNEj?ULMHiFLjYMa}`;T4Mt79(5f`iTq)Y1}!vBhSHJ&{N#{EP|&v);5Jl8?Heh)1<)cpn(CSY!uwSEJ)BgT#=w=hOYxR)y-qA^io+t+6sT5^H}v>xxs zIHB|l)9irvdp;&nQ7iaV%-LuL6JRUP51}hBCMZ$C@6;eqbr5X6u2_>*CqKQBV6{gj z>2~-jdFJ!erl#B{)6E=o7`l|~m@@OMiq0x`;W4$o47VWq|%-pA-R|NFT~b-1!J9(&%W=qawI=uGizvRHYEd%K=-Z>sE&g+s15{d#zQa zN{LzobJ=K#yUkM^$xhQSoj$m2!JWL98z(ka%Mp?J_h9wHn}uB>EmBqID<-)JEKaz( z`K0`!qD*a8^zPn4;>P8~s|hslEOa&xKw%E9dXeqQ=Aq5#%7G2c5or25#(MO_nte}b z-;J9IEQ89$_jTA7Z|{^+3ipIEW_Dq|Ry+e!$D@Y}PV>9FhEf%}-{?>9_m~-fZ}#in-dp~U8m1r^^m#??gB1Pr{Pz(ybnp>p;6F1C3nI&AQ$>uUAKbEyC?4AwUF)(JOIZ&aChmE zh0y9tROy!{`fl~+o3&^1u5A^1D036*v1Z8Qm@<2UAk*F2(+bM!(U_p0Nxax z0fl?T7E(Y1@PYyav^DTae+|yJ>#A7+JBp4<80ws+)KXtJD?&oC5OZoRS~?y(OV8## z;1i1q{m53cv1xY)Z0rtGaX5;fD_M_oNY!Of{Q>% zTc_VOK`&7njT*<74SqP~@aLoRUP{T8vdC}AW^%}k*E@=r|HXgZm~6e{Y`Bhx^~4Fs z)+G}nqmNRX*cKha-@i$48J4L}owf_&i}Q4_?B{eYN2f1*>dD3>J^Fz|?0y>9xlp_; zS*h7B_c3}4gkC=<_7Uq03{G^UV1nQ8uRa7gL2Hyy!~@gcAezt~Kjbk}KSviHNEj?c zbmtv@JU88UlPa@ZqjzL(ajHZX5dr@l#oFL_v zE_GNzIH;Iuxq*D}2G&vfzSB83MiS#xt8!$^(3#T3OO3+Fdi;?na(BwK3gn#A^qlEc z3w+EikH!_NImGIS<-@J~Vr+oDi>gaoCyEtznJH#EoV#lB`l1*G!*LApZ!m~=`Ea== z`V<6aHbz#a`Ekqli${$8DwABhyx?Gn2K>w-VIEfAe!l&&_Smmw3L}ZS4pDq5EXdvLtd={_za`B=CKq!Sip2Jv=Y_FGl(^5YzXK0Dw)5b3ege;$!ag%maveHWaT7;?WP&fUs?)_FUblo;r z*vy9TvS0{AC7wsldw4xJwgFIy(U?q@KX8X+hup3_I`2^-=b8t3b@~IaP!k>9BugJM z@hr|Rt0G@z;AX59WF_N@xxO>@y2g~##}0CFEiB;^hV%K>0x!s@!RoKt;taUkQh;Dp zufywALkH|p$vqJIy~WPBuJ!?SgNMmLW2qlkVe3<$^v9e+U0vNshKjGE>(Ud1&;DsA z#81@?+@mOg3>Ub8(P%YrcQru(jk!4Nfnkl36*BNA9Nf_IxqA%?QO6L5c&O>T0HQEJWI0`XZu?=NY1KQL+sg$qAyPp;c(P_@-Om#Pmk{2zdiOJ#>Mew~n^)y#wU?S==U=^U&)0eK+IwCoM|n#YYl1Nb}mG1hkc*A4m~vRVU9zdhYd-a99S zgu-ZSLN#z2!W)Gic3j6r!V{)&ZmTr0kXwMuNaAT!4~gsEt$;xuumaQqm|mY(g3xzP zzWV-moXDrAEM`A)*NB~hI6OWkX-I_+YDgQnoIn8r>M@I|V9hgrMV73(G9ad6R4fPR1}cicX723TrdCUy6HGzynrM-nsbn8?_;PFg%IZcf<1o< z)lW(Q0+g8tS4fnZE07i-2l^1Os@{)?UqrH;gZ%jXm+SEA-uCVzh~(*iu9CPc|Gi2z zGJJ959wdU}7z%`()|0rhyXv#O@DlPFBvl>{gTov6j};-ZY7pH1xX=eqyXw4#;yQ!C z6H?Y7=t*MCL{z1D9^67PnGIlsfO_ahO3hi0zkJIG56g4U3}1(+AF*5a0F81F$K z(;y3`&W&gJ3zq#9N|Tg=PU?%Ba04av6$oSTLm&l!39I;XivK%P{1>eFDHM|*B&U5^A^4dMmMqImDv#tzwkMasaxb1XFM=Y))0VJ3s*odkm>eKPjKL7h?u zq=g=&!TbL{7H&1tTjUm(1L74VCK7`(prIz8s1B3{N%?ozj&nrsqo1E$z660#k_Mxp zXMG$EBO8?=X|7&!BR%L<$XAgXkCPCEm8H+^e!PgTXGL!>7StE*I1b5d8<2x9s2rBu zJyf{{LEIo^@qas(OM2#d**&*%g^{fK^2QomCtXG6i~u4%eg#X)L{A2(qZ_*~;jD!bY%lkWN6U4%y}o};?E#?7yrH|iBs(|<2*WKqZI%B8gh|F{|-cx z>hC)IV^56QZU#>y`_I#0D_6%@PN1g#d79oblP}i(6e{A`{|51vs05S>I{3`h2l7hf zFRmfoM@q%fC%ctOlD_yXPU`9B05gvdW@jMR|Ar5w98ZA>14)US9FV&E{}~E9tDu$h zWYvj(-`@u{Njl?b1F~Ds{&Oe3Y5ei)49c~U-MBqVFZ_s{v>*JtE)o|5y=v&dr2Xab zRzDm%d7-+3q*?G!OY%qOj;~)aL~&~k;a&T@ zMS+BK$i&H5%D+pCf=pW$6?geY#q>rkAw15u94H*kc^7PXpHa^G42X-Mmx-((sp2gB zB7{@v129#>SHI)KeAb28IAmINIndTql25fto!Z&lQl8{6)@0?9C=3s9Mz-%xzXB54 zKj;%0mMh%meh%^&JQfT~kU^e9XMMhSJngN%cWcN`LMX59k>OHJiTNIZb@yf%_Wn9u zOg_;dtNRi#xy9NF7ez2F`xl&^o|*+Wuq>K^D^#bX)hw{zAuW9Jmsj{?Tv3o)O<7)X z{JSAkFhy{`8%d9pG;W&--S)BGv-m|1E(t+Yx_7y4R^XabTEWOc; zSj!wy$;6rQ%?SPML$6XL8zk`h`qv(6?oV+->P`Yi&t5L3DOPe*86O7d2!wJRdl>l! z2KtkBcxPe;d3bP{Jrc7Qwo>-x3~5fKRK+hq@mi!N)qz}O2J6nI@L0iS6OY1nejy_F zEh&v7Q8SJ-nXP-p#)6=kQhRROT<1+w5H?2I1S40wsG#H>ZGuHCIk@^}f zY5;0dFiH_?7Jk&selR4h*ArX36k6^u=U?8mD0=H?z-}Ip*}(vPAhm@Yv}J0@e*Y>$ zF5*4{U~jLvq~PV-(q}u`5BC*ZpFdSS9+Rs>I-C^}kc%lGnUyv?7NjiIyEyp8R_fTJ za$mM7aJOLxG=dPEfC!tKI5jDl!oJN`tKG`QLo)-4qopu$V2D6JT`K1?;&z$ZoxPUgBWwD2R}s`OKkBrR1GBQ&i^xh#ymaD53oePss%C056( zr7_B0r!V6`gX!uGqf`zUa<{uPsWuuqvraW%mE?A`DH#Ge7#}{G^p{OtuH=G>vOLf{ zv>RC6Yl0!f?3LYnGrnqAJ(uk6$MrJ^Ys9$Tg1Ylf_I%UfDK>Bybujq+)|H)s(3W7< z@XkPfBE`%`^hi?#GEk=%)rH~|_0(_|X@FxxWRaSVlezV%fxRc^;f7OA*cV+2H4hRT=VB)6FnhGiAUY zcukG>wAyU^w5V+@3 zwZB3rf&0LUCi^SPc_e1H*M7pAx8QiGk|pd}(PV#ryqiN!TXbK11*|^ij9ZGtCoY<} zioFFmK}pYJf5tdxE_sIjjN8h5QdK2KbC8kYn8)fL59N{l6zKG7v@)d{gIn+6*Y=|i zm0J$Pk#r(QCHJO-MtrCBtf-B|(KbPbfrk)}#4chFy~Rz(b9I__VYkuu7R#L02R!^& zX_zV)u3WTjUQmT4WJA}@M}CU&j2tK*T%9A9aLnE$ZBlh8V)2JG3drkIicSh3wR6kF zEI_d_;AqPzZ6a?HLKvUtn3>jdHt~#&cDz44D?6sL7WKyL)t6}97$~b+Mt6o>K}P7& z-XEo#u(|~e5n(Z|5lu{OIvC5dbc|nOa2YIv}6LVr-wT}x=U@+ODn=2#DZyH_kg_0pT z3}rht)xG*&+u61qU0Xwlqdv)bG6MmtoxUkN&-W&VCJtqZj_31DYdi5gSY&ji_I1~( z(%nD5j-Uc=E-MY~N9+qcP*=LhUC;heg+cc*A7>QeV0E1^Q#Ns!;yvb^DN$P-SBcz{ zlbH?+v!RJ6ok6G{an_gO7$Ikfu4Y#NV+a=>dls?cNdNeR9yvvs{5~uKDSKP>Tq!T6N zSza}SO9Y^+6Y3wGo;SSwuFdTx8X$Yx+-+wD&!db#+`PB%Rc`S3NvH^K+mP46_`y;4 z?d@eGR@Taxu1%uzcM@&m!wsw~mzLf5Tla$;vt_V85gWYBvL{dpL5!}2}?yA@JB8W>+Iw7J-hr5f3usNR(97M8{yY!~lNk5ix} z*r?@Z3^$5H!x<~o)T>BKcR&P_BYtx!hH|0V|N3>NV0Bs7N#}F=E$UUSBPiPAWe9DF2a9;)nz7I z(p)rvmXBbEhcofU2Kuvve6D<|bh-IH(Pk=h8-x zzTRKnYmA3QKrVm*we!a^W`FrJ;um z_6OYrD}z%8P&WX?ML%j_5$0wpN6gI@`pPhaXmgeQ;R}Gj1vQ&d>xw#zj?WzgQKoIh zN|q9be4s%E4dm_ggI>F{+>IG`#grLG?8aZ9Cbxg00A|lLj8(EX#`o5G6w~7hwCo?e z>3Ta%M_eAcHBfAIFcQ14wYbgx;1ywsVC8J1!$tCv1GqTIh0tHE@fgp+dJ`RC&QNPD ztT70_MfV80bH=Rhy_*aAwVrk$gq%-wgE3RiJO}K|&i#QaEbFF9F=7riF*hez3ep0j1#K2v zGK`q{Q~ly3+`2JxcUfSp0TFzL+movXO0>e(AQlKfm~Vhc`Hf$}8%N((A{aUxULcW;)due8XZtFVc-tU@UB`vK zax1*P9gsT3(SCr7$uZ}PY&}q7#diZYSI_h*b*(xm$RH{QRgr#iV`V6gxfA43{{N;`>&7khYXKlzHhAY+Ok%de!qcmbiTSQ*Lv<@n&+ z=pudBJCT)=lUN*`v)P7RPX&D^uk6IsCLTw$aXX05-17|em(7J9!nCmS-Q*?*;NTA3 zq&7gRJYJtg(cMOV7&fn%dfabQ<3RNxeK}&pYigi#g4n|&?Qy~?HhGv$*==uidB?k| zeL~!}p>@-YiBLiB%qMta18kg}B3lP-3vLw4>EyeiGKO6G6Xzw}*84C}e0yi=dSDOr zsSiiLGRQXqx3g}jP36~3A`N(J3Z+KAOf{2jD-V2ODNuE6ISL+l+)?fQC>7WT)Np7z;&A}8ctz-Zlr3ix5!_(*tX<{$-ySqLavRzi&GblgRbwSji#5| zL8iijhMsQmS`F^nOdqFYvHQS)T60^rw!8aO5R3yMS?am!W8xdgvq$Lfd@2*pDZaM% zuGW`^e%I#kCw`hiQ^4h@rgZOm%YYbiD{iOY#7yfD)MYwXgx|R>uF7cYj*}RyCQ_v} z#5i?>k0p#PMX}LX{x0l9q)J1GK&m4z%NMq%ij9Wyl(5GUDnCO`rf%@CAlRl98ujJZ zRo;cGl!tVuIC8Vtvwc)((_!>q5VTM&wyPEOr|q`Y#Hzn{7hLGec?sC!WrbNi8B_Y@ zsUA7BYF6rK6#xJ`5u184)$P$LKQeVbvWGU8tJ&x1{z*|eMKdQ_^>`|uEb zhhA0!7dH$fBE_%kEqNk$Nhyy$fo$2^z&FSDgr6Mqp1i@w*`)G=>fis_W! z5qf%V@M;m4u@$T%^~GC|-b2Ir6%B$KJL^-8;leH>;-zaK=qjes2GGpOmPjZdqJRb>XO8+?YiceRhx=&-aY^#=z3zk? z{mTUiBGZ5-D>Ry-!X8L8h<4A!#rID z5$9l1x$8U%+?R~_{6%R^bt?C(w$8p@eKq&Sw-=U?J*#t+b19)` zJ=pKukx3wv0urlKe zqu#zaK`ZU*?2JJ(M4?T6NKO|kJW@z5-wju%1Y@<0;d_*CxVF6+T%QyDP(9yyykGE2 zeKw;c;Ia&*hZ@P`-dOKhpoMUr#t;c8D{(cxyWHFO zHJb5?yED@=UWu;_yU@?5SMPXa=~&#BRCXwyufW>)nVZ4eW$EGK3{}wgSRD0SzwL@J z*4bPvy%JyTY{N{B+ub6rAN)cWCL7SjS5@WgVz$CIwpN#VnzGEY!Y(71uR|-MT;Mxu z#n)TQidGgoD>9ND_ygmiP*FjfJ9D#tO5V#PZ7I>p;{5eeOV_`USc5sGjJa^l7bHy-rM7xlCcE(omKxt%j48YKna= zoVG+Q)1dTqrTjH!Cyp>Q?7Ju7TyBu|l%8c-Sk{<|x=N+)faO$ENw`y(Di^HmOF^*C z9GYD$G;>ll|23P8+{d7RDFy~z26DARLkhM2$jJfNOx)@p26vk`R>vuEXSeBDdT^4?^ zTFxwcw-(uPZl~;DohmZ7h)g)wo3{L+RX@jbePnW&){t20zUPtqNp0GDED*V|MHi?_ z<{&T)#e$Tnb`4qG(ph7gf zz{Q|hx%7~k^YilF^79$Fa@)#14r|MRr!rLyOW@>`w%_U;n6iTJIcOJX5U0g3D9OJ*eepjN%{^NCi6C)OLxh3d%W94T`HHg`zJ4d3I~?|Z)GjujF(a3y;g6}{)wO8 z_1xQ`Fxm4B9PyppDqOzASUfR3xMEPd4IEkk3FNGQG>5fEufABAyg}8$U$gvl;?KvU zVTD|YTC}8P+*qMm&G0)Vl&RtI$LHg(B+I26Jb}lMvt2|6vFmoy5%O=%9$IZ1RaX3 zwE%&Dz46;!owk?(K(M{t7|UT6Hzcw?-N-^Z5e_}w&P}#+uxZ{h`2KFk=G?Wf{kW@~ z);j63W9W60!D~O*EX{PX%?*9Le%t|Pjr>&}cDws8TmJm4N;a~S1E*1pyhw=iZc{17 z=;ln2Npnb02*<*tN-ieLw;;K~D)QV^bCba@SjIbrsW03h^vnXdCg^$fpOx}Y~D}xsx2o2czxI8puO%$t9gGq!Z5WN`9&5 z144RA(cQ$2ZP6ZAU}Q#l2w8(Xt7Mb)dVQ`a*W!(@m^I7!iKzixd1PI&+PaVg*S39{p%6#9K@(MxaYFaQQ#pX z2aII*y`!NtGc!40I`3{S?JC5h`7?>jgEmswC3584Z9t$u>LY_vP%pCF?^l`4Y4A_=qrv z;JxzbR_5}7qop>u1ipRGwVc1;NKX!rM8*>1@vQz#T&oc)4U;}d#hnG^#%?*IRJVQj z?&Oc4o3J6X=M2JDIM>F-xhvBx5f5Eb5o6nK1&5&Ic;`D2#-NW5tUOV|CaV=-me*Z0DbQ*e>dVxmBrX<}W=~wGUn1B@qGZ3v1y})I4 zIK6fqp5tn6;#+)ZJK);$Buhs~zbms*k~+s`*jR6&xX;Xdee08ulCT6OBd3RR555qY zHK@}U&#x^GOG#cuCudo`g(IvS1)TGmAYObz17T!1j@i|lL0wC-tSw(WVG@l|6EiS4 zRR8mn(xtk9pq&!X;G|lfX9Pr#gaK6;=Z)f@zk;x~;fkp{cRp2~PVQ9Oy z2her<&b7@yP0HKTHnmnU!ts6@o<*PfWXx`q6EHowWf#xXXno?oJqaru`*`2Clu#*O(Y8@?~XIbgeu`6f-_ zTzlPDlvVE&rGK$DL-h_D<)@mDW-`*^GFG|^u5qX$%Elc+<8oExusEk4v%~e8g7yl{ zodL^G4(x{RRCty+v1wgRKWEo-lP0s*_4~XPPt}V8oNu9iruv@AK|an26j2_@LU*GN z=NAMsgCipqn(Kwn3N5R&%6oO3ECq9Q%H@Vd;eA4 zWq*QGk}$4fAdjHqKjNl<-6xhS(H6t|CWDTK z2VAG1Yn>^cVCCYLGhAsDD-eobs5Hd4`R{oNG4vM>xDdnMdlWI^$_XnH?RWQ}fsFl# zmcE7Up9~1^iUaCpal1O!(b?SK24@OJyh9Tl{`F{N^vd4j6I<~TZlj6`%L6cqC-12~ zo?@b+7P)MDxDaaT3H7=pt>1>`3@eX{kp~+tzMmY6Z(m>Y z-%0KLE@aX8^TyOXU6WOA@#*?3k@xl&%;(-%Fwc{3l(h)z?y$)XZW9da(UqMEZZQXgDzcLG{Ml42*l=)>?nEkSUdJyLH zJBfw`6tSQo6TNH1rVz$w%I4my6SM^vUh2TeHKgw9j8yh55}vn4JKO$I>`XCPC+ZY# zkABb1(Lwa50uwQZWf=bl{|qftUKA9!T-BB3RX)P7QH)#YS{<%p7$kb2-zOu}0+?J7 za4DL~x63ZH8;F^oOpf2q&t+o0@Ex{HvJ!+|Cru)?-tW+QmMMM#{&IVUCO%;2b^lS-z9QdNR{Jpa@b!?e`-C@A#Bf{V z3id#ItD(qxe~j(D>fm(Sc$8ZzdLgnL~kvnM(!8Khf(8 z$e93I5qNid&<=JEUR?5K10D1f>8HI9LR^R4_#nHv2z>Yfd}HkgQdYg>sI=3o1wXN& z?tg@zKWo&d=or4gz9JO3aL+$Hn;4O2xK`&6+bDiFB(RBYg|XU1PTw97^;#>s2@^M) zw}}_&vFgPz6r?Fs%*?`h=J?^M#(fo9=#Kp&AmO=a3Vdjz;V!EQQ27|BAaNi9dy+@6<+Z`QPa9lDZIl6t+R=4Rtw1 z3?^dQA<#b@ilr@UsVLupPs3~ymx`B0+WV0shl}sm+uSTWarh$^HOmueJS<6@oOifo}05vEmO^D6dc0p@npKXYe@dg??2nN05$gQmvwxZ z2eP?w`$t?L$NU|9#FI-J?|5{>i5eq1aHM7srZ%Q>hPiswHZ8*39ZR|$I~ z2NWJ{`f&xzF&kev0_x;yzdDD(EdU!PrIlwFw}0@bbHK9}eVcw&WYC@n_zjid~lMSdNcz)~I^8tJ^uTcdl)OE=5guJNoUw)#k!L$V5k)Z~E1P-=m^dvPUX9ND-4&9C|1CP2-m^SZkj0VPwRk7{E14&QKfDBhH_?|W(kHWb3vZkXOV+lf13fJjBKbI@Fy7!%(9Aj)$ z{cBH4*nGJ5eC4=oOa<24#Z^%yD)L|o0=4z%pP~z-!9Q??t%bIgdDB=aZ~oz_<8?Fp zu%_DSogr@AKG1Bc-)D@f@>+h>pQ0KJx3rmlnj0FQC(!NZ^y5MiT``|th!QCdE^n^n-_v@{MZt)rO)j2JQSsoufD zpQ)D98dAzD=frLxrnZt&a#NIOOZhVBkTsp<5#v|vcE_DeqQWK0B0ci6+eyL9udyeG zeIC{Ll3ttFZWJ2f4P9+#4SEWu{#`R3jXx4+svrqrsUU8)R2I-W2ql{Onq7wztP#rm zWVl?#!E2?HuUeXt?3$^5wc)M^3av-A?L7&eETg>`%}EeClEN5|y)~a}r?{JYe|*93&@WJ&~n`}M_E3R_xF zn_W2JSnpPELmm8y9q1Jmk3p&Z$u~l~#n$LTB&2-2UBcdzN>7uiYI_XNg0;BrDMX(u7xe?Sqd z%v&00zj|(h?lO++Jp^hKJ~wWCOQtU(`j`nZU6*qm++WHbVRn>ZbXqh$9!cA6RsE`Q z_S^dp)c0Ic%cTu0?ld&R3Y$Q0SjyGSF|867Q8>2;?~mVgo2|QvC1s9W!WtwSOYJ1(c-0 ze>5pc@nmDRQHQY{GDr-B39`=wEA2_e|6bpU5#vH= zgFI(awD!d@(@c?)@%DcolKeq-mEQ>R;4#=po*)q~pyiEU`?N(The1}Z?2`jizk zQP&nk(SVxOKuY4rJxJGXGX~5meu=xQY67DWSlg*>v{jY&(Sfm@b&d^A>+b z8H^U{OotoLY?FF>`hpZ-ijBSjE1^JqI0G}G0C7Tmq+RHJi2uIb*A;A(E=e7f<+pOU;``IDz#^?410))cOT0?12B1V2 zi84rLF7-8jN#1WeQUJpBzqX8Cs@XdM(fre7K_0GL`0*NyB4%Jn(pCmJyowNUTGo^t zJg?&HdQIUCxVAX?y2FK*vf+0Uj0Jb(l;Zy?6E`1oDgN(lrar49t$x_v0GN&YxJy7*6b zoo}1^{O_WwuLuXi_Cj?AnA5+%>~|frBZHu_!L25xRX=m`&GWg=@n6dMWDh~@rsIiWknPynq+ta0AAf^kmTHT=(2>%aK&A;KO5Z#L zZ$(9j)R+kw1i}v*X3sMDVQKf&YJm-;4cv~@=UXEwu{F)0SEi)LtLFiBPt5jNVF)YO z@<_eD$m9HRB_9Go-2|&NOwRAJK{#5a@{&fiC0T{sQ*LG&Vl*`E;p8BU@ zu0QX72tNgZw19cc`?LZp6R=e{Q>{XEDS?N2DQsH=g*&P@RKhv-TUf2tM2Aq({|~t^-*FT#%=Vt2zWy4fZ*R zmBQzXl9wn(Bfxh#Wl*1rbVpdlIUp$KIuECHP+;z;!ljdX>$=1Z3edf;6p4===Mrbe z!8-;P=>IF~$^)U^zW5g*QAEr`wwEPjDcjFFQc~8k43Q-jis9MGjG3s{qKKl#p2)sb zV>h-eg%H`75vJ_RjBUdB-Klu<=l6c^z2}~D?m3@x=6mlU?eFZ2i%7WZ$_yzkE=21* z)T}h{qK?fhLQ03vSgEM%E)5MQ4uebMYSSB%Mw&TtEp~dO4ph(tks0|xC;OU;TcZTbWu5d5 zz8!0mzBx%fGUQ173e!o?8;17=Zr^ngDb6;}Mx6|Q{s(x|_G zy(?KA=@?q@9A`$GmWr5vIXW131~ByFwG(UybyWZ-TMwtcl5;)Bt{?JnFDg@RHYp)ybq*zE9QI@2zS@-4XQ$2^Pr~wvhtn?prTb z|5mM5Kq)pIlJm%v+;;Wo$&D)+h#3Y$sCuuZ%de(wuxUV>VC>&ZcFI~BWq=G@Ȱ z?I874hPkxfHrW26s*<^I0lyU`Ng$%DEtkQK-n3TD{UZ4k+{))!@_ol@kLVQEUagXJ zbd|N*boT<)lsBA(;EoU+dJJkG)gr>orV8GPD?`42B>8W863jsZs;w>JbTkJ?^x)aZ zDzkX6LOEf%wiKzQ#YkTRRRN{EcCRzHOg5|Qs>_jHA`3}9?PBg4%z0DCqiLzAt*PHN2}ce5yhHX@DkWD2;yacz=9QWh2tPLz zX1A3JZhn2^As^rV#ZGnN*?R7Ul&TfjRmFPN=1o=9`~?y*vU#ZwNuht7d z8cMVoedUpD%(n4FfRsMM640Vt;>d#g%Pz(&5Mc4v<1jxt(V8nI3>pFMeYQOqWEFw(qMwf7sn9 zQ3IH}C}QFNzVdXEV_T-PyF*Pq;Whd>s|?f1j}eCgM0t9h*u3D5!QlK@SU?MF#>nGl z#lY*wNIHw9L5R>66xNh7Z$-E@?ZJw1LAvHvk%n&~3d6|wE>`D+lmlSLoetafx4zcU zG04!*^JfE!y_H3##DRkVz;s0LdaF!jf}KEfz0%Fv{P~94B7c7D{qs^dvVC}ezC8%j z-G-jWDQH>fQRiAtnmlg?vE8S9Omhx@NcGkgY0`w0wLrnXk(|@|q(d#mIiFdlM1J-v z27R$kQCPK4nW@2;ht!5BSQbo4YIkLQf7W}uwS25J?+mTEFW^VWcWJfQ9p_pfAT>RE z4j9~<)k$qvWM|utB;bI3lP0PtUcTd)ZGP)XNF0BK$p>Ru1%++GTk-&~kRy+D76!d~ zTP%H^F!|kKq;8fJR`~n0zIWF&`df__!lb*dFe4KvxV-G%LJ?n>TS&9PysqcbUla~T zjl9J{Xkcf}8TDrDbWz#U->-(7srazPJ8t^6ciYQ&a%TZ zj3QugG^-QqE|qsHhVq0q-85(rbfDtG+0&MgZX~LkV)g8x?Vv#Q+V#$yb&fT&fuYyB zcZ^AWWfq)4i_F2SRF`mm4o1JzeT+kI@XG2%5e_0UJQT1OWK)t<(q56ZSY}lc*g6Gw zZWW+%oXgV&M%6p(OB0WE;SM6=Jj9KkFiRfp4iQOg$v>$o?NLl^Y$Zc zwHnIQD4!n_MnkP;{3oJ4iX7g9e_^lH7|(j423IpY;3DiqpGZ{C1TQHVr1iPquQJ)u z6oG^9f3mTE`B@O82f&97fOrSlHoi8gvq3)M!Cq09&fJQXzx1)}H0=0O%BDJ2rfwj5b_`N+xQ;26R6gi z?NeEA$l?+s2V@c|k@Q)gJrK3G1#yrT9RdDw%!qVHJlcp6d*%G-(6yD$LGZG`w0jdHe)WE1qb0`?nRymwf;!d0T{ymQPdH5CaV*`Ex){-Y{5}p?!%C1--6Km z>&I1HstXaf`OQ8H4VxoUcG?ID(Klywe=2QZV=Pd^zo3a1P>i z+rBBf&DK!?X6W6vGGOjN{Dwc8M3s$`y@gsdNB~f@$U3^N0w1)j6auM&x&gmj;@{q- zS&afE^-n|gEy2srLZA|Wp?=R68d3Wk z;9_dkP%DG1hRf>Qn7o3_hbjD`;B~F!(4xR^dd!o}_V(oaUQ=l_ec!{N*!s$bFqYY# z9Yrjy*mBIZlu3RogJ)1;xJ`N~*^+60gk9|xEV@oN9?1a zUaL7Xdf@>yJGoop+`|+iQxV%AgURci+t9lYQj|WH(~B;jdwJD=E6%@+GNl?`dT2G7 zYHS>gufuWhN20fL|vx5wcg_l$arJ#Fi$;N)vBX#HQI@>RaWB7kas4 zikK8l27x_vw646n!taX(3JRvz8vypxO@D`48<=O=eD8}-&zw(J7t;|p+! zR0~fF)0sYh1mD%0veSqq;N?GWEI%nfCTOyeGup|SbK(9Z#-@m~4Py}OAqa?`}OId$3TrIdh?%t6BC>D3w1Wa%4@&bX=@&q{*uilGMIOFu+- zc-!d#j zRQ?0SX6HsEE9xZu;Q zPRQN;ZhEj3ZOsU+dJ4@5;b*jgi&iuCSWwR(ebZl*T(laxr__L)y$xJff>d$;iZy!* z4%uEXpC?&A_z?K;^^On&0V-bb#&J7qI$Z1!Qzmh7u?PtgJ z8ZrEk)16IkG3jgPAgn}JuRI^kOi8puVji|fMETvMu8hWr9YYBGCXfJ%iDFd|b ze@RbyDxUvunRo!SV70hwOoBiS6I}_QHBzx$VzdYl`aw@&@}B>#C|I9)wu`#~qfH+y zPu4SbDIRn+1j{p>_ruDA)VJ4u`e3|`@`v{*7+|JO%j-YfiJ^D0X)O=lqrmM0eUHXX z(7RYXNR+PvCpS~Mfq`*J)XuK-KD|{6qFAU_@0;*{T`oMWk(ajJg0awBxPfNxF2~KR z?XrEv)FKCH&@D*fm)jdn@*c~K`9vee7qmb=V%O96sJ7*uE3D*`rL{7FmZuh{;rn;l zpNd?L9aW@fr`%U3Yf;-tJRf(;^U%LC`+%26Pk@1%rGZ{OT!c9<~I05Ef(@p^xg@ zF`eFNH9_>wxqBn(dP*eo?2OQvEFi8S0fY?hx`$41g`C^!;{*@go;YIm*5iZ&84LWr zWk)^Gk}8yD0n`=b6d7urGnxGQ6dvpdg9p%)B@yvGuI7pTPEJ^Z^R19u-NU*)-qzk8 zKh}T|1oFamv;QUYUyqsLn?P9`AhhPavz3UxDTh_8F9DHTFkN+aiWI W%TGzlTG`++bV1+rY=NFr*#7|=6urg( literal 141952 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 SNwPpq5P2D;XT?vA-u@rtadiX$ From 44d72dfff90c910b4bf56b82e8e13ddee7560e0f Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Tue, 4 Feb 2025 21:09:03 +0100 Subject: [PATCH 06/11] improves on the nfregex binary x3 --- Dockerfile | 2 +- backend/binsrc/classes/netfilter.cpp | 227 ++++++++++++------ backend/binsrc/classes/regex_rules.cpp | 28 ++- backend/binsrc/nfqueue.cpp | 52 ++-- backend/modules/nfregex/firegex.py | 3 +- frontend/src/pages/NFRegex/ServiceDetails.tsx | 1 + tests/nf_test.py | 16 +- tests/run_tests.sh | 16 +- 8 files changed, 217 insertions(+), 128 deletions(-) diff --git a/Dockerfile b/Dockerfile index c576406..dd7cc14 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN bun run build #Building main conteiner FROM --platform=$TARGETARCH debian:stable-slim AS base RUN apt-get update -qq && apt-get upgrade -qq && \ - apt-get install -qq python3-pip \ + apt-get install -qq python3-pip build-essentials \ libnetfilter-queue-dev libnfnetlink-dev libmnl-dev libcap2-bin\ nftables libvectorscan-dev libtins-dev python3-nftables diff --git a/backend/binsrc/classes/netfilter.cpp b/backend/binsrc/classes/netfilter.cpp index 89b0417..b226842 100644 --- a/backend/binsrc/classes/netfilter.cpp +++ b/backend/binsrc/classes/netfilter.cpp @@ -17,12 +17,42 @@ using Tins::TCPIP::Stream; using Tins::TCPIP::StreamFollower; using namespace std; - #ifndef NETFILTER_CLASSES_HPP #define NETFILTER_CLASSES_HPP typedef Tins::TCPIP::StreamIdentifier stream_id; typedef map matching_map; +/* Considering to use unorder_map using this hash of stream_id + +namespace std { + template<> + struct hash { + size_t operator()(const stream_id& sid) const + { + return std::hash()(sid.max_address[0] + sid.max_address[1] + sid.max_address[2] + sid.max_address[3] + sid.max_address_port + sid.min_address[0] + sid.min_address[1] + sid.min_address[2] + sid.min_address[3] + sid.min_address_port); + } + }; +} + +*/ + +#ifdef DEBUG +ostream& operator<<(ostream& os, const Tins::TCPIP::StreamIdentifier::address_type &sid){ + bool first_print = false; + for (auto ele: sid){ + if (first_print || ele){ + first_print = true; + os << (int)ele << "."; + } + } + return os; +} + +ostream& operator<<(ostream& os, const stream_id &sid){ + os << sid.max_address << ":" << sid.max_address_port << " -> " << sid.min_address << ":" << sid.min_address_port; + return os; +} +#endif struct packet_info; @@ -52,6 +82,60 @@ struct stream_ctx { in_scratch = nullptr; } } + + void clean_stream_by_id(stream_id sid){ + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.clean_stream_by_id] Cleaning stream context of " << sid << endl; + #endif + auto stream_search = in_hs_streams.find(sid); + hs_stream_t* stream_match; + if (stream_search != in_hs_streams.end()){ + stream_match = stream_search->second; + if (hs_close_stream(stream_match, in_scratch, nullptr, nullptr) != HS_SUCCESS) { + cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; + throw invalid_argument("Cannot close stream match on hyperscan"); + } + in_hs_streams.erase(stream_search); + } + + stream_search = out_hs_streams.find(sid); + if (stream_search != out_hs_streams.end()){ + stream_match = stream_search->second; + if (hs_close_stream(stream_match, out_scratch, nullptr, nullptr) != HS_SUCCESS) { + cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; + throw invalid_argument("Cannot close stream match on hyperscan"); + } + out_hs_streams.erase(stream_search); + } + } + + void clean(){ + + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.clean] Cleaning stream context" << endl; + #endif + + if (in_scratch){ + for(auto ele: in_hs_streams){ + if (hs_close_stream(ele.second, in_scratch, nullptr, nullptr) != HS_SUCCESS) { + cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; + throw invalid_argument("Cannot close stream match on hyperscan"); + } + } + in_hs_streams.clear(); + } + + if (out_scratch){ + for(auto ele: out_hs_streams){ + if (hs_close_stream(ele.second, out_scratch, nullptr, nullptr) != HS_SUCCESS) { + cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; + throw invalid_argument("Cannot close stream match on hyperscan"); + } + } + out_hs_streams.clear(); + } + clean_scratches(); + } }; struct packet_info { @@ -139,76 +223,59 @@ class NetfilterQueue { } - //Input data filtering - void on_client_data(Stream& stream) { - string data(stream.client_payload().begin(), stream.client_payload().end()); - sctx.tcp_match_util.pkt_info->is_input = true; - sctx.tcp_match_util.matching_has_been_called = true; - bool result = callback_func(*sctx.tcp_match_util.pkt_info); - if (result){ - clean_stream_by_id(sctx.tcp_match_util.pkt_info->sid); + static void on_data_recv(Stream& stream, stream_ctx* sctx, string data, bool is_input) { + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.on_data_recv] data: " << data << endl; + #endif + sctx->tcp_match_util.pkt_info->is_input = is_input; + sctx->tcp_match_util.matching_has_been_called = true; + bool result = callback_func(*sctx->tcp_match_util.pkt_info); + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.on_data_recv] result: " << result << endl; + #endif + if (!result){ + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.on_data_recv] Stream matched, removing all data about it" << endl; + #endif + sctx->clean_stream_by_id(sctx->tcp_match_util.pkt_info->sid); stream.ignore_client_data(); stream.ignore_server_data(); } - sctx.tcp_match_util.result = result; + sctx->tcp_match_util.result = result; + } + + //Input data filtering + static void on_client_data(Stream& stream, stream_ctx* sctx) { + on_data_recv(stream, sctx, string(stream.client_payload().begin(), stream.client_payload().end()), true); } //Server data filtering - void on_server_data(Stream& stream) { - string data(stream.server_payload().begin(), stream.server_payload().end()); - sctx.tcp_match_util.pkt_info->is_input = false; - sctx.tcp_match_util.matching_has_been_called = true; - bool result = callback_func(*sctx.tcp_match_util.pkt_info); - if (result){ - clean_stream_by_id(sctx.tcp_match_util.pkt_info->sid); - stream.ignore_client_data(); - stream.ignore_server_data(); - } - this->sctx.tcp_match_util.result = result; + static void on_server_data(Stream& stream, stream_ctx* sctx) { + on_data_recv(stream, sctx, string(stream.server_payload().begin(), stream.server_payload().end()), false); } - void on_new_stream(Stream& stream) { + static void on_new_stream(Stream& stream, stream_ctx* sctx) { + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.on_new_stream] New stream detected" << endl; + #endif if (stream.is_partial_stream()) { + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.on_new_stream] Partial stream detected, skipping" << endl; + #endif return; } - cerr << "[+] New connection!" << endl; stream.auto_cleanup_payloads(true); - stream.client_data_callback( - [&](auto a){this->on_client_data(a);} - ); - stream.server_data_callback( - [&](auto a){this->on_server_data(a);} - ); - } - - void clean_stream_by_id(stream_id stream_id){ - auto stream_search = this->sctx.in_hs_streams.find(stream_id); - hs_stream_t* stream_match; - if (stream_search != this->sctx.in_hs_streams.end()){ - stream_match = stream_search->second; - if (hs_close_stream(stream_match, sctx.in_scratch, nullptr, nullptr) != HS_SUCCESS) { - cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; - throw invalid_argument("Cannot close stream match on hyperscan"); - } - this->sctx.in_hs_streams.erase(stream_search); - } - - stream_search = this->sctx.out_hs_streams.find(stream_id); - if (stream_search != this->sctx.out_hs_streams.end()){ - stream_match = stream_search->second; - if (hs_close_stream(stream_match, sctx.out_scratch, nullptr, nullptr) != HS_SUCCESS) { - cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; - throw invalid_argument("Cannot close stream match on hyperscan"); - } - this->sctx.out_hs_streams.erase(stream_search); - } + stream.client_data_callback(bind(on_client_data, placeholders::_1, sctx)); + stream.server_data_callback(bind(on_server_data, placeholders::_1, sctx)); } // A stream was terminated. The second argument is the reason why it was terminated - void on_stream_terminated(Stream& stream, StreamFollower::TerminationReason reason) { + static void on_stream_terminated(Stream& stream, StreamFollower::TerminationReason reason, stream_ctx* sctx) { stream_id stream_id = stream_id::make_identifier(stream); - cerr << "[+] Connection closed: " << &stream_id << endl; - this->clean_stream_by_id(stream_id); + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.on_stream_terminated] Stream terminated, deleting all data" << endl; + #endif + sctx->clean_stream_by_id(stream_id); } @@ -220,12 +287,10 @@ class NetfilterQueue { */ int ret = 1; mnl_socket_setsockopt(sctx.nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int)); - sctx.follower.new_stream_callback( - [&](auto a){this->on_new_stream(a);} - ); - sctx.follower.stream_termination_callback( - [&](auto a, auto b){this->on_stream_terminated(a, b);} - ); + + sctx.follower.new_stream_callback(bind(on_new_stream, placeholders::_1, &sctx)); + sctx.follower.stream_termination_callback(bind(on_stream_terminated, placeholders::_1, placeholders::_2, &sctx)); + for (;;) { ret = recv_packet(); if (ret == -1) { @@ -241,6 +306,9 @@ class NetfilterQueue { ~NetfilterQueue() { + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.~NetfilterQueue] Destructor called" << endl; + #endif send_config_cmd(NFQNL_CFG_CMD_UNBIND); _clear(); } @@ -263,23 +331,9 @@ class NetfilterQueue { } mnl_socket_close(sctx.nl); sctx.nl = nullptr; - sctx.clean_scratches(); - - for(auto ele: sctx.in_hs_streams){ - if (hs_close_stream(ele.second, sctx.in_scratch, nullptr, nullptr) != HS_SUCCESS) { - cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; - throw invalid_argument("Cannot close stream match on hyperscan"); - } - } - sctx.in_hs_streams.clear(); - for(auto ele: sctx.out_hs_streams){ - if (hs_close_stream(ele.second, sctx.out_scratch, nullptr, nullptr) != HS_SUCCESS) { - cerr << "[error] [NetfilterQueue.clean_stream_by_id] Error closing the stream matcher (hs)" << endl; - throw invalid_argument("Cannot close stream match on hyperscan"); - } - } - sctx.out_hs_streams.clear(); + sctx.clean(); } + template static void build_verdict(T packet, uint8_t *payload, uint16_t plen, nlmsghdr *nlh_verdict, nfqnl_msg_packet_hdr *ph, stream_ctx* sctx){ Tins::TCP* tcp = packet.template find_pdu(); @@ -300,7 +354,17 @@ class NetfilterQueue { }; sctx->tcp_match_util.matching_has_been_called = false; sctx->tcp_match_util.pkt_info = &pktinfo; + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.build_verdict] TCP Packet received " << packet.src_addr() << ":" << tcp->sport() << " -> " << packet.dst_addr() << ":" << tcp->dport() << ", sending to libtins StreamFollower" << endl; + #endif sctx->follower.process_packet(packet); + #ifdef DEBUG + if (sctx->tcp_match_util.matching_has_been_called){ + cerr << "[DEBUG] [NetfilterQueue.build_verdict] StreamFollower has called matching functions" << endl; + }else{ + cerr << "[DEBUG] [NetfilterQueue.build_verdict] StreamFollower has NOT called matching functions" << endl; + } + #endif if (sctx->tcp_match_util.matching_has_been_called && !sctx->tcp_match_util.result){ Tins::PDU* data_layer = tcp->release_inner_pdu(); if (data_layer != nullptr){ @@ -317,7 +381,7 @@ class NetfilterQueue { if (!udp){ throw invalid_argument("Only TCP and UDP are supported"); } - Tins::PDU* application_layer = tcp->inner_pdu(); + Tins::PDU* application_layer = udp->inner_pdu(); u_int16_t payload_size = 0; if (application_layer != nullptr){ payload_size = application_layer->size(); @@ -369,6 +433,13 @@ class NetfilterQueue { nlh_verdict = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, ntohs(nfg->res_id)); + #ifdef DEBUG + cerr << "[DEBUG] [NetfilterQueue.queue_cb] Packet received" << endl; + cerr << "[DEBUG] [NetfilterQueue.queue_cb] Packet ID: " << ntohl(ph->packet_id) << endl; + cerr << "[DEBUG] [NetfilterQueue.queue_cb] Payload size: " << plen << endl; + cerr << "[DEBUG] [NetfilterQueue.queue_cb] Payload: " << string(payload, payload+plen) << endl; + #endif + // Check IP protocol version if ( (payload[0] & 0xf0) == 0x40 ){ build_verdict(Tins::IP(payload, plen), payload, plen, nlh_verdict, ph, sctx); diff --git a/backend/binsrc/classes/regex_rules.cpp b/backend/binsrc/classes/regex_rules.cpp index 08a62ce..c01b2a2 100644 --- a/backend/binsrc/classes/regex_rules.cpp +++ b/backend/binsrc/classes/regex_rules.cpp @@ -78,9 +78,9 @@ class RegexRules{ if (n_of_regex == 0){ return; } - const char* regex_match_rules[n_of_regex]; - unsigned int regex_array_ids[n_of_regex]; - unsigned int regex_flags[n_of_regex]; + vector regex_match_rules(n_of_regex); + vector regex_array_ids(n_of_regex); + vector regex_flags(n_of_regex); for(int i = 0; i < n_of_regex; i++){ regex_match_rules[i] = decoded[i].second.regex.c_str(); regex_array_ids[i] = i; @@ -89,17 +89,25 @@ class RegexRules{ regex_flags[i] |= HS_FLAG_CASELESS; } } - - hs_database_t* rebuilt_db; - hs_compile_error_t *compile_err; + #ifdef DEBUG + cerr << "[DEBUG] [RegexRules.fill_ruleset] compiling " << n_of_regex << " regexes..." << endl; + for (int i = 0; i < n_of_regex; i++){ + cerr << "[DEBUG] [RegexRules.fill_ruleset] regex[" << i << "]: " << decoded[i].first << " " << decoded[i].second.regex << endl; + cerr << "[DEBUG] [RegexRules.fill_ruleset] regex_match_rules[" << i << "]: " << regex_match_rules[i] << endl; + cerr << "[DEBUG] [RegexRules.fill_ruleset] regex_flags[" << i << "]: " << regex_flags[i] << endl; + cerr << "[DEBUG] [RegexRules.fill_ruleset] regex_array_ids[" << i << "]: " << regex_array_ids[i] << endl; + } + #endif + hs_database_t* rebuilt_db = nullptr; + hs_compile_error_t *compile_err = nullptr; if ( hs_compile_multi( - regex_match_rules, - regex_flags, - regex_array_ids, + regex_match_rules.data(), + regex_flags.data(), + regex_array_ids.data(), n_of_regex, is_stream?HS_MODE_STREAM:HS_MODE_BLOCK, - nullptr,&rebuilt_db, &compile_err + nullptr, &rebuilt_db, &compile_err ) != HS_SUCCESS ) { cerr << "[warning] [RegexRules.fill_ruleset] hs_db failed to compile: '" << compile_err->message << "' skipping..." << endl; diff --git a/backend/binsrc/nfqueue.cpp b/backend/binsrc/nfqueue.cpp index d961807..bd7b6b5 100644 --- a/backend/binsrc/nfqueue.cpp +++ b/backend/binsrc/nfqueue.cpp @@ -54,10 +54,16 @@ struct matched_data{ bool has_matched = false; }; -bool filter_callback(packet_info & info){ + +bool filter_callback(packet_info& info){ shared_ptr conf = regex_config; - if (conf->ver() != info.sctx->latest_config_ver){ - info.sctx->clean_scratches(); + auto current_version = conf->ver(); + if (current_version != info.sctx->latest_config_ver){ + #ifdef DEBUG + cerr << "[DEBUG] [filter_callback] Configuration has changed (" << current_version << "!=" << info.sctx->latest_config_ver << "), cleaning scratch spaces" << endl; + #endif + info.sctx->clean(); + info.sctx->latest_config_ver = current_version; } scratch_setup(conf->input_ruleset, info.sctx->in_scratch); scratch_setup(conf->output_ruleset, info.sctx->out_scratch); @@ -66,6 +72,12 @@ bool filter_callback(packet_info & info){ if (regex_matcher == nullptr){ return true; } + + #ifdef DEBUG + cerr << "[DEBUG] [filter_callback] Matching packet with " << (info.is_input ? "input" : "output") << " ruleset" << endl; + cerr << "[DEBUG] [filter_callback] Packet: " << info.payload << endl; + #endif + matched_data match_res; hs_error_t err; hs_scratch_t* scratch_space = info.is_input ? info.sctx->in_scratch: info.sctx->out_scratch; @@ -77,25 +89,40 @@ bool filter_callback(packet_info & info){ }; hs_stream_t* stream_match; if (conf->stream_mode()){ - matching_map match_map = info.is_input ? info.sctx->in_hs_streams : info.sctx->out_hs_streams; - auto stream_search = match_map.find(info.sid); + matching_map* match_map = info.is_input ? &info.sctx->in_hs_streams : &info.sctx->out_hs_streams; + #ifdef DEBUG + cerr << "[DEBUG] [filter_callback] Dumping match_map " << match_map << endl; + for (auto ele: *match_map){ + cerr << "[DEBUG] [filter_callback] " << ele.first << " -> " << ele.second << endl; + } + cerr << "[DEBUG] [filter_callback] End of match_map" << endl; + #endif + auto stream_search = match_map->find(info.sid); - if (stream_search == match_map.end()){ + if (stream_search == match_map->end()){ + + #ifdef DEBUG + cerr << "[DEBUG] [filter_callback] Creating new stream matcher for " << info.sid << endl; + #endif if (hs_open_stream(regex_matcher, 0, &stream_match) != HS_SUCCESS) { cerr << "[error] [filter_callback] Error opening the stream matcher (hs)" << endl; throw invalid_argument("Cannot open stream match on hyperscan"); } - if (info.is_tcp){ - match_map[info.sid] = stream_match; - } + match_map->insert_or_assign(info.sid, stream_match); }else{ stream_match = stream_search->second; } + #ifdef DEBUG + cerr << "[DEBUG] [filter_callback] Matching as a stream" << endl; + #endif err = hs_scan_stream( stream_match,info.payload.c_str(), info.payload.length(), 0, scratch_space, match_func, &match_res ); }else{ + #ifdef DEBUG + cerr << "[DEBUG] [filter_callback] Matching as a block" << endl; + #endif err = hs_scan( regex_matcher,info.payload.c_str(), info.payload.length(), 0, scratch_space, match_func, &match_res @@ -105,13 +132,6 @@ bool filter_callback(packet_info & info){ cerr << "[error] [filter_callback] Error while matching the stream (hs)" << endl; throw invalid_argument("Error while matching the stream with hyperscan"); } - if ( - !info.is_tcp && conf->stream_mode() && - hs_close_stream(stream_match, scratch_space, nullptr, nullptr) != HS_SUCCESS - ){ - cerr << "[error] [filter_callback] Error closing the stream matcher (hs)" << endl; - throw invalid_argument("Cannot close stream match on hyperscan"); - } if (match_res.has_matched){ auto rules_vector = info.is_input ? conf->input_ruleset.regexes : conf->output_ruleset.regexes; stringstream msg; diff --git a/backend/modules/nfregex/firegex.py b/backend/modules/nfregex/firegex.py index 762e817..6e1e9bf 100644 --- a/backend/modules/nfregex/firegex.py +++ b/backend/modules/nfregex/firegex.py @@ -80,7 +80,8 @@ class FiregexInterceptor: proxy_binary_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),"../cppqueue") self.process = await asyncio.create_subprocess_exec( proxy_binary_path, - stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE + stdout=asyncio.subprocess.PIPE, stdin=asyncio.subprocess.PIPE, + env={"MATCH_MODE": "stream" if self.srv.proto == "tcp" else "block", "NTHREADS": os.getenv("NTHREADS","1")}, ) line_fut = self.process.stdout.readuntil() try: diff --git a/frontend/src/pages/NFRegex/ServiceDetails.tsx b/frontend/src/pages/NFRegex/ServiceDetails.tsx index 584117f..e1bdbdd 100644 --- a/frontend/src/pages/NFRegex/ServiceDetails.tsx +++ b/frontend/src/pages/NFRegex/ServiceDetails.tsx @@ -16,6 +16,7 @@ function ServiceDetailsNFRegex() { const [tooltipAddRegexOpened, setTooltipAddRegexOpened] = useState(false) const regexesList = nfregexServiceRegexesQuery(srv??"") + if (services.isLoading) return if (!srv || !serviceInfo || regexesList.isError) return return <> diff --git a/tests/nf_test.py b/tests/nf_test.py index 034b80a..cc780bd 100644 --- a/tests/nf_test.py +++ b/tests/nf_test.py @@ -88,7 +88,7 @@ def checkRegex(regex, should_work=True, upper=False): if r["regex"] == regex: #Test the regex s = base64.b64decode(regex).upper() if upper else base64.b64decode(regex) - if not server.sendCheckData(secrets.token_bytes(200) + s + secrets.token_bytes(200)): + if not server.sendCheckData(secrets.token_bytes(40) + s + secrets.token_bytes(40)): puts("The malicious request was successfully blocked ✔", color=colors.green) n_blocked += 1 time.sleep(1) @@ -104,7 +104,7 @@ def checkRegex(regex, should_work=True, upper=False): puts("Test Failed: The regex wasn't found ✗", color=colors.red) exit_test(1) else: - if server.sendCheckData(secrets.token_bytes(200) + base64.b64decode(regex) + secrets.token_bytes(200)): + if server.sendCheckData(secrets.token_bytes(40) + base64.b64decode(regex) + secrets.token_bytes(40)): puts("The request wasn't blocked ✔", color=colors.green) else: puts("Test Failed: The request was blocked when it shouldn't have", color=colors.red) @@ -186,18 +186,6 @@ checkRegex(regex) clear_regexes() -#Create Server regex and verify that should not matches -if(firegex.nf_add_regex(service_id,regex,"S",active=True, is_case_sensitive=True)): - puts(f"Sucessfully added server to client regex {str(secret)} ✔", color=colors.green) -else: - puts(f"Test Failed: Coulnd't add server to client regex {str(secret)} ✗", color=colors.red) - exit_test(1) - -checkRegex(regex, should_work=False) - -#Delete regex -clear_regexes() - #Rename service if(firegex.nf_rename_service(service_id,f"{args.service_name}2")): puts(f"Sucessfully renamed service to {args.service_name}2 ✔", color=colors.green) diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 47bf7e2..cfe00d9 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -9,23 +9,23 @@ ERROR=0 pip3 install -r requirements.txt echo "Running standard API test" -#python3 api_test.py -p $PASSWORD || ERROR=1 +python3 api_test.py -p $PASSWORD || ERROR=1 echo "Running Netfilter Regex TCP ipv4" python3 nf_test.py -p $PASSWORD -m tcp || ERROR=1 echo "Running Netfilter Regex TCP ipv6" -#python3 nf_test.py -p $PASSWORD -m tcp -6 || ERROR=1 +python3 nf_test.py -p $PASSWORD -m tcp -6 || ERROR=1 echo "Running Netfilter Regex UDP ipv4" -#python3 nf_test.py -p $PASSWORD -m udp || ERROR=1 +python3 nf_test.py -p $PASSWORD -m udp || ERROR=1 echo "Running Netfilter Regex UDP ipv6" -#python3 nf_test.py -p $PASSWORD -m udp -6 || ERROR=1 +python3 nf_test.py -p $PASSWORD -m udp -6 || ERROR=1 echo "Running Port Hijack TCP ipv4" -#python3 ph_test.py -p $PASSWORD -m tcp || ERROR=1 +python3 ph_test.py -p $PASSWORD -m tcp || ERROR=1 echo "Running Port Hijack TCP ipv6" -#python3 ph_test.py -p $PASSWORD -m tcp -6 || ERROR=1 +python3 ph_test.py -p $PASSWORD -m tcp -6 || ERROR=1 echo "Running Port Hijack UDP ipv4" -#python3 ph_test.py -p $PASSWORD -m udp || ERROR=1 +python3 ph_test.py -p $PASSWORD -m udp || ERROR=1 echo "Running Port Hijack UDP ipv6" -#python3 ph_test.py -p $PASSWORD -m udp -6 || ERROR=1 +python3 ph_test.py -p $PASSWORD -m udp -6 || ERROR=1 exit $ERROR From bf8f1976016b8930bebfb292489a3392c71504cf Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Tue, 4 Feb 2025 22:09:22 +0100 Subject: [PATCH 07/11] improves on the nfregex binary x4 --- backend/binsrc/classes/netfilter.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/binsrc/classes/netfilter.cpp b/backend/binsrc/classes/netfilter.cpp index b226842..59db232 100644 --- a/backend/binsrc/classes/netfilter.cpp +++ b/backend/binsrc/classes/netfilter.cpp @@ -267,13 +267,14 @@ class NetfilterQueue { stream.auto_cleanup_payloads(true); stream.client_data_callback(bind(on_client_data, placeholders::_1, sctx)); stream.server_data_callback(bind(on_server_data, placeholders::_1, sctx)); + stream.stream_closed_callback(bind(on_stream_close, placeholders::_1, sctx)); } // A stream was terminated. The second argument is the reason why it was terminated - static void on_stream_terminated(Stream& stream, StreamFollower::TerminationReason reason, stream_ctx* sctx) { + static void on_stream_close(Stream& stream, stream_ctx* sctx) { stream_id stream_id = stream_id::make_identifier(stream); #ifdef DEBUG - cerr << "[DEBUG] [NetfilterQueue.on_stream_terminated] Stream terminated, deleting all data" << endl; + cerr << "[DEBUG] [NetfilterQueue.on_stream_close] Stream terminated, deleting all data" << endl; #endif sctx->clean_stream_by_id(stream_id); } @@ -289,7 +290,7 @@ class NetfilterQueue { mnl_socket_setsockopt(sctx.nl, NETLINK_NO_ENOBUFS, &ret, sizeof(int)); sctx.follower.new_stream_callback(bind(on_new_stream, placeholders::_1, &sctx)); - sctx.follower.stream_termination_callback(bind(on_stream_terminated, placeholders::_1, placeholders::_2, &sctx)); + sctx.follower.stream_termination_callback(bind(on_stream_close, placeholders::_1, &sctx)); for (;;) { ret = recv_packet(); From 676a2289da4e1f27ff81339c97cbfa0d377aeb3d Mon Sep 17 00:00:00 2001 From: Domingo Dirutigliano Date: Tue, 4 Feb 2025 22:51:30 +0100 Subject: [PATCH 08/11] improves on the nfregex binary x5 --- .gitignore | 2 +- backend/binsrc/classes/netfilter.cpp | 1 - backend/binsrc/nfqueue.cpp | 10 ++++++---- frontend/src/components/AddNewRegex.tsx | 17 ++++++++++------- tests/benchmark.py | 2 -- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 85481dc..4221f2b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,7 @@ docker-compose.yml firegex-compose.yml firegex-compose-tmp-file.yml firegex.py - +/tests/benchmark.csv # misc **/.DS_Store **/.env.local diff --git a/backend/binsrc/classes/netfilter.cpp b/backend/binsrc/classes/netfilter.cpp index 59db232..37ff5f7 100644 --- a/backend/binsrc/classes/netfilter.cpp +++ b/backend/binsrc/classes/netfilter.cpp @@ -438,7 +438,6 @@ class NetfilterQueue { cerr << "[DEBUG] [NetfilterQueue.queue_cb] Packet received" << endl; cerr << "[DEBUG] [NetfilterQueue.queue_cb] Packet ID: " << ntohl(ph->packet_id) << endl; cerr << "[DEBUG] [NetfilterQueue.queue_cb] Payload size: " << plen << endl; - cerr << "[DEBUG] [NetfilterQueue.queue_cb] Payload: " << string(payload, payload+plen) << endl; #endif // Check IP protocol version diff --git a/backend/binsrc/nfqueue.cpp b/backend/binsrc/nfqueue.cpp index bd7b6b5..0cd3dd7 100644 --- a/backend/binsrc/nfqueue.cpp +++ b/backend/binsrc/nfqueue.cpp @@ -72,10 +72,12 @@ bool filter_callback(packet_info& info){ if (regex_matcher == nullptr){ return true; } - + #ifdef DEBUG cerr << "[DEBUG] [filter_callback] Matching packet with " << (info.is_input ? "input" : "output") << " ruleset" << endl; - cerr << "[DEBUG] [filter_callback] Packet: " << info.payload << endl; + if (info.payload.size() <= 30){ + cerr << "[DEBUG] [filter_callback] Packet: " << info.payload << endl; + } #endif matched_data match_res; @@ -153,14 +155,14 @@ int main(int argc, char *argv[]){ if (matchmode != nullptr && strcmp(matchmode, "block") == 0){ stream_mode = false; } - cerr << "[info] [main] Using " << n_of_threads << " threads, stream mode: " << stream_mode << endl; + regex_config.reset(new RegexRules(stream_mode)); NFQueueSequence queues(n_of_threads); queues.start(); cout << "QUEUES " << queues.init() << " " << queues.end() << endl; - cerr << "[info] [main] Queues: " << queues.init() << ":" << queues.end() << " threads assigned: " << n_of_threads << endl; + cerr << "[info] [main] Queues: " << queues.init() << ":" << queues.end() << " threads assigned: " << n_of_threads << " stream mode: " << stream_mode << endl; config_updater(); } diff --git a/frontend/src/components/AddNewRegex.tsx b/frontend/src/components/AddNewRegex.tsx index 0f846e1..86d08c7 100644 --- a/frontend/src/components/AddNewRegex.tsx +++ b/frontend/src/components/AddNewRegex.tsx @@ -1,4 +1,4 @@ -import { Button, Group, Space, TextInput, Notification, Switch, NativeSelect, Modal } from '@mantine/core'; +import { Button, Group, Space, TextInput, Notification, Switch, Modal, Select } from '@mantine/core'; import { useForm } from '@mantine/form'; import { useState } from 'react'; import { RegexAddForm } from '../js/models'; @@ -17,13 +17,13 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=> const form = useForm({ initialValues: { regex:"", - mode:"C -> S", + mode:"C", is_case_insensitive:false, deactive:false }, validate:{ regex: (value) => value !== "" ? null : "Regex is required", - mode: (value) => ['C -> S', 'S -> C', 'C <-> S'].includes(value) ? null : "Invalid mode", + mode: (value) => ['C', 'S', 'B'].includes(value) ? null : "Invalid mode", } }) @@ -38,12 +38,11 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=> const submitRequest = (values:RegexAddInfo) => { setSubmitLoading(true) - const filter_mode = ({'C -> S':'C', 'S -> C':'S', 'C <-> S':'B'}[values.mode]) const request:RegexAddForm = { is_case_sensitive: !values.is_case_insensitive, service_id: service, - mode: filter_mode?filter_mode:"B", + mode: values.mode?values.mode:"B", regex: b64encode(values.regex), active: !values.deactive } @@ -86,8 +85,12 @@ function AddNewRegex({ opened, onClose, service }:{ opened:boolean, onClose:()=> {...form.getInputProps('deactive', { type: 'checkbox' })} /> - S', 'S -> C', 'C <-> S']} +