Mail-DMARC-1.20240314000755000765000024 014574361234 13062 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/.perltidyrc000444000765000024 123214574361234 15377 0ustar00mattstaff000000000000-l=78 # Max line width is 78 cols -i=4 # Indent level is 4 cols -ci=4 # Continuation indent is 4 cols #-st # Output to STDOUT -b # edit in place and backup -se # Errors to STDERR -vt=2 # Maximal vertical tightness -cti=0 # No extra indentation for closing brackets -pt=1 # Medium parenthesis tightness -bt=1 # Medium brace tightness -sbt=1 # Medium square bracket tightness -bbt=1 # Medium block brace tightness -nsfs # No space before semicolons -nolq # Don't outdent long quoted strings -wbb="% + - * / x != == >= <= =~ !~ < > | & >= < = **= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x=" # Break before all operators Mail-DMARC-1.20240314/Build.PL000444000765000024 1164414574361234 14541 0ustar00mattstaff000000000000use strict; use warnings; use Module::Build 0.3601; my $module_build_args = { "build_requires" => { "Module::Build" => "0.3601" }, "configure_requires" => { "File::ShareDir::Install" => "0.06", "Module::Build" => "0.3601" }, "dist_abstract" => "Perl implementation of DMARC", "dist_author" => [ "Matt Simerson ", "Davide Migliavacca ", "Marc Bradshaw " ], "dist_name" => "Mail-DMARC", "license" => "perl", "module_name" => "Mail::DMARC", "release_status" => "stable", "add_to_cleanup" => [ "dmarc_reports.sqlite", "t/reports-test.sqlite"], "recommends" => { "Mail::DKIM" => 0, # "MIME::Lite" => 0, "Net::IMAP::Simple" => 0, "Net::SMTPS" => 0, }, "requires" => { "perl" => "5.10.0", "Carp" => 0, "Config::Tiny" => 0, "DBD::SQLite" => "1.31", "DBIx::Simple" => "1.35", "Data::Dumper" => 0, "Email::MIME" => 0, "Email::Sender" => 0, "Email::Sender::Simple" => "1.300032", "Email::Simple" => 0, "Encode" => 0, "English" => 0, "File::ShareDir" => 0, "Getopt::Long" => 0, "HTTP::Tiny" => 0, "IO::Compress::Gzip" => 0, "IO::Compress::Zip" => 0, "IO::File" => 0, "IO::Socket::SSL" => 0, "IO::Uncompress::Gunzip" => 0, "IO::Uncompress::Unzip" => 0, "Net::DNS::Resolver" => 0, "Net::IDN::Encode" => 0, "Net::IP" => 0, "Net::SSLeay" => 0, "POSIX" => 0, "Pod::Usage" => 0, "Regexp::Common" => "2013031301", "Socket" => 0, "Socket6" => "0.23", "Sys::Hostname" => 0, "Sys::Syslog" => 0, "Test::File::ShareDir" => 0, "URI" => 0, "XML::LibXML" => 0, }, "auto_features" => { "mysql" => { "description" => "MySQL backend storage", "prereqs" => { "runtime" => { "requires" => { 'DBD::mysql' => '4.001', } } } }, "postgres" => { "description" => "PostgresQL backend storage", "prereqs" => { "runtime" => { "requires" => { 'DBD::Pg' => '0' } } } }, "web_services" => { "description" => "HTTP API and web UI to DMARC reports", "prereqs" => { "runtime" => { "requires" => { "CGI" => 0, "HTTP::Request" => 0, "JSON" => 0, "LWP::UserAgent" => 0, "Net::HTTP" => 0, "Net::Server::HTTP" => 0, "Net::Server" => 2, } } } }, "smtp_sending" => { "description" => "Send DMARC reports via SMTP", "prereqs" => { "runtime" => { "Net::SMTPS" => 0, "Mail::DKIM::PrivateKey" => 0, "Mail::DKIM::Signer" => 0, "Mail::DKIM::TextWrap" => 0, } } }, "imap_fetch" => { "description" => "Retrieve DMARC reports from an IMAP account", "prereqs" => { "runtime" => { "Net::IMAP::Simple" => 0, } } } }, "recursive_test_files" => 1, "script_files" => [ "bin/dmarc_update_public_suffix_list", "bin/dmarc_send_reports", "bin/dmarc_httpd", "bin/dmarc_lookup", "bin/dmarc_receive", "bin/dmarc_http_client", "bin/dmarc_view_reports" ], "share_dir" => { "dist" => "share" }, "test_requires" => { "Test::Exception" => 0, "Test::File::ShareDir" => 0, "Test::More" => 0, "Test::Output" => 0, "Net::DNS::Resolver::Mock" => 0 }, "develop_requires" => { "Test::Pod" => "1.41" }, "meta_add" => { }, "meta_merge" => { "prereqs" => { "test" => { "recommends" => { "XML::SAX::ParserFactory" => "0", "XML::Validator::Schema" => "0" }, }, "develop" => { "requires" => { "Test::Pod" => "1.41" }, "suggests" => { "Test::Perl::Critic" => "0" } }, }, "resources" => { "bugtracker" => "https://github.com/msimerson/mail-dmarc/issues", "homepage" => "https://github.com/msimerson/mail-dmarc/wiki", "repository" => "https://github.com/msimerson/mail-dmarc", }, "x_contributors" => [ "Benny Pedersen ", "Jean Paul Galea ", "Marisa Clardy ", "Priyadi Iman Nurcahyo ", "Ricardo Signes " ], } }; my $fallback_build_requires = { "Module::Build" => "0.3601", "Test::Exception" => 0, "Test::File::ShareDir" => 0, "Test::More" => 0, "Test::Output" => 0 }; unless ( eval { Module::Build->VERSION(0.4004) } ) { delete $module_build_args->{test_requires}; $module_build_args->{build_requires} = $fallback_build_requires; } my $build = Module::Build->new(%$module_build_args); # if ( $build->prompt( "Database engine", "sqlite" ) ) { # $build->notes( 'DB_ENGINE' => $build->args('db_engine') ); # } $build->create_build_script; Mail-DMARC-1.20240314/Changes.md000444000765000024 2557514574361234 15147 0ustar00mattstaff000000000000### 1.20240313 - Fix error email sent when reports are too large - Delete reports after sending error emails - Make sending of error emails optional ### 1.20240214 - feat: add imap option to specify port #195 - feat: add configurable DNS retrans option #214 - ignore empty/wrong lines on whitelist_dmarc file #219 - test: mock DNS during testing #213 - ci: restore CI tests to working order - Force lower case for SPF domain input #212 ### 1.20230215 - Fix error when logging a report which was skipped for size ### 1.20211209 - Properly delete sent reports when the database does not support cascade ### 1.20210927 - Fix reporting for selectors whose name evaluates to false - Use maybestarttls for opportunistic encryption when sending reports using Email::Sender v2.0 or greater - Remove dead domain dmarc-qa.com from tests - Print full syntax guide with "--help" option (Jeremiah Morris) ### 1.20210427 - Fix report sending issues with SSL/TLS ### 1.20210220 - Fix db connection cache - use Email::Sender for report sending ### 1.20200214 - move HTTP::Tiny into deps (used for PSL updates) ### 1.20200116 - skip HTTP tests when optional JSON not installed #171 ### 1.20200114 - skip HTTP tests when optional deps not installed #171 - update PSL - auto update PSL as part of release ### 1.20200113 - lazy load Net::SMTPS #168 ### 1.20200108 - NEW FEATURE: Postgres support #150 - removed dist::zilla - additional tests enabled - html UI: use https URLS everywhere - SPF: don't warn when scope is missing from reports - receive: permit other MIME types that have xml.gz filename - DKIM: when message has no result, add "none" - sqlite: add default current_timestamp - bin/install_deps.pl: apt improvements ### 1.20191004 - updated PSL - update jQuery, jQuery grid - empty ENV FROM when missing #144 ### 1.20190831 - improve aggregate report docs #142 - added dmarc_whitelist hosts #119 ### 1.20190308 - Lower memory usage when sending reports ### 1.20181001 - Check author when saving a new report record - Fix bug in RUA filtering when recipient had a size filter - Fix TLS fails for report sending to certain domains - Fix report sending loop problem ### 1.20180125 - Allow domains listed in the public suffix list to align. ### 1.20170911 - STARTTLS workaround for Net::SMTPS issue. ### 1.20170906 - Ignore the case of tag keys when parsing DMARC records ### 1.20170222 - Ensure entities in XML agg reports are properly escaped #104 - geoip v6 support and field selection #103 - use a larger integer type for report_record.count #102 - improved apt package lookups in install_deps.pl #98 ### 1.20160612 - fix aggregrate schema test #96 - Do not reject NXDOMAIN as per rfc #94 - added none result for no policy #93 - avoid deadlock with some invalid rua data #92 - avoid loop when sending reports via http #92 ### 1.20150908 - Optionally log sending of reports to syslog ### 1.20150527 - check for an updated PSL file and load if necessary - handle domains with missing rua/ruf - add timeout to sending script ### 1.20150317 - squash subdomains w/o DMARC records into parent report (#59) - add batch reporting (suppress throttling until...) - align reports with hour/UTC day - swap git contributors plugin ### 1.20150310 - lower case domain names at entry points (resolves #53) - tolerate substitution of = with : in DMARC DNS rec ### 1.20150228 - fix the policy_evaluated fields in outbound reports - accommodate a common DMARC error substiting = with : - initialized config file first (was non-deterministic) - tolerate missing SPF auth type scope ### 1.20150222 - remove ./mail-dmarc.ini (sample in share/mail-dmarc.ini) - load PSL before dmarc_httpd forks, so we only load it once - quieter report sending output unless --verbose ### 1.20150211 - optionally DKIM sign sent reports - warn when DMARC record format is invalid - accept callbacks (lazy eval) for SPF & DKIM results - make the report record building consistent for eval and reporting - rewrite DKIM result invalid -> temperror - capture test warnings, so 'make test' is prettier ### 1.20150123 - enable lazy evaluation of SPF & DKIM (Ricardo Signes) - check ShareDir for mail-dmarc.ini, if not in a standard location - map DKIM status=invalid to status->temperror - add config arg to dmarc_update_public_suffix_list (Ricardo Signes) - Send only a single cc email (Marc Bradshaw) - DMARC: update docs to show SPF one-shot syntax - PurePerl: one shot accepts a Mail::DKIM::Verifier - trap errors thrown by is_dkim_aligned - INSTALL: added 'install mail-dmarc.ini' step - Show "new record" output only in verbose mode. (Marc Bradshaw) - require DBIx::Simple 1.35 (was any) ### 1.20141230 - Add script to update the public suffix list file (Marc Bradshaw) ### 1.20141206 - Delete reports with no valid rua (Marc Bradshaw) - Ignore DomainKeys signatures (Marc Bradshaw) - allow configurable delay between sending emails (Marc Bradshaw) - permit absolute paths for public suffix list file location (Marc Bradshaw) - fix lookup for *.foo entries (Marc Bradshaw) ### 1.20141119 - added auto_save option for validation reports - updated bin/install_deps.pl ### 1.20141030 - percent policy logic wasn't being applied correctly - fix for reasons not stored in SQL ### 1.20140711 - Store/SQL: use full sql name in WHERE clause - DMARC/HTTP: added error handling and tests - removed excess comma in mail_dmarc_schema.mysql - added quotes around ($commit || ''), just in case - try IMAP fetch without SORT if no results, for IMAP servers like Gmail that don't support SORT - warn but still pass test if DNS query fails ### 1.20140623 - updated tests to accomodate the cached PSL ### 1.20140622 - load PSL into hash to speed subsequent lookups (esp for daemon) - uncommented Net::Server in Prereqs/Recommended section - added INSTALL - updated dmarc_httpd description to note validation feature - updated public_suffix_list ### 1.20140210 - NEW FEATURE: added HTTP validation service (see dmarc_httpd) - install_deps: install optional prereqs by default - added Best Current Practices link on main page - minor tweaks to Pod (Ricardo Signes) - PurePerl: added comments about Sender header when message has multiple-address format used in the From header - updated public_suffix_list ### 1.20130906 - handle errors encountered when reporting address is illegal - delete reports that return a SMTP 5XX code for the recipient - delete reports after encountering 12 errors - added 'too big' notices when report size exceeds limit - updated install_deps.pl ### 1.20130625 - added a bunch of tests from http://dmarc-qa.com - URI: supress undef error if URI scheme not defined - policy->parse: properly parse records with unnecessary trailing ; - reporting is 'external' based on Org Domain (was email domain) ### 1.20130616 - combined update/replace SQL methods - dmarc_view_reports: fix duplicated variable name ### 1.20130615 - bug fixes and purge unused classes ### 1.20130614 - Added whitelist feature - SMTP: remove Subject: Report-ID - SMTP: more types of SMTP errors are stored and reported - dmarc_send_reports: added verbose option - dmarc_view_reports: fix for searches with MySQL backend ### 1.20130612 - dmarc_view_reports: improve gentoo support by adding /usr to search path for GeoIP DBs on gentoo - Benny Pedersen ### 1.20130610 - tolerate receiving reports with no records (ahem, hotmail.com) - simplify SMTP flow-of-control, additional SMTP tests - avoid the join/split of binip in SQL::populate_agg_records - replace carp with warn in several places (more legible warning) - added RUA validity checks to dmarc_lookup ### 1.20130605 - in aggregate reports, group by IP and auth results (was only IP) - refactored SQL::retrieve_todo into 3 methods, added tests - SQL: added unique constraint on domain.domain ### 1.20130604 - main branches are master (devel) and releases (more obvious) - added mailing list impact FAQ - SQL: removed record.rcpt_dom - corrected a XML schema error - index.html - widened disposition column - only show rcpt domain in record (subgrid) - corrected subgrid row_id - additional validation of aggregate reports ### 1.20130601 - make sure a report record exists when fetching SMTP todo - added insecure SMTP fallback if STARTTLS fails - added color coded results to HTTP grid ### 1.20130531 - added gzip support to HTTP server, compressed JS files - reason is internally an arrayref of hashrefs (was a single hashref) - documentation additions - removed unused JS files - add validation and fixup of SPF result for incoming reports - normalized domain columns in spf & dkim tables ### 1.20130528 - bump major version to 1 - normalized domain columns in report_record - fixups to handle reports with invalid formatting - improved handling for IMAP SSL connections - made internal represention of Mail::DMARC::dkim & spf consistent with their aggregate report representation ### 0.20130528 - updated Send/SMTP to use report::aggregate - switched back to gzip reports (instead of zip) - dmarc_view_reports, added filtering ability, GeoIP location ### 0.20130524 - added bin/dmarc_httpd - added bin/dmarc_view_reports - renamed: dmarc_report -> dmarc_send_reports ### 0.20130521 - check for report_record existence before insertion - SQL: added report_record.count column - subclassed aggregreate reports into Report::Aggregate - consolidates two agg. rep. generation methods to one - SQL: added table report_error - updated SQLite schema with native column types ### 0.20130520 - added bin/dmarc_receive (via IMAP, mbox, or message file) - added report retrieval via IMAP - extract sender domain from subject or MIME metadata - SQL: added author.extra_contact - SQL: removed 'NOT NULL' requirements for values often missing from incoming reports. ### 0.20130517 - send reports with zip until 7/1, gzip after - replace Socket 2 with Socket6 (better Windows compatibility) - added parsing of incoming email reports - added author and domain tables - added three related columns from/rcpt/author ids to report table - add email hostname to MX list when attempting SMTP delivery - during report delivery, check report URI max size ### 0.20130515 - use File::ShareDir to access share/* - added external reporting verification ### 0.20130514 - moved DNS settings into config file - fixed a case where disposition was not set - added bin/dmarc_report - sends email reports with Email::MIME & Net::SMTPS - deletes reports after successful delivery - required Socket 2 (correct IPv6 handling) - several SQL schema changes - has_valid_reporting_uri does validation now ### 0.20130510 ### 0.20130507 - added sql and MySQL schema - added bin/dmarc_lookup - replaced Regexp::Common IP validation with Net::IP (perl 5.8 compat) - added Results.pm tests - added full section numbers to Draft quotes ### 0.20130506 - added Result and Result/Evaluated.pm - consolidated DNS functions into DNS.pm - uses Regexp::Common, requiring perl 5.10. - Mail::DMARC::Policy is well defined and tested - setting up package Mail-DMARC-1.20240314/DEVELOP.md000444000765000024 332514574361234 14642 0ustar00mattstaff000000000000# Find the source [The source code is hosted on GitHub](https://github.com/msimerson/mail-dmarc) # Download the source To make changes or submit patches, visit the GitHub URL and click the ***Fork*** button. Then clone your fork to your local disk: git clone git@github.com:YOUR-USER-NAME/mail-dmarc.git # Use the source Use git in the normal way: cd mail-dmarc .... make a change or two ... git status ( see changes ) git diff ( show diffs ) git add ... ( stage changes ) git commit If your changes are significant and might possibly involve more than one commit, create a branch first: git checkout -b fix-knob-handle ... make changes ... git commit ... make more related changes ... git commit When you are done making changes, push them to GitHub: git push origin (push to your GitHub account) When the new feature branch is no longer useful, delete it: git branch -d fix-knob-handle # Submit your changes git push origin master (push to your GitHub account) Visit your fork on the GitHub web site. On the main page of your fork is a ***Pull Request*** button. That is how you submit your changes to the main repo. A collaborator will review your PR and either comment or merge it. # Check build status: [![Build Status](https://github.com/msimerson/mail-dmarc/actions/workflows/ci.yml/badge.svg)](https://github.com/msimerson/mail-dmarc/actions/workflows/ci.yml) GitHub Actions automatically runs build tests when commits are pushed to GitHub, and sends notifications to the author(s) in case of failure. For everyone else, checking the build status after a push request is merged is a good idea. # Release ````sh .release/do.sh ```` Mail-DMARC-1.20240314/FAQ.md000444000765000024 17514574361234 14133 0ustar00mattstaff000000000000 The Mail::DMARC [FAQ is here](https://github.com/msimerson/mail-dmarc/wiki). https://github.com/msimerson/mail-dmarc/wiki Mail-DMARC-1.20240314/INSTALL.md000444000765000024 174014574361234 14651 0ustar00mattstaff000000000000 # Install Mail::DMARC's dependencies ## The fast way, using your systems package manager (yum, apt, ports): perl bin/install_deps.pl If your system doesn't have a package manager, or the package version fails, or the version installed by your systems package manager isn't new enough, install_deps will also attempt to install the latest version via CPAN. Once the dependencies are installed, install Mail::DMARC as any other perl module: perl Makefile.PL make make install clean Copy the mail-dmarc.ini file to your systems preferred local etc directory (/etc, /usr/local/etc, opt/local/etc) and edit at least the settings in the [organization] block: cp mail-dmarc.ini /etc/ $EDITOR /etc/mail-dmarc.ini NOTE: Most of the dependencies are optionally required for the DMARC reporting features. Mail::DMARC will perform validation with only these modules: Regexp::Common Config::Tiny File::ShareDir Net::DNS::Resolver Net::IP Socket6 Mail-DMARC-1.20240314/LICENSE000444000765000024 4366014574361234 14255 0ustar00mattstaff000000000000This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2018 by Matt Simerson. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2018 by Matt Simerson. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Mail-DMARC-1.20240314/MANIFEST000444000765000024 1065214574361234 14374 0ustar00mattstaff000000000000.perltidyrc bin/dmarc_http_client bin/dmarc_httpd bin/dmarc_lookup bin/dmarc_receive bin/dmarc_send_reports bin/dmarc_update_public_suffix_list bin/dmarc_view_reports bin/install_deps.pl Build.PL Changes.md DEVELOP.md example/report_cgi.png FAQ.md INSTALL.md lib/Mail/DMARC.pm lib/Mail/DMARC/Base.pm lib/Mail/DMARC/HTTP.pm lib/Mail/DMARC/Policy.pm lib/Mail/DMARC/PurePerl.pm lib/Mail/DMARC/Report.pm lib/Mail/DMARC/Report/Aggregate.pm lib/Mail/DMARC/Report/Aggregate/Metadata.pm lib/Mail/DMARC/Report/Aggregate/Record.pm lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results.pm lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results/DKIM.pm lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results/SPF.pm lib/Mail/DMARC/Report/Aggregate/Record/Identifiers.pm lib/Mail/DMARC/Report/Aggregate/Record/Row.pm lib/Mail/DMARC/Report/Aggregate/Record/Row/Policy_Evaluated.pm lib/Mail/DMARC/Report/Receive.pm lib/Mail/DMARC/Report/Send.pm lib/Mail/DMARC/Report/Send/HTTP.pm lib/Mail/DMARC/Report/Send/SMTP.pm lib/Mail/DMARC/Report/Sender.pm lib/Mail/DMARC/Report/Store.pm lib/Mail/DMARC/Report/Store/SQL.pm lib/Mail/DMARC/Report/Store/SQL/Grammars/MySQL.pm lib/Mail/DMARC/Report/Store/SQL/Grammars/PostgreSQL.pm lib/Mail/DMARC/Report/Store/SQL/Grammars/SQLite.pm lib/Mail/DMARC/Report/URI.pm lib/Mail/DMARC/Result.pm lib/Mail/DMARC/Result/Reason.pm lib/Mail/DMARC/Test/Transport.pm LICENSE Makefile.PL MANIFEST This list of files MANIFEST.SKIP META.json META.yml README.md share/dmarc_whitelist share/html/css/ellipsis-xbl.xml share/html/css/ui.jqgrid.css share/html/css/ui.multiselect.css share/html/index.html share/html/js/i18n/grid.locale-ar.js.gz share/html/js/i18n/grid.locale-bg.js.gz share/html/js/i18n/grid.locale-bg1251.js.gz share/html/js/i18n/grid.locale-cat.js.gz share/html/js/i18n/grid.locale-cn.js.gz share/html/js/i18n/grid.locale-cs.js.gz share/html/js/i18n/grid.locale-da.js.gz share/html/js/i18n/grid.locale-de.js.gz share/html/js/i18n/grid.locale-dk.js.gz share/html/js/i18n/grid.locale-el.js.gz share/html/js/i18n/grid.locale-en.js.gz share/html/js/i18n/grid.locale-es.js.gz share/html/js/i18n/grid.locale-fa.js.gz share/html/js/i18n/grid.locale-fi.js.gz share/html/js/i18n/grid.locale-fr.js.gz share/html/js/i18n/grid.locale-gl.js.gz share/html/js/i18n/grid.locale-he.js.gz share/html/js/i18n/grid.locale-hr.js.gz share/html/js/i18n/grid.locale-hr1250.js.gz share/html/js/i18n/grid.locale-hu.js.gz share/html/js/i18n/grid.locale-id.js.gz share/html/js/i18n/grid.locale-is.js.gz share/html/js/i18n/grid.locale-it.js.gz share/html/js/i18n/grid.locale-ja.js.gz share/html/js/i18n/grid.locale-kr.js.gz share/html/js/i18n/grid.locale-lt.js.gz share/html/js/i18n/grid.locale-mne.js.gz share/html/js/i18n/grid.locale-nl.js.gz share/html/js/i18n/grid.locale-no.js.gz share/html/js/i18n/grid.locale-pl.js.gz share/html/js/i18n/grid.locale-pt-br.js.gz share/html/js/i18n/grid.locale-pt.js.gz share/html/js/i18n/grid.locale-ro.js.gz share/html/js/i18n/grid.locale-ru.js.gz share/html/js/i18n/grid.locale-sk.js.gz share/html/js/i18n/grid.locale-sr-latin.js.gz share/html/js/i18n/grid.locale-sr.js.gz share/html/js/i18n/grid.locale-sv.js.gz share/html/js/i18n/grid.locale-th.js.gz share/html/js/i18n/grid.locale-tr.js.gz share/html/js/i18n/grid.locale-tw.js.gz share/html/js/i18n/grid.locale-ua.js.gz share/html/js/i18n/grid.locale-vi.js.gz share/html/js/jquery.jqGrid.min.js.gz share/html/plugins/grid.addons.js.gz share/html/plugins/grid.postext.js.gz share/html/plugins/grid.setcolumns.js.gz share/html/plugins/jquery.contextmenu.js.gz share/html/plugins/jquery.searchFilter.js.gz share/html/plugins/jquery.tablednd.js.gz share/html/plugins/searchFilter.css share/html/plugins/ui.multiselect.css share/html/plugins/ui.multiselect.js.gz share/mail-dmarc.cron share/mail-dmarc.ini share/mail_dmarc_schema.mysql share/mail_dmarc_schema.pgsql share/mail_dmarc_schema.sqlite share/public_suffix_list share/rua-schema.xsd t/00.Dmarc.t t/01.Policy.t t/03.Base.t t/04.PurePerl.t t/06.Result.t t/09.HTTP.t t/10.Report.t t/11.Report.Store.t t/12.Report.Store.SQL.t t/13.Report.Aggregate.t t/14.Report.Aggregate.Metadata.t t/15.Report.Aggregate.Record.t t/16.Report.Aggregate.Record.Auth_Results.t t/17.Report.Aggregate.Schema.t t/20.Report.URI.t t/21.Report.Send.t t/22.Report.Send.SMTP.t t/23.Report.Send.HTTP.t t/25.Report.Receive.t t/26.Report.Sender.t t/backends/mail-dmarc.sql.mysql.ini t/backends/mail-dmarc.sql.Pg.ini t/backends/mail-dmarc.sql.SQLite.ini t/mail-dmarc.ini t/whitelist TODO.md xt/author-critic.t xt/perlcritic.rc Mail-DMARC-1.20240314/MANIFEST.SKIP000444000765000024 26514574361234 15100 0ustar00mattstaff000000000000.DS_Store .coveralls.yml .git .test .release .tar.gz .travis.yml ^_build ^blib ^Makefile$ ^Makefile.old$ ^Build$ ^MANIFEST\.bak$ ^MYMETA. dmarc_reports.sqlite t/reports-test.sqlite Mail-DMARC-1.20240314/META.json000444000765000024 1634314574361234 14667 0ustar00mattstaff000000000000{ "abstract" : "Perl implementation of DMARC", "author" : [ "Matt Simerson ", "Davide Migliavacca ", "Marc Bradshaw " ], "dynamic_config" : 1, "generated_by" : "Module::Build version 0.4231", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Mail-DMARC", "prereqs" : { "build" : { "requires" : { "Module::Build" : "0.3601" } }, "configure" : { "requires" : { "File::ShareDir::Install" : "0.06", "Module::Build" : "0.3601" } }, "runtime" : { "recommends" : { "Mail::DKIM" : "0", "Net::IMAP::Simple" : "0", "Net::SMTPS" : "0" }, "requires" : { "Carp" : "0", "Config::Tiny" : "0", "DBD::SQLite" : "1.31", "DBIx::Simple" : "1.35", "Data::Dumper" : "0", "Email::MIME" : "0", "Email::Sender" : "0", "Email::Sender::Simple" : "1.300032", "Email::Simple" : "0", "Encode" : "0", "English" : "0", "File::ShareDir" : "1.00", "Getopt::Long" : "0", "HTTP::Tiny" : "0", "IO::Compress::Gzip" : "0", "IO::Compress::Zip" : "0", "IO::File" : "0", "IO::Socket::SSL" : "0", "IO::Uncompress::Gunzip" : "0", "IO::Uncompress::Unzip" : "0", "Net::DNS::Resolver" : "0", "Net::IDN::Encode" : "0", "Net::IP" : "0", "Net::SSLeay" : "0", "POSIX" : "0", "Pod::Usage" : "0", "Regexp::Common" : "2013031301", "Socket" : "0", "Socket6" : "0.23", "Sys::Hostname" : "0", "Sys::Syslog" : "0", "Test::File::ShareDir" : "0", "URI" : "0", "XML::LibXML" : "0", "perl" : "v5.10.0" } }, "test" : { "requires" : { "Net::DNS::Resolver::Mock" : "0", "Test::Exception" : "0", "Test::File::ShareDir" : "0", "Test::More" : "0", "Test::Output" : "0" } } }, "provides" : { "Mail::DMARC" : { "file" : "lib/Mail/DMARC.pm", "version" : "1.20240314" }, "Mail::DMARC::Base" : { "file" : "lib/Mail/DMARC/Base.pm", "version" : "1.20240314" }, "Mail::DMARC::HTTP" : { "file" : "lib/Mail/DMARC/HTTP.pm", "version" : "1.20240314" }, "Mail::DMARC::Policy" : { "file" : "lib/Mail/DMARC/Policy.pm", "version" : "1.20240314" }, "Mail::DMARC::PurePerl" : { "file" : "lib/Mail/DMARC/PurePerl.pm", "version" : "1.20240314" }, "Mail::DMARC::Report" : { "file" : "lib/Mail/DMARC/Report.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Aggregate" : { "file" : "lib/Mail/DMARC/Report/Aggregate.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Aggregate::Metadata" : { "file" : "lib/Mail/DMARC/Report/Aggregate/Metadata.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Aggregate::Record" : { "file" : "lib/Mail/DMARC/Report/Aggregate/Record.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Aggregate::Record::Auth_Results" : { "file" : "lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM" : { "file" : "lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results/DKIM.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF" : { "file" : "lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results/SPF.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Aggregate::Record::Identifiers" : { "file" : "lib/Mail/DMARC/Report/Aggregate/Record/Identifiers.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Aggregate::Record::Row" : { "file" : "lib/Mail/DMARC/Report/Aggregate/Record/Row.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Aggregate::Record::Row::Policy_Evaluated" : { "file" : "lib/Mail/DMARC/Report/Aggregate/Record/Row/Policy_Evaluated.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Receive" : { "file" : "lib/Mail/DMARC/Report/Receive.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Send" : { "file" : "lib/Mail/DMARC/Report/Send.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Send::HTTP" : { "file" : "lib/Mail/DMARC/Report/Send/HTTP.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Send::SMTP" : { "file" : "lib/Mail/DMARC/Report/Send/SMTP.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Sender" : { "file" : "lib/Mail/DMARC/Report/Sender.pm" }, "Mail::DMARC::Report::Store" : { "file" : "lib/Mail/DMARC/Report/Store.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Store::SQL" : { "file" : "lib/Mail/DMARC/Report/Store/SQL.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Store::SQL::Grammars::MySQL" : { "file" : "lib/Mail/DMARC/Report/Store/SQL/Grammars/MySQL.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Store::SQL::Grammars::PostgreSQL" : { "file" : "lib/Mail/DMARC/Report/Store/SQL/Grammars/PostgreSQL.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::Store::SQL::Grammars::SQLite" : { "file" : "lib/Mail/DMARC/Report/Store/SQL/Grammars/SQLite.pm", "version" : "1.20240314" }, "Mail::DMARC::Report::URI" : { "file" : "lib/Mail/DMARC/Report/URI.pm", "version" : "1.20240314" }, "Mail::DMARC::Result" : { "file" : "lib/Mail/DMARC/Result.pm", "version" : "1.20240314" }, "Mail::DMARC::Result::Reason" : { "file" : "lib/Mail/DMARC/Result/Reason.pm", "version" : "1.20240314" }, "Mail::DMARC::Test::Transport" : { "file" : "lib/Mail/DMARC/Test/Transport.pm" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/msimerson/mail-dmarc/issues" }, "homepage" : "https://github.com/msimerson/mail-dmarc/wiki", "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "url" : "https://github.com/msimerson/mail-dmarc" } }, "version" : "1.20240314", "x_contributors" : [ "Benny Pedersen ", "Jean Paul Galea ", "Marisa Clardy ", "Priyadi Iman Nurcahyo ", "Ricardo Signes " ], "x_serialization_backend" : "JSON::PP version 4.06" } Mail-DMARC-1.20240314/META.yml000444000765000024 1210214574361234 14504 0ustar00mattstaff000000000000--- abstract: 'Perl implementation of DMARC' author: - 'Matt Simerson ' - 'Davide Migliavacca ' - 'Marc Bradshaw ' build_requires: Module::Build: '0.3601' Net::DNS::Resolver::Mock: '0' Test::Exception: '0' Test::File::ShareDir: '0' Test::More: '0' Test::Output: '0' configure_requires: File::ShareDir::Install: '0.06' Module::Build: '0.3601' dynamic_config: 1 generated_by: 'Module::Build version 0.4231, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Mail-DMARC provides: Mail::DMARC: file: lib/Mail/DMARC.pm version: '1.20240314' Mail::DMARC::Base: file: lib/Mail/DMARC/Base.pm version: '1.20240314' Mail::DMARC::HTTP: file: lib/Mail/DMARC/HTTP.pm version: '1.20240314' Mail::DMARC::Policy: file: lib/Mail/DMARC/Policy.pm version: '1.20240314' Mail::DMARC::PurePerl: file: lib/Mail/DMARC/PurePerl.pm version: '1.20240314' Mail::DMARC::Report: file: lib/Mail/DMARC/Report.pm version: '1.20240314' Mail::DMARC::Report::Aggregate: file: lib/Mail/DMARC/Report/Aggregate.pm version: '1.20240314' Mail::DMARC::Report::Aggregate::Metadata: file: lib/Mail/DMARC/Report/Aggregate/Metadata.pm version: '1.20240314' Mail::DMARC::Report::Aggregate::Record: file: lib/Mail/DMARC/Report/Aggregate/Record.pm version: '1.20240314' Mail::DMARC::Report::Aggregate::Record::Auth_Results: file: lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results.pm version: '1.20240314' Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM: file: lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results/DKIM.pm version: '1.20240314' Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF: file: lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results/SPF.pm version: '1.20240314' Mail::DMARC::Report::Aggregate::Record::Identifiers: file: lib/Mail/DMARC/Report/Aggregate/Record/Identifiers.pm version: '1.20240314' Mail::DMARC::Report::Aggregate::Record::Row: file: lib/Mail/DMARC/Report/Aggregate/Record/Row.pm version: '1.20240314' Mail::DMARC::Report::Aggregate::Record::Row::Policy_Evaluated: file: lib/Mail/DMARC/Report/Aggregate/Record/Row/Policy_Evaluated.pm version: '1.20240314' Mail::DMARC::Report::Receive: file: lib/Mail/DMARC/Report/Receive.pm version: '1.20240314' Mail::DMARC::Report::Send: file: lib/Mail/DMARC/Report/Send.pm version: '1.20240314' Mail::DMARC::Report::Send::HTTP: file: lib/Mail/DMARC/Report/Send/HTTP.pm version: '1.20240314' Mail::DMARC::Report::Send::SMTP: file: lib/Mail/DMARC/Report/Send/SMTP.pm version: '1.20240314' Mail::DMARC::Report::Sender: file: lib/Mail/DMARC/Report/Sender.pm Mail::DMARC::Report::Store: file: lib/Mail/DMARC/Report/Store.pm version: '1.20240314' Mail::DMARC::Report::Store::SQL: file: lib/Mail/DMARC/Report/Store/SQL.pm version: '1.20240314' Mail::DMARC::Report::Store::SQL::Grammars::MySQL: file: lib/Mail/DMARC/Report/Store/SQL/Grammars/MySQL.pm version: '1.20240314' Mail::DMARC::Report::Store::SQL::Grammars::PostgreSQL: file: lib/Mail/DMARC/Report/Store/SQL/Grammars/PostgreSQL.pm version: '1.20240314' Mail::DMARC::Report::Store::SQL::Grammars::SQLite: file: lib/Mail/DMARC/Report/Store/SQL/Grammars/SQLite.pm version: '1.20240314' Mail::DMARC::Report::URI: file: lib/Mail/DMARC/Report/URI.pm version: '1.20240314' Mail::DMARC::Result: file: lib/Mail/DMARC/Result.pm version: '1.20240314' Mail::DMARC::Result::Reason: file: lib/Mail/DMARC/Result/Reason.pm version: '1.20240314' Mail::DMARC::Test::Transport: file: lib/Mail/DMARC/Test/Transport.pm recommends: Mail::DKIM: '0' Net::IMAP::Simple: '0' Net::SMTPS: '0' requires: Carp: '0' Config::Tiny: '0' DBD::SQLite: '1.31' DBIx::Simple: '1.35' Data::Dumper: '0' Email::MIME: '0' Email::Sender: '0' Email::Sender::Simple: '1.300032' Email::Simple: '0' Encode: '0' English: '0' File::ShareDir: '1.00' Getopt::Long: '0' HTTP::Tiny: '0' IO::Compress::Gzip: '0' IO::Compress::Zip: '0' IO::File: '0' IO::Socket::SSL: '0' IO::Uncompress::Gunzip: '0' IO::Uncompress::Unzip: '0' Net::DNS::Resolver: '0' Net::IDN::Encode: '0' Net::IP: '0' Net::SSLeay: '0' POSIX: '0' Pod::Usage: '0' Regexp::Common: '2013031301' Socket: '0' Socket6: '0.23' Sys::Hostname: '0' Sys::Syslog: '0' Test::File::ShareDir: '0' URI: '0' XML::LibXML: '0' perl: v5.10.0 resources: bugtracker: https://github.com/msimerson/mail-dmarc/issues homepage: https://github.com/msimerson/mail-dmarc/wiki license: http://dev.perl.org/licenses/ repository: https://github.com/msimerson/mail-dmarc version: '1.20240314' x_contributors: - 'Benny Pedersen ' - 'Jean Paul Galea ' - 'Marisa Clardy ' - 'Priyadi Iman Nurcahyo ' - 'Ricardo Signes ' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Mail-DMARC-1.20240314/Makefile.PL000444000765000024 1402614574361234 15214 0ustar00mattstaff000000000000use strict; use warnings FATAL => 'all'; use 5.008; use ExtUtils::MakeMaker; use File::ShareDir::Install; $File::ShareDir::Install::INCLUDE_DOTFILES = 1; $File::ShareDir::Install::INCLUDE_DOTDIRS = 1; install_share dist => "share"; my %META = ( "prereqs" => { "configure" => { "requires" => { "ExtUtils::MakeMaker" => 0, "File::ShareDir::Install" => "0.06", } }, "build" => { "requires" => { } }, "test" => { "recommends" => { "XML::SAX::ParserFactory" => "0", "XML::Validator::Schema" => "0" }, "requires" => { "Test::Exception" => 0, "Test::File::ShareDir" => 0, "Test::More" => 0, "Test::Output" => 0, "Net::DNS::Resolver::Mock" => 0 } }, "runtime" => { "recommends" => { "CGI" => 0, "HTTP::Request" => 0, "JSON" => 0, "LWP::UserAgent" => 0, "Mail::DKIM::PrivateKey" => 0, "Mail::DKIM::Signer" => 0, "Mail::DKIM::TextWrap" => 0, "Net::HTTP" => 0, "Net::SMTPS" => 0, "Net::Server::HTTP" => 0, }, "requires" => { "perl" => "5.10.0", "CPAN" => 0, "Carp" => 0, "Config::Tiny" => 0, "DBD::SQLite" => "1.31", "DBIx::Simple" => "1.35", "Data::Dumper" => 0, "Email::MIME" => 0, "Email::Sender" => 0, "Email::Sender::Simple" => "1.300032", "Email::Simple" => 0, "Encode" => 0, "English" => 0, "File::ShareDir" => 0, "Getopt::Long" => 0, "HTTP::Tiny" => 0, "IO::Compress::Gzip" => 0, "IO::Compress::Zip" => 0, "IO::File" => 0, "IO::Socket::SSL" => 0, "IO::Uncompress::Gunzip" => 0, "IO::Uncompress::Unzip" => 0, "Net::DNS::Resolver" => 0, "Net::IDN::Encode" => 0, "Net::IP" => 0, "Net::SSLeay" => 0, "POSIX" => 0, "Pod::Usage" => 0, "Regexp::Common" => "2013031301", "Socket" => 0, "Socket6" => "0.23", "Sys::Hostname" => 0, "Sys::Syslog" => 0, "URI" => 0, "XML::LibXML" => 0, } }, "develop" => { "requires" => { }, "suggests" => { } }, }, "resources" => { "bugtracker" => { "web" => "https://github.com/msimerson/mail-dmarc/issues" }, "homepage" => "https://github.com/msimerson/mail-dmarc/wiki", "repository" => { "type" => "git", "url" => "git://github.com/msimerson/mail-dmarc.git", "web" => "https://github.com/msimerson/mail-dmarc" }, "license" => [ 'http://dev.perl.org/licenses/' ], }, "optional_features" => { "MySQL" => { "description" => "MySQL backend storage", "prereqs" => { "runtime" => { "requires" => { 'DBD::mysql' => '4.001', } } } }, "Postgres" => { "description" => "PostgresQL backend storage", "prereqs" => { "runtime" => { "requires" => { 'DBD::Pg' => '0' } } } }, "web_service" => { "description" => "HTTP web UI to DMARC reports", "prereqs" => { "runtime" => { "requires" => { "CGI" => 0, "HTTP::Request" => 0, "JSON" => 0, "Net::HTTP" => 0, "Net::Server::HTTP" => 0, } } } }, "smtp_sending" => { "description" => "Send DMARC reports via SMTP", "prereqs" => { "runtime" => { "requires" => { "Email::Sender" => 0, "Net::SMTPS" => 0, "Mail::DKIM::PrivateKey" => 0, "Mail::DKIM::Signer" => 0, "Mail::DKIM::TextWrap" => 0 } } } }, "imap_fetch" => { "description" => "Retrieve DMARC reports from an IMAP account", "prereqs" => { "runtime" => { "requires" => { "Net::IMAP::Simple" => 0, } } } } }, ); my %MM_ARGS = ( "NAME" => "Mail::DMARC", "ABSTRACT" => "Perl implementation of DMARC", "AUTHOR" => "Matt Simerson , Davide Migliavacca , Marc Bradshaw ", "DISTNAME" => "Mail-DMARC", "EXE_FILES" => [ "bin/dmarc_update_public_suffix_list", "bin/dmarc_send_reports", "bin/dmarc_httpd", "bin/dmarc_lookup", "bin/dmarc_receive", "bin/dmarc_http_client", "bin/dmarc_view_reports" ], "META_MERGE" => { "meta-spec" => { version => 2 }, "x_contributors" => [ "Benny Pedersen ", "Jean Paul Galea ", "Marisa Clardy ", "Priyadi Iman Nurcahyo ", "Ricardo Signes " ], }, "MIN_PERL_VERSION" => "5.008", "VERSION" => "1.20191025", "test" => { "TESTS" => "t/*.t" }, "clean" => { "FILES" => [ "dmarc_reports.sqlite", "t/reports-test.sqlite", 'MANIFEST.bak' ] }, ); # some nifty boilerplate from local::lib my $requires = $MM_ARGS{PREREQ_PM} = { %{$META{prereqs}{runtime}{requires}} }; $MM_ARGS{META_ADD} = { 'meta-spec' => { version => 2 }, %META }; for (qw(configure build test runtime)) { my $key = $_ eq 'runtime' ? 'PREREQ_PM' : uc $_.'_REQUIRES'; my $r = $MM_ARGS{$key} = { %{$META{prereqs}{$_}{requires} || {}}, %{delete $MM_ARGS{$key} || {}}, }; defined $r->{$_} or delete $r->{$_} for keys %$r; } my $eumm_version = eval $ExtUtils::MakeMaker::VERSION; if ( $eumm_version < 6.47_01 ) { delete $MM_ARGS{MIN_PERL_VERSION}; } if ( $eumm_version < 6.51_03 ) { delete $MM_ARGS{CONFIGURE_REQUIRES}; } if ( $eumm_version < 6.63_03 ) { $MM_ARGS{BUILD_REQUIRES} = {%{$MM_ARGS{BUILD_REQUIRES}}, %{delete $MM_ARGS{TEST_REQUIRES}}}; } if ( $eumm_version < 6.55_01 ) { $MM_ARGS{PREREQ_PM} = {%{$MM_ARGS{PREREQ_PM}}, %{delete $MM_ARGS{BUILD_REQUIRES}}} } my %WriteMakefileArgs = (%MM_ARGS); WriteMakefile(%WriteMakefileArgs); { package MY; use File::ShareDir::Install qw(postamble); } Mail-DMARC-1.20240314/README.md000444000765000024 2602314574361234 14521 0ustar00mattstaff000000000000# Status Badges [![Build Status](https://github.com/msimerson/mail-dmarc/actions/workflows/ci.yml/badge.svg)](https://github.com/msimerson/mail-dmarc/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/msimerson/mail-dmarc/badge.svg)](https://coveralls.io/r/msimerson/mail-dmarc) # NAME Mail::DMARC - Perl implementation of DMARC # VERSION version 1.20240314 # SYNOPSIS DMARC: Domain-based Message Authentication, Reporting and Conformance my $dmarc = Mail::DMARC::PurePerl->new( ... # see the documentation for the "new" method for required args ); my $result = $dmarc->validate(); if ( $result->result eq 'pass' ) { ...continue normal processing... return; }; # any result that did not pass is a fail. Now for disposition if ( $result->evalated->disposition eq 'reject' ) { ...treat the sender to a 550 ... }; if ( $result->evalated->disposition eq 'quarantine' ) { ...assign a bunch of spam points... }; if ( $result->evalated->disposition eq 'none' ) { ...continue normal processing... }; # DESCRIPTION This module is a suite of tools for implementing DMARC. It adheres to the 2013 DMARC draft, intending to implement every MUST and every SHOULD. This module can be used by... - MTAs and filtering tools like SpamAssassin to validate that incoming messages are aligned with the purported sender's policy. - email senders, to receive DMARC reports from other mail servers and display them via CLI and web interfaces. - MTA operators to send DMARC reports to DMARC author domains. When a message arrives via SMTP, the MTA or filtering application can pass in a small amount of metadata about the connection (envelope details, SPF and DKIM results) to Mail::DMARC. When the **validate** method is called, Mail::DMARC will determine if: a. the header_from domain exists b. the header_from domain publishes a DMARC policy c. if a policy is published... d. does the message conform to the published policy? e. did the policy request reporting? If so, save details. The validation results are returned as a [Mail::DMARC::Result](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AResult) object. If the author domain requested a report, it was saved to the [Report Store](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AReport%3A%3AStore). The Store class includes a SQL implementation that is tested with SQLite, MySQL and PostgreSQL. There is more information available in the $result object. See [Mail::DMARC::Result](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AResult) for complete details. Reports are viewed with the [dmarc\_view\_reports](https://metacpan.org/pod/dmarc_view_reports) program or with a web browser and the [dmarc\_httpd](https://metacpan.org/pod/dmarc_httpd) program. Aggregate reports are sent to their requestors with the [dmarc\_send\_reports](https://metacpan.org/pod/dmarc_send_reports) program. For aggregate reports that you have been sent, the [dmarc\_receive](https://metacpan.org/pod/dmarc_receive) program will parse the email messages (from IMAP, Mbox, or files) and save the report results into the [Report Store](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AReport%3A%3AStore). The report store can use the same database to store reports you have received as well as reports you will send. There are several ways to identify the difference, including: - received reports will have a null value for report\_policy\_published.rua - outgoing reports will have null values for report.uuid and report\_record.count # CLASSES [Mail::DMARC](https://metacpan.org/pod/Mail%3A%3ADMARC) - the perl interface for DMARC [Mail::DMARC::Policy](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3APolicy) - a DMARC policy [Mail::DMARC::PurePerl](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3APurePerl) - Pure Perl implementation of DMARC [Mail::DMARC::Result](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AResult) - the results of applying policy [Mail::DMARC::Report](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AReport) - Reporting: the R in DMARC > [Mail::DMARC::Report::Send](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AReport%3A%3ASend) - send reports via SMTP & HTTP > > [Mail::DMARC::Report::Receive](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AReport%3A%3AReceive) - receive and store reports from email, HTTP > > [Mail::DMARC::Report::Store](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AReport%3A%3AStore) - a persistent data store for aggregate reports > > [Mail::DMARC::Report::View](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3AReport%3A%3AView) - CLI and CGI methods for viewing reports [Mail::DMARC::libopendmarc](http://search.cpan.org/~shari/Mail-DMARC-opendmarc) - an XS implementation using libopendmarc # METHODS ## new Create a DMARC object. my $dmarc = Mail::DMARC::PurePerl->new; Populate it. $dmarc->source_ip('192.0.1.1'); $dmarc->envelope_to('recipient.example.com'); $dmarc->envelope_from('sender.example.com'); $dmarc->header_from('sender.example.com'); $dmarc->dkim( $dkim_verifier ); $dmarc->spf([ { domain => 'example.com', scope => 'mfrom', result => 'pass', }, { scope => 'helo', domain => 'mta.example.com', result => 'fail', }, ]); Run the request: my $result = $dmarc->validate(); Alternatively, pass in all the required parameters in one shot: my $dmarc = Mail::DMARC::PurePerl->new( source_ip => '192.0.1.1', envelope_to => 'example.com', envelope_from => 'cars4you.info', header_from => 'yahoo.com', dkim => $dkim_results, # same format spf => $spf_results, # as previous example ); my $result = $dmarc->validate(); ## source\_ip The remote IP that attempted sending the message. DMARC only uses this data for reporting to domains that request DMARC reports. ## envelope\_to The domain portion of the RFC5321.RcptTo, (aka, the envelope recipient), and the bold portion in the following example: > RCPT TO:&lt;user@**example.com**> ## envelope\_from The domain portion of the RFC5321.MailFrom, (aka, the envelope sender). That is the the bold portion in the following example: > MAIL FROM:&lt;user@**example.com**> ## header\_from The domain portion of the RFC5322.From, aka, the From message header. > From: Ultimate Vacation &lt;sweepstakes@**example.com**> You can instead pass in the entire From: header with header\_from\_raw. ## header\_from\_raw Retrieve the header\_from domain by parsing it from a raw From field/header. The domain portion is extracted by [get\_dom\_from\_header](https://metacpan.org/pod/Mail%3A%3ADMARC%3A%3APurePerl%23get_dom_from_header), which is fast, generally effective, but also rather crude. It has limits, so read the description. ## dkim If Mail::DKIM::Verifier was used to validate the message, just pass in the Mail::DKIM::Verifier object that processed the message: $dmarc->dkim( $dkim_verifier ); Otherwise, pass in an array reference. Each member of the DKIM array results represents a DKIM signature in the message and consists of the 4 keys shown in this example: $dmarc->dkim( [ { domain => 'example.com', selector => 'apr2013', result => 'fail', human_result=> 'fail (body has been altered)', }, { # 2nd signature, if present }, ] ); The dkim results can also be build iteratively by passing in key value pairs or hash references for each signature in the message: $dmarc->dkim( domain => 'sig1.com', result => 'fail' ); $dmarc->dkim( domain => 'sig2.com', result => 'pass' ); $dmarc->dkim( { domain => 'example.com', result => 'neutral' } ); Each hash or hashref is appended to the dkim array. Finally, you can pass a coderef which won't be called until the dkim method is used to read the dkim results. It must return an array reference as described above. The dkim result is an array reference. ### domain The d= parameter in the DKIM signature ### selector The s= parameter in the DKIM signature ### result The validation results of this signature. One of: none, pass, fail, policy, neutral, temperror, or permerror ### human result Additional information about the DKIM result. This is comparable to Mail::DKIM::Verifier->result\_detail. ## spf The spf method works exactly the same as dkim. It accepts named arguments, a hashref, an arrayref, or a coderef: $dmarc->spf( domain => 'example.com', scope => 'mfrom', result => 'pass', ); The SPF domain and result are required for DMARC validation and the scope is used for reporting. ### domain The SPF checked domain ### scope The scope of the checked domain: mfrom, helo ### result The SPF result code: none, neutral, pass, fail, softfail, temperror, or permerror. # DESIGN & GOALS ## Correct The DMARC spec is lengthy and evolving, making correctness a moving target. In cases where correctness is ambiguous, options are generally provided. ## Easy to use Providing an implementation of DMARC that SMTP utilities can utilize will aid DMARC adoption. The list of dependencies appears long because of reporting. If this module is used without reporting, the number of dependencies not included with perl is about 5. ## Maintainable Since DMARC is evolving, this implementation aims to be straight forward and easy to alter and extend. The programming style is primarily OO, which carries a small performance penalty but dividends in maintainability. When multiple options are available, such as when sending reports via SMTP or HTTP, calls should be made to the parent Send class to broker the request. When storing reports, calls are made to the Store class which dispatches to the SQL class. The idea is that if someone desired a data store other than those provided by perl's DBI class, they could easily implement their own. If you do, please fork it on GitHub and share. ## Fast If you deploy this in an environment where performance is insufficient, please profile the app and submit a report and preferably, patches. # SEE ALSO [Mail::DMARC on GitHub](https://github.com/msimerson/mail-dmarc) 2015-03 [RFC 7489](https://tools.ietf.org/html/rfc7489) DMARC [Best Current Practices](http://tools.ietf.org/html/draft-crocker-dmarc-bcp-03) # HISTORY The daddy of this perl module was a [DMARC module for the qpsmtpd MTA](https://github.com/smtpd/qpsmtpd/blob/master/plugins/dmarc). # AUTHORS - Matt Simerson - Davide Migliavacca - Marc Bradshaw # CONTRIBUTORS - Benny Pedersen - Jean Paul Galea - Marisa Clardy - Priyadi Iman Nurcahyo - Ricardo Signes # COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Mail-DMARC-1.20240314/TODO.md000444000765000024 104514574361234 14306 0ustar00mattstaff000000000000 ### Planned Features * [ ] Forensic reports * [ ] HTTP report delivery * [ ] more SMTP error reporting * [ ] Report SPF records in dmarc\_lookup output * [ ] add a 'cron' mode for dmarc\_send and dmarc\_receive, if no controlling TTY, don't output status messages * [ ] skip DMARC reporting for incoming DMARC reports destined to config->organization->email ### Maybe TODO: * [ ] detect > 1 From recipient, apply strongest policy ### Done * [x] automatically delete reports after 12 delivery errors * [x] send a 'too big' notification email Mail-DMARC-1.20240314/bin000755000765000024 014574361234 13632 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/bin/dmarc_http_client000555000765000024 453714574361234 17411 0ustar00mattstaff000000000000#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Getopt::Long; use HTTP::Request; use JSON; use LWP::UserAgent; my %command_line_options = ( 'host:s' => \my $host, 'port:s' => \my $port, 'data:s' => \my $data, ); GetOptions (%command_line_options); if (!$host) { $host = 'localhost'; warn "using default: --host=$host\n"; }; if (!$port) { $port = '8080'; warn "using default: --port=$port\n"; }; if (!$data) { $data = get_json_request(); warn "using sample --data\n"; }; if ($data eq '-') { $data = ''; while ($_ = <>) { chomp; $data .= $_; }; } my $url = "http://$host:$port/dmarc/json/validate"; my $ua = LWP::UserAgent->new; my $req = HTTP::Request->new(POST => $url); $req->content_type('application/json'); $req->content($data); my $response = $ua->request($req)->decoded_content; #print Dumper($response); # raw JSON response my $result; eval { $result = JSON->new->utf8->decode($response) }; if ($result) { print Dumper($result); # pretty formatted struct exit; }; die $response; sub get_json_request { return JSON->new->encode ({ source_ip => '192.0.1.1', envelope_to => 'example.com', envelope_from => 'cars4you.info', header_from => 'yahoo.com', dkim => [ { domain => 'example.com', selector => 'apr2013', result => 'fail', human_result => 'fail (body has been altered)', } ], spf => [ { domain => 'example.com', scope => 'mfrom', result => 'pass', } ], }); }; __END__ =pod =head1 NAME dmarc_http_client: an HTTP client for submitting a DMARC validation request =head1 SYNOPSIS Send JSON encoded HTTP requests to the DMARC validation service provided by dmarc_httpd. dmarc_http_client --host=localhost --port=8080 --data='{"envelope_from":"cars4you.info"...}' The data option accepts a special '-' value that will read the JSON encoded data from STDIN. Use it like this: cat /path/to/data.json | dmarc_http_client --data=- =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =cut Mail-DMARC-1.20240314/bin/dmarc_httpd000555000765000024 412714574361234 16212 0ustar00mattstaff000000000000#!/usr/bin/perl use strict; use warnings; use lib 'lib'; use Mail::DMARC; use Mail::DMARC::HTTP; my $dmarc = Mail::DMARC->new(); $dmarc->is_public_suffix('tnpi.net'); my $report = $dmarc->report; my $http = Mail::DMARC::HTTP->new; $http->dmarc_httpd($report); exit; __END__ =pod =head1 NAME dmarc_httpd: a web server for DMARC validation and report viewing =head1 SYNOPSIS A HTTP interface for: =over 4 =item * local DMARC reports =item * DMARC validator service =back Start the HTTP server: dmarc_httpd Connect with a web browser to L. =head1 DESCRIPTION The HTTP server handles 4 types of requests: =over 4 =item * / Serves files stored in the perl share directory of the Mail::DMARC module. This presently entails one HTML file and a handful of CSS and JS files for the report viewing feature. =item * /dmarc/json/validate - DMARC validation requests Accepts a JSON encoded HTTP POST request. Validates the request, performs a DMARC validation and returns a JSON encoded result object. This is the API for non-perl applications to utilize Mail::DMARC. See the dmarc_http_client app for a usage example. =item * /dmarc/json/report Accepts AJAX requests from the browser and returns JSON encoded DMARC reports. =item * /dmarc/json/row Accepts AJAX requests from the browser and returns JSON encoded DMARC report rows. =back An implementation that uses the http validation service is the included and another is the dmarc plugin in the . A L is available which shows the web interface. It is implemented almost entirely in JavaScript, using jQuery, jQueryUI, and jqGrid. Web server settings are in the [http] and [https] sections of mail-dmarc.ini. =head1 THANKS jQuery - http://www.jquery.com/ jqGrid - http://www.trirand.com/blog/ =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =cut Mail-DMARC-1.20240314/bin/dmarc_lookup000555000765000024 424214574361234 16376 0ustar00mattstaff000000000000#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Getopt::Long; use Pod::Usage; $Data::Dumper::Sortkeys = 1; $Data::Dumper::Quotekeys = 0; use lib 'lib'; use Mail::DMARC::PurePerl; my %command_line_options = ( 'domain:s' => \my $domain, 'verbose' => \my $verbose, ); GetOptions (%command_line_options); $verbose = 1 if ! defined $verbose; $domain ||= $ARGV[0]; $domain or pod2usage; my $dmarc = Mail::DMARC::PurePerl->new; $dmarc->verbose($verbose); $dmarc->header_from($domain); my $policy = $dmarc->discover_policy() or die "no DMARC policy published for $domain\n"; print Dumper( $policy ); if ( $policy->rua ) { print "\n"; my $uri_count = $dmarc->has_valid_reporting_uri( $policy->rua ); print "valid report URI: "; print $uri_count ? "yes\n" : "no\n"; }; exit; __END__ =pod =head1 NAME dmarc_lookup: look up DMARC policy for a domain =head1 SYNOPSIS dmarc_lookup example.com [ --verbose ] =head1 DESCRIPTION Query the DNS for a DMARC policy for a (sub)domain. Displays any found results as the DNS record as a perl object. In the simplest case, where the domain name in the email From header matches the I, this is roughly equivalent to the following commands: dig +short _dmarc.example.com TXT print $_->txtdata."\n" for Net::DNS::Resolver->new(dnsrch=>0)->send('_dmarc.example.com','TXT')->answer; When the domain name in the email From header (header_from) is not an Organizational Domain (ex: www.example.com), an attempt is made to determine the O.D. using the Mozilla Public Suffix List. When the O.D. differs from the header_from, a second DNS query is sent to _dmarc.[O.D.]. =head1 EXAMPLES A DMARC record in DNS format looks like this: v=DMARC1; p=reject; adkim=s; aspf=s; rua=mailto:dmarc@example.com; pct=100; DMARC records are stored as TXT resource records in the DNS, at _dmarc.example.com. Other ways to retrieve a DMARC record for a domain are: =head1 SEE ALSO L =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =cut Mail-DMARC-1.20240314/bin/dmarc_receive000555000765000024 366314574361234 16515 0ustar00mattstaff000000000000#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Getopt::Long; use Pod::Usage; use lib 'lib'; use Mail::DMARC::Report::Receive; $|++; my %command_line_options = ( 'file:s' => \my $file, 'imap' => \my $imap, 'mbox' => \my $mbox, 'verbose+' => \my $verbose, ); GetOptions (%command_line_options); pod2usage(0) if ! $imap && ! $mbox && ! $file; ## no critic (Carp) my $recv = Mail::DMARC::Report::Receive->new() or die; $recv->verbose($verbose) if $verbose; $recv->from_imap if $imap; $recv->from_mbox($mbox) if $mbox; $recv->from_file($file) if $file; exit; __END__ =head1 NAME dmarc_receive: receive aggregate reports via IMAP, mbox, or message file(s) =head1 USAGE dmarc_receive [ --imap | --mbox | --file ] =head1 DESCRIPTION This script processes incoming DMARC reports from IMAP, files, or a mbox formatted file. =head2 IMAP To process reports with IMAP, you must configure the [imap] settings in mail-dmarc.ini. This program will: * log into the IMAP account * select the specified folder (INBOX, dmarc, etc) * for every unread (Unseen) message, search for DMARC reports =head3 IMAP Aggregate report IMAP aggregate reports are detected by the presence of zip or gzip attachments. When an aggregate report is detected: * the attachment is decompressed * the XML is parsed * the report is saved to the report store * the message is marked as read/seen * move message to [imap][a_done] folder (if defined) =head3 IMAP Forensic report IMAP forensic reports are detected by the presence of the content-types message/feedback-report and text/rfc822-headers. When a forensic report is detected it is moved to the [imap][f_done] IMAP folder. =head2 File as message Accepts the filename of a file containing a mail message. The message is parsed and stored. =head2 Mbox Accepts the filename of a mbox format file containing mail messages. The messages are parsed and stored. =cut Mail-DMARC-1.20240314/bin/dmarc_send_reports000555000765000024 60114574361234 17547 0ustar00mattstaff000000000000#!/usr/bin/perl use strict; use warnings; use Mail::DMARC::Report::Sender; my $sender = Mail::DMARC::Report::Sender->new; $sender->run; __END__ =pod =head1 NAME dmarc_send_reports: send aggregate reports =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =cut Mail-DMARC-1.20240314/bin/dmarc_update_public_suffix_list000555000765000024 350614574361234 22326 0ustar00mattstaff000000000000#!/usr/bin/perl use strict; use warnings; use Getopt::Long; use HTTP::Tiny; use Mail::DMARC; use Pod::Usage; my $dryrun = 0; my $random = 0; GetOptions ( 'dryrun' => \$dryrun, 'help' => \my $help, 'random' => \$random, 'config-file=s' => \my $config_file, ); pod2usage if $help; if ( $random ) { my $sleep_for = int(rand(60*60)); sleep $sleep_for; } Mail::DMARC->new( (defined $config_file ? (config_file => $config_file) : ()) )->update_psl_file($dryrun); __END__ =pod =head1 NAME dmarc_update_public_suffix_list: command line tool to download updated public suffix list =head1 SYNOPSIS dmarc_update_public_suffix_list [ --option=value ] =head1 DESCRIPTION Downloads a new Public Suffix List to the location specified by /etc/mail-dmarc.ini The PSL is maintained by the Mozilla Foundation. It is updated a few times per month, you are requested to download no more than once per day. The URL of the file is https://publicsuffix.org/list/effective_tld_names.dat More details can be found at https://publicsuffix.org/ =head2 Options dmarc_update_public_suffix_list [ --dryrun --help ] dryrun - show what would be done without overwriting file random - introduce a random delay to spread server load intended for use when running from crontab config-file - alternate config file path help - print this syntax guide =head1 EXAMPLES To check that a new file can be downloaded without error but not download the file: dmarc_update_public_suffix_list --dryrun To download a new Public Suffix List to the location specified my mail-dmarc.ini dmarc_update_public_suffix_list =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =cut Mail-DMARC-1.20240314/bin/dmarc_view_reports000555000765000024 1713014574361234 17635 0ustar00mattstaff000000000000#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Getopt::Long; use Pod::Usage; $|++; my %command_line_options = ( 'author:s' => \my $author, 'from_dom:s' => \my $from, 'begin:s' => \my $begin, 'end:s' => \my $end, 'disposition:s' => \my $disposition, 'dkim:s' => \my $dkim, 'spf:s' => \my $spf, 'dns' => \my $dns_opt, 'geoip:s' => \my $geoip_opt, 'help' => \my $help, 'verbose' => \my $verbose, ); GetOptions (%command_line_options); use lib 'lib'; use Mail::DMARC::Report; my $report = Mail::DMARC::Report->new; my $gip; pod2usage if $help; my $reports = $report->store->retrieve( (defined $from ? (from_domain => $from ) : () ), (defined $author ? (author => $author ) : () ), (defined $begin ? (begin => $begin ) : () ), (defined $end ? (end => $end ) : () ), ); print_header(); foreach my $report_ref ( reverse @$reports ) { my $rows = $report->store->backend->get_rr( rid => $report_ref->{rid} )->{rows}; next if $disposition && ! grep { $_->{disposition} eq $disposition } @$rows; next if $dkim && ! grep { $_->{dkim} eq $dkim } @$rows; next if $spf && ! grep { $_->{spf } eq $spf } @$rows; print_record($report_ref); print_rows( $rows ); print "\n"; } sub print_record { my $rec = shift; printf "%3s %26s %15s\n", @$rec{qw/ rid author begin /}; return; }; sub print_rows { my $rows = shift; foreach my $row ( @$rows ) { no warnings; ## no critic (NoWarn) next if $disposition && $disposition ne $row->{disposition}; next if $dkim && $dkim ne $row->{dkim}; next if $spf && $spf ne $row->{spf}; printf " | -- %3s %20s %39s %13s %7s %7s", @$row{qw/ count header_from source_ip disposition dkim spf /}; foreach my $r ( @{ $row->{reasons} } ) { print ' ' . $r->{type}; print "( $r->{comment} )" if $r->{comment}; }; my $geoip_details = get_geoip_details( $row->{source_ip} ); print " $geoip_details"; my $dns_hostname = get_dns_hostname( $row->{source_ip} ); print " $dns_hostname\n"; } return; }; sub print_header { printf "%3s %26s %15s\n", qw[ ID Author Report-Start ]; printf " | -- %3s %20s %39s %13s %7s %7s\n", 'Qty','From','IP','Disposition','DKIM','SPF'; return; }; sub get_geoip_details { my $ip = shift; return if ! defined $geoip_opt; $geoip_opt ||= 'city,country_code,continent_code'; $gip ||= get_geoip_db(); return if ! $gip; if ($ip =~ /^\d+\.\d+\.\d+\.\d+$/) { $ip = '::ffff:'.$ip; } my $r = $gip->record_by_addr_v6($ip) or return ''; my @result; my @fields = split(',', $geoip_opt); my @allowed = qw( country_code country_code3 country_name region region_name city postal_code latitude longitude time_zone area_code continent_code metro_code ); foreach my $f (@fields) { next if ! grep {$_ eq $f} @allowed; next if ! $r->${f}(); push @result, $r->${f}(); } return join(', ', @result); } sub get_geoip_db { return $gip if $gip; eval "require Geo::IP"; ## no critic (Eval) if ($@) { warn "unable to load Geo::IP\n"; return; }; foreach my $local ( '/usr/local', '/opt/local', '/usr' ) { my $db_dir = "$local/share/GeoIP"; foreach my $db (qw/ GeoIPCityv6 GeoLiteCityv6 /) { if (-f "$db_dir/$db.dat") { print "using db $db" if $verbose; $gip = Geo::IP->open("$db_dir/$db.dat"); } last if $gip; } last if $gip; }; return $gip; } sub get_dns_hostname { my $ip = shift; return if ! $dns_opt; my @answers = $report->has_dns_rr('PTR', $ip); return '' if 0 == scalar @answers; return $answers[0] if scalar @answers >= 1; print Dumper(\@answers); return; }; exit; __END__ =head1 SYNOPSIS dmarc_view_reports [ --option=value ] Dumps the contents of the DMARC data store to your terminal. The most recent records are show first. =head2 Search Options author - report author (Yahoo! Inc, google.com, etc..) from_dom - message sender domain begin - epoch start time to display messages after end - epoch end time to display messages before disposition - DMARC disposition (none,quarantine,reject) dkim - DKIM alignment result (pass/fail) spf - SPF alignment result (pass/fail) =head2 Other Options dmarc_view_reports [ --geoip --dns --help --verbose ] geoip - do GeoIP lookups (requires the free Maxmind GeoCityLitev6 database). dns - do reverse DNS lookups and display hostnames help - print this syntax guide verbose - print additional debug info =head1 EXAMPLES To search for all reports from google.com that failed DMARC alignment: dmarc_view_reports --author=google.com --dkim=fail --spf=fail Note that we don't use --disposition. That would only tell us the result of applying DMARC policy, not necessarily if the messages failed DMARC alignment. To display GeoIP lookup data for the source ip: dmarc_view_reports --geoip By default; city, country_code & continent_code are shown. You can optionally pass a comma delimited string to --geoip= with any of the following fields: country_code country_code3 country_name region region_name city postal_code latitude longitude time_zone area_code continent_code metro_code dmarc_view_reports --geoip=country_name,continent_code dmarc_view_reports --geoip=continent_code,country_name # keep order dmarc_view_reports --geoip=city,city,city # repeat =head1 SAMPLE OUTPUT ID Recipient From/Sender Report-Start | -- Qty Source IP Disposition DKIM SPF 570 theartfarm.com simerson.net 2013-05-20 09:40:50 | -- 1 75.126.200.152 quarantine fail fail 568 yeah.net tnpi.net 2013-05-21 09:00:00 | -- 1 111.176.77.138 reject fail fail 567 126.com tnpi.net 2013-05-21 09:00:00 | -- 1 49.73.135.125 reject fail fail 565 google.com mesick.us 2013-05-20 17:00:00 | -- 88 208.75.177.101 none pass pass 564 google.com theartfarm.com 2013-05-20 17:00:00 | -- 3 208.75.177.101 none pass pass 563 google.com lynboyer.com 2013-05-20 17:00:00 | -- 1 2a00:1450:4010:c03::235 none pass fail forwarded | -- 12 208.75.177.101 none pass pass | -- 1 209.85.217.174 none pass fail forwarded 561 google.com simerson.net 2013-05-20 17:00:00 | -- 1 208.75.177.101 none pass pass 560 google.com tnpi.net 2013-05-20 17:00:00 | -- 1 208.75.177.101 none pass pass | -- 1 27.20.110.240 reject fail fail 559 hotmail.com lynboyer.com 2013-05-20 20:00:00 | -- 6 208.75.177.101 none pass pass =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =cut Mail-DMARC-1.20240314/bin/install_deps.pl000555000765000024 3374714574361234 17046 0ustar00mattstaff000000000000#!/usr/bin/perl # VERSION 1.12 use strict; use warnings; use CPAN; use Data::Dumper; use English qw( -no_match_vars ); my $apps = [ # { app => 'mysql-server-5', info => { port => 'mysql55-server', dport=>'mysql5', yum =>'mysql-server'} }, # { app => 'apache22' , info => { port => 'apache22', dport=>'', yum => 'httpd' } }, ]; $EUID == 0 or die "You will have better luck if you run me as root.\n"; ## no critic (Carp) my @failed; foreach ( @$apps ) { my $name = $_->{app} or die 'missing app name'; ## no critic (Carp) install_app( $name, $_->{info} ); }; foreach ( get_perl_modules() ) { my $module = $_->{module} or die 'missing module name'; ## no critic (Carp) next if $module eq 'perl'; my $info = $_->{info}; my $version = $info->{version} || ''; print "checking for $module $version\n"; eval "use $module $version"; ## no critic (Eval) next if ! $EVAL_ERROR; next if $info->{ships_with} && $info->{ships_with} eq 'perl'; install_module( $module, $info, $version ); eval "use $module $version"; ## no critic (Eval) if ($EVAL_ERROR) { push @failed, $module; } } if ( scalar @failed > 0 ) { print "The following modules failed installation:\n"; print join( "\n", @failed ); print "\n"; } exit; sub get_perl_modules { if ( -f 'dist.ini' ) { return get_perl_modules_from_ini(); }; if ( -f 'Makefile.PL' ) { return get_perl_modules_from_Makefile_PL(); }; die "unable to find module list. Run this script in the dist dir\n"; ## no critic (Carp) } sub get_perl_modules_from_Makefile_PL { my $fh = IO::File->new( 'Makefile.PL', 'r' ) or die "unable to read Makefile.PL\n"; ## no critic (Carp) my $depth = 0; my @modules; while ( my $line = <$fh> ) { chomp $line; if ( $line =~ /"(PREREQ_PM|prereqs)"\s*=>\s*{/ ) { $depth++; # print "$depth: $line (" . scalar @modules . ")\n"; next; } next if $depth < 1; if ($line =~ /(prereqs|configure|build|test|runtime|develop|requires|recommends|suggests).*{/){ $depth++; # print "$depth: $line (" . scalar @modules . ")\n"; next; } if ($line =~ /}/) { # print "$depth: $line (" . scalar @modules . ")\n"; $depth--; last if $depth == 0; next; }; if ($line !~ /=/) { # no = char means not a module print "$line\n"; next; } my ($mod, $ver) = split /\s*=\s*/, $line; $mod =~ s/[\s'"\#]*//xg; # strip whitespace & quotes ## no critic (Regex) next if ! $mod; push @modules, name_overrides($mod); # print "module: .$mod.\n"; } $fh->close; return @modules; } sub get_perl_modules_from_ini { my $fh = IO::File->new( 'dist.ini', 'r' ) or die "unable to read dist.ini\n"; ## no critic (Carp) my $in = 0; my @modules; while ( my $line = <$fh> ) { # install all prepreqs if ( '[Prereqs' eq substr($line,0,8) ) { # except for ones needed only by devs next if $line =~ /(?:BuildRequires|TestRequires)/i; $in++; next; }; next if ! $in; # print "line: $line\n"; next if ';' eq substr($line,0,1); # comment last if '[' eq substr($line,0,1); # [...] starts a new section my ($mod,$ver) = split /\s*=\s*/, $line; $mod =~ s/\s*//g; # remove whitespace next if ! $mod || ! defined $ver; push @modules, name_overrides($mod); print "module: $mod\n"; } $fh->close; # print Dumper(\@modules); return @modules; } sub install_app { my ( $app, $info ) = @_; if ( lc($OSNAME) eq 'darwin' ) { install_app_darwin( $app, $info ); } elsif ( lc($OSNAME) eq 'freebsd' ) { install_app_freebsd( $app, $info ); } elsif ( lc($OSNAME) eq 'linux' ) { install_app_linux( $app, $info ); }; return; } sub install_app_darwin { my ($app, $info ) = @_; my $port = $info->{dport} || $info->{port} || $app; if ( ! -x '/opt/local/bin/port' ) { print "MacPorts is not installed! Consider installing it.\n"; return; } system "/opt/local/bin/port install $port" and warn "install failed for Darwin port $port"; ## no critic (Carp) return; } sub install_app_freebsd { my ( $app, $info ) = @_; if ( -x '/usr/sbin/pkg' ) { if ( `/usr/sbin/pkg info -x $app` ) { ## no critic (Backtick) return print "$app is installed.\n"; } print "installing $app"; return if install_app_freebsd_pkg($info, $app); } print " from ports..."; if ( -x '/usr/sbin/pkg_info' ) { if ( `/usr/sbin/pkg_info | /usr/bin/grep $app` ) { ## no critic (Backtick) return print "$app is installed.\n"; }; } print "installing $app"; return install_app_freebsd_port($app, $info); } sub install_app_freebsd_port { my ( $app, $info ) = @_; my $name = $info->{port} || $app; my $category = $info->{category} || '*'; my ($portdir) = glob "/usr/ports/$category/$name"; ## no critic (Backtick) if ( $portdir && -d $portdir ) { print " from ports ($portdir)\n"; system "make -C $portdir install clean" and do { warn "'make install clean' failed for port $app\n"; ## no critic (Carp) }; }; return; } sub install_app_freebsd_pkg { my ( $info, $app ) = @_; my $pkg = get_freebsd_pkgng() or return; my $name = $info->{port} || $app; print "installing $name\n"; system "$pkg install -y $name"; return 1 if is_freebsd_port_installed($name); return 0 if ($app eq $name); system "$pkg install -y $app"; return 1 if is_freebsd_port_installed($app); return 0; } sub install_app_linux { my ($app, $info ) = @_; if ( -x '/usr/bin/yum' ) { my $rpm = $info->{yum} || $app; system "/usr/bin/yum -y install $rpm"; } elsif ( -x '/usr/bin/apt-get' ) { my $package = lc($info->{apt} || $app); system "/usr/bin/apt-get -y install $package"; } else { warn "no Linux package manager detected\n"; ## no critic (Carp) }; return; } sub install_module { my ($module, $info, $version) = @_; if ( lc($OSNAME) eq 'darwin' ) { install_module_darwin($module, $info, $version); } elsif ( lc($OSNAME) eq 'freebsd' ) { install_module_freebsd($module, $info, $version); } elsif ( lc($OSNAME) eq 'linux' ) { install_module_linux( $module, $info, $version); }; eval "require $module" or print ''; ## no critic (Stringy) return 1 if ! $EVAL_ERROR; install_module_cpan($module, $version); return; } sub install_module_cpan { my ($module, $version) = @_; print " from CPAN..."; sleep 1; # this causes problems when CPAN is not configured. #local $ENV{PERL_MM_USE_DEFAULT} = 1; # supress CPAN prompts local $ENV{FTP_PASSIVE} = 1; # for FTP behind NAT/firewalls # some Linux distros break CPAN by auto/preconfiguring it with no URL mirrors. # this works around that annoying little habit $CPAN::Config = get_cpan_config(); ## no critic (PackageVars) # a hack to grab the latest version on CPAN before its hits the mirrors if ( $module eq 'Provision::Unix' && $version ) { $module =~ s/\:\:/\-/g; $module = "M/MS/MSIMERSON/$module-$version.tar.gz"; } CPAN::Shell->install($module); ## no critic (PackageVars) return; } sub install_module_darwin { my ($module, $info, $version) = @_; my $dport = '/opt/local/bin/port'; if ( ! -x $dport ) { print "MacPorts is not installed! Consider installing it.\n"; return; } my $port = "p5-$module"; $port =~ s/::/-/g; system "$dport install $port" and warn "install failed for Darwin port $module"; ## no critic (Carp) return; } sub install_module_freebsd { my ($module, $info, $version) = @_; my $name = $info->{port} || $module; my $portname = substr($name, 0, 3) eq 'p5-' ? $name : "p5-$name"; $portname =~ s/::/-/g; if (is_freebsd_port_installed($portname)) { return print "$module is installed.\n"; }; return 1 if install_module_freebsd_pkg($portname); return install_module_freebsd_port($portname, $info, $module); } sub install_module_freebsd_port { my ($portname, $info, $module) = @_; print " from ports...$portname..."; if (is_freebsd_port_installed($module, $portname)) { return print "$module is installed.\n"; } print "installing $module ..."; my $category = $info->{category} || '*'; my ($portdir) = glob "/usr/ports/$category/$portname"; if ( ! $portdir || ! -d $portdir ) { print "no match at /usr/ports/$category/$portname\n"; return; }; print " from ports ($portdir)\n"; system "make -C $portdir install clean" and warn "'make install clean' failed for port $module\n"; ## no critic (Carp) return; } sub install_module_freebsd_pkg { my ( $module ) = @_; my $pkg = get_freebsd_pkgng() or return 0; print "installing $module\n"; system "$pkg install -y $module"; return is_freebsd_port_installed($module); } sub is_freebsd_port_installed { my ( $module, $portname ) = @_; my $pkg = get_freebsd_pkgng(); if ($pkg) { return 1 if `$pkg info -x $module`; ## no critic (Backtick) } my $pkg_info = get_freebsd_pkgng(); if ($pkg_info) { $portname ||= $module; if ( `$pkg_info | /usr/bin/grep $portname` ) { ## no critic (Backtick) return print "$module is installed.\n"; } } return 0; } sub get_freebsd_pkg_info { if ( -x '/usr/sbin/pkg_info' ) { return '/usr/sbin/pkg_info'; }; return; } sub get_freebsd_pkgng { my $pkg = '/usr/local/sbin/pkg'; # port version is likely newest if (! -x $pkg) { $pkg = '/usr/sbin/pkg'; }; # fall back if (! -x $pkg) { warn "pkg not installed!\n"; return 0; } return $pkg; } sub install_module_linux { my ($module, $info, $version) = @_; my $package; if ( -x '/usr/bin/yum' ) { ## no critic (Backtick) return install_module_linux_yum($module, $info); } elsif ( -x '/usr/bin/apt-get' ) { ## no critic (Backtick) return install_module_linux_apt($module, $info); } warn "no Linux package manager detected\n"; ## no critic (Carp) return; } sub install_module_linux_yum { my ($module, $info) = @_; my $package; if ( $info->{yum} ) { $package = $info->{yum}; } else { $package = "perl-$module"; $package =~ s/::/-/g; }; system "/usr/bin/yum -y install $package"; return; } sub install_module_linux_apt { my ($module, $info) = @_; my $package; if ( $info->{apt} ) { $package = $info->{apt}; } else { $package = 'lib' . lc $module . '-perl'; $package =~ s/::/-/g; }; system "/usr/bin/apt-get -y install $package"; return; } sub get_cpan_config { my $ftp = `which ftp`; chomp $ftp; ## no critic (Backtick) my $gzip = `which gzip`; chomp $gzip; ## no critic (Backtick) my $unzip = `which unzip`; chomp $unzip; ## no critic (Backtick) my $tar = `which tar`; chomp $tar; ## no critic (Backtick) my $make = `which make`; chomp $make; ## no critic (Backtick) my $wget = `which wget`; chomp $wget; ## no critic (Backtick) return { 'build_cache' => q[10], 'build_dir' => qq[$ENV{HOME}/.cpan/build], 'cache_metadata' => q[1], 'cpan_home' => qq[$ENV{HOME}/.cpan], 'ftp' => $ftp, 'ftp_proxy' => q[], 'getcwd' => q[cwd], 'gpg' => q[], 'gzip' => $gzip, 'histfile' => qq[$ENV{HOME}/.cpan/histfile], 'histsize' => q[100], 'http_proxy' => q[], 'inactivity_timeout' => q[5], 'index_expire' => q[1], 'inhibit_startup_message' => q[1], 'keep_source_where' => qq[$ENV{HOME}/.cpan/sources], 'lynx' => q[], 'make' => $make, 'make_arg' => q[], 'make_install_arg' => q[], 'makepl_arg' => q[], 'ncftp' => q[], 'ncftpget' => q[], 'no_proxy' => q[], 'pager' => q[less], 'prerequisites_policy' => q[follow], 'scan_cache' => q[atstart], 'shell' => q[/bin/csh], 'tar' => $tar, 'term_is_latin' => q[1], 'unzip' => $unzip, 'urllist' => [ 'http://www.perl.com/CPAN/', 'http://mirrors.kernel.org/pub/CPAN/', 'ftp://cpan.cs.utah.edu/pub/CPAN/', 'ftp://mirrors.kernel.org/pub/CPAN', 'ftp://osl.uoregon.edu/CPAN/', 'http://cpan.yahoo.com/', 'ftp://ftp.funet.fi/pub/languages/perl/CPAN/' ], 'wget' => $wget, }; } sub name_overrides { my $mod = shift; # Package and port managers have naming conventions for perl modules. The # methods will typically work out the name based on the module name and a # couple rules. When that doesn't work, add entries here for FreeBSD (port), # MacPorts ($dport), yum, and apt. my @modules = ( { module=>'LWP::UserAgent' , info => { cat=>'www', port=>'p5-libwww', dport=>'p5-libwww-perl', yum=>'perl-libwww-perl' }, }, { module=>'Mail::Send' , info => { port => 'Mail::Tools', } }, { module=>'Date::Parse' , info => { port => 'TimeDate', } }, { module=>'LWP' , info => { port => 'p5-libwww', } }, { module=>'Net::DNS::Resolver', info => { apt => 'libnet-dns-perl', } }, { module=>'DBD::SQLite' , info => { apt => 'libdbd-sqlite3-perl', } }, ); my ($match) = grep { $_->{module} eq $mod } @modules; return $match if $match; return { module=>$mod, info => { } }; } __END__ =pod =head1 NAME install_deps.pl: install dependencies with package manager or CPAN =head1 AUTHORS =over 4 =item * Matt Simerson =back =cut Mail-DMARC-1.20240314/example000755000765000024 014574361234 14515 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/example/report_cgi.png000444000765000024 134023714574361234 17607 0ustar00mattstaff000000000000PNG  IHDR# iCCPICC ProfileH Ww\S[@HB D@JM"]z l$$*ElRDĮ,o,(bʛKQo~sΜ;\4U S# c'$&)̸lodd8uWl [* |A6$6ykIe9{r^ qC)CmP5 h\L\a '@qσxLfLWClMf~Cc=q4;g̐| >eG7 m6 6īEN0_)揈s81kBU<$v?B3=-IĦl{'քU&fFEٹ#|^҈>J7 2A_cDq%ǂ>N$/(Gy3#!4p^81 BFxi}<ȃ)ĩI,C/sœ`e p @ n lK 3A,2H t `a>C~hV {{x8}`q]qƞ<7}.eD7CD6|Ϙc®֮H#&! ;N`#X#`cDZ& ;JFp!Cdp6Y?ɈeIM1lapQ67qQaE)cԆ}ps]'y9Y.Ìp }>[:'p99p-92P` #vwKhxSօ\V n$=6k'0}L^G;xrY'^$¯B` 3 D5,0Y`X A!X 6`/pA/@/x t b8 #QH" "G!KBd=RDj_ <҉B k4TGq+ꋆ14Tfy2t5Z{z*h0a+E`IX*&`XVap-^X'L™ cq/W%x5ހ¯^+N#ِIRIHE'*IHEzG&Yd \4\*Vr=I~DP(:'%¥P)[({())]J*JJJAJIJ%JEJ)]VzԯlWFBYrr?UjAPӨ:i]c7*bE**TΩ0RF2㥪t<"T{Ԕոj JPSg۫GgR߭~^E\#PLcIGLigK.M&G3MPsffx8ZZG,e2XkXYYF%rTݨˣkhhk_ IYӨsO׵֝;Kwiݞњ=FF?g7Wo^^~TIAFc݆LC/CFZl_v}kgb$7inolakĸ $dIIDy͔\DfΚ707_nhBۂcgQkqגnmeYnyՊljnժvYZ_Amm6[m:ǐƸ)sÖfkk[k`,kl%cǾg:.iܺqg}s˰cajľϡ#1qc6㷍tܩ鋳̹ι%٥冫k*sn$7?nG>;w#cdz *&<4zTxvx)}L|>>O}||{?߿% (h , d$ v B Yrqj8.CO¢J[›'C'nxw$ɤq/"2+ɑK'?u6=#zw51wb-c屭qqSjįW$Kp1Q7QؔDIKL8eӔNS^f1mugL?:CuwƁdRr|n9/Rm7S ="oQG/.J I۞>="*} #>>S)39DC.95`왝RiT垵)W&Fe7hCnRAWniYqV-6z9O~mg4o}\,HYкdᲅ]U/.N_%뗼]yESm>#_c++WnY_pЮ*ު ?\kl[K^+Y{}?0qCFƂo7th|͊-[n\"*VWZ_WV|m^;w6""/TVV~T)Oո۽vcoަ:ۺ}`|_?lub*h@46MMC6{4moUG:زcH[zNO12\hgP@"[4&1>utV C<٩}(| oRݷhj2 1686 1228 L@IDATx|'7RH$$^&Ҥ(E ED^bO*P(D  HG H A$! )!93;dh>sagϜ6ݝλytqxcQ(@ P(@ P(@ PHS(@ P(@ P(@ P@/^(@ P(@ P(@ P ,(@ P(@ P(@ P^%)@ P(@ P(@ P X2 P(@ P(@ P(@Kz S(@ P(@ P(@  0dH(@ P(@ P(@ Pzܧ(@ P(@ P(@ P0``ɐ(@ P(@ P(@ P ,5O P(@ P(@ P(`(! P(@ P(@ P(@ Xkp(@ P(@ P(@ PP%C(@ P(@ P(@ P 0>(@ P(@ P(@ PK4<@ P(@ P(@ P(``I} P(@ P(@ P(@C ix(@ P(@ P(@ P@/^(@ P(@ P(@ P ,(@ P(@ P(@ P^%)@ P(@ P(@ P X2 P(@ P(@ P(@Kz S(@ P(@ P(@  \RHO>F҉t[Y̹y0rkYTV}٪fi \yLԏT溔\3u_}+_rsMA~u;(@ P(@ P׿v/z"244vcn'D~OGөC0OyR/_'=?m3$lF7G+>hyI=$Pl~|qL_\R3L"'BM5ФM4 ɋrxI`Ң|Y{x;WY|8d~CB,$!=Q~U4l[mnjnl{Z`NE;tʖ6 YO݇[kWN+k[V?>+:]Dj165ȯ8w4ge©ʘ?W"NeT1asvs<> 񞕛瞓)s 瓮o }_eбC^l2Xzo& ^߂Ou޾[*6.]Vcx[_8Lrd Sw)wO`XqYnݚ`ڊOU|DkŒG-y`d$n=-FYRxrP/+v|k [> SA`Y{ĕS uV*q3Ra*ɭȬERCv],㮝A]Ϝ}[c%Jr67'1Y|#Z^= "2nh_X2yR#"TԏT*?(@ P(@ Pׄu*~ ډѽy5TZ{ElxoI%ӫh}q߃J%(@ P(@ Pdot F{INn֦oێm*Q.s Ş{nvZ",*RdCPLc<tWK1!Y]+B_n٥9[3I{-<",}'p"ٸ ڼ.8K8y.t2"yŦVz3$.F!AEpC px9͝\ 6[?Ǿ]V €! rWzsl[Sz#k'Wm]Sy[+Fց7rh[Wgzm$u8>rDӚC*h* CYgE~Eg񡋭~TBX]ud_~Tp+Fߔl|%4G(@ P(@ Pm@ҟCޛ m[U8? -kOLzԭ:o_,7T&fu zvtg&'7x/Jd [97xwi Ե0Jw,_y(&|:=#&+Xpv? cbꠎ{F/E{݌e7˾2,N b>u?pn&}_ƾ?z#?t[{Frf9e$5uh{ ^əCG{~w7N_}yݥ Ü5 ~Æmck`KDғO~ׯ+Z 4 @8̽TG`¢E S甎5o~_(}o,m^ OrípzeZ0}eY @':!,|?7ɔ}AѪ`YFk>/ȵkז2$UbM1_orT[tP {Bh},>Vak"RC4t5sA|Wf! Cv]0tP?rp*q@/bru3cB(_y۞Ș?ZO%x}&D~S@ΑϱM|d¢c1ƶ9e>|m,YuҚv+l]1H+^*|A[~y^{yU9y+>?>9Aj:m,EY+ [].vbAtDe4}dYK{K0uƈz&ӌ'򘫛nх|ۡN}rLCcbÅ[CV\=jߙ(@ P(@ PbF,ɦ䈖[&uBmG.Va>Kw>G+jڃ w]bllxu#aϙ"Ҧ7`lYxTܲmF{ZN+k%}+t֩bnFO|v++=\<:+ޚi9PGʼnM-Mo(˾j݇`iQV$eV`tU؉wك7:z0k1]i̬1 DOى?M{1B"Q<'Ȧjyq~ث'rDߐadVq\Z+}o,"`Hjnhv ǯ!h(@|<sswIoFjg== *)<Օ5Ы/l_mU,VS$CdM8em٭ka;0q8l9DL䉨q7}:7]GcּXp!ǖ!MO'B1wQ<=?j6Ţ"Z()F"8* zayX8oo$#b+_jdTrChTS퉵n5dߧŁ^B-F`ԉ$LVdZUsӟƸ0zk߯,H׻Csz:?n({LzVq'ձ1y9Z;]nAcPY}g(@ P(@ PW@ҵX x5L=XvS~EzngH~&Nѿo-Tiaum<==o[dֹ?i׏V|M—/"vFK{|阖'jc`ӊ]X:?OQ Ʌԧmo7m:)_ kk]40/w'FJU[mmCvh<6L R[+f OgH ÔCЯs{ľ򂈼|% %F{{!u'1 Dc{dZeQҭ' v~|?kK}tD0ZJw;_zNtV*Rr ra:h1?&xzM"c) wTu0gإUi_kcT1J+\e:]{Ik6aw* #G) wbK:RbѶ!h.ns]Ĺ443J׷jܥOj%xN#)#mA>ߊXq~b-O56Iơe!C7ڡj1-2 =݆ѐY/G}p Aϧ>N{1&:޷]&j(Atj׉~lrHrTV'#ӊG;G*uO\*:cg.pM=M|Nz TlY4WO Wƽzi.?~p ,](%-Ȩ%? X݌ [Nɮ^gw| mmW"nRkĄ=&Ӿ3W'F}w%Ёd툳\db HR,߃*c(@ P(@ PU!`Ƀw] Aޘ#BkSm_q^]ܸ/k\8=P 3CNW)ϯ-ұ ekrj-ruo/,CXpe#턁ڨKiF?R1%b`dysTO\_6q#fTr3zo< ,@*Τ\à0$Sˋs@k-^Hd Ԫ-n6W]5nR+G%(MqOTEW随WM"ȶMX6c|_ [ Vz3L/.ZtG|. kd/V g⿺Zqc1(jn9'Njo~/e҆)"zG{GO4zuGĉ~d{!ĚTlgP*G!#zx/ȭreˀ%"K"=[+(OW㔄$r@t)LI3-KOS>b*jW\RCLUk#F+ѼQ L^DZ5&_mbt^qtͰdŒ\g/S!PBf܊ՕT^1;YL O`ZMYA}ܧ(@ P(@ PX:jt;haoMNA ]t0X;ej^Uy<&7[hhhey}bt?[ dJ=DW|(.gISD)[Nߧ7mlp!WAb>R(@ P(@ \mE "!w@Z1m1A7j|L|T䩍 L;_t NP)+l'& 6 N6.o>?znEX}u;Sny~We|#G]韜)yфӾ,}:L.&gYV";Eg"+<ݩޤl_^ō3,ӭ'E|ݯ.Om++߈qqE=&=M}ooT1j*ն*rQOJgkB㑝lOoD,F ge#,vd9Rbڅ$em0\LGُp-Ί[ح< l_ޚNq*S{O[/Z݃[ιx5ᰒirt%z.C)ӾumkYϚ}PA(R> R(@ P(@ \]%wuWWniNhd$FL"gҮjH_u%,)6ÔZiI9mIQ7=c|-b_ݢ-?S/Wz&P {pm$ .IL8,m',]w{}0KLi=G?u]A|zqwFԼ{*m1,%%սɿZ@=X\JҞvuX=Qi(~bfhQqpCj|v!:sT!J`*z5 U$F2{=FaKw+u$k+WWm;qr,{ Ԯg]o}"M7Ab.i9C_mEZWFBlGYri0VAYԯ;?ڹC P(@ P(p%#wQIn:Їl7@Y\Fnbj $.!>冬{WF$j;1vRGK`Ϭk7lF,DQLk;U`ʛL_?X77b|N'YOT,HۃG5T+N?<3X&8ꎸƑ{bңӰp&#™X^ oƍ#|WJ 0N)uQ-:#FIy.|:ϟ\=c CcgnǓX'3W'~Ŭ7b>G;Ot~Xe3qܱ6B6uC)A߲A{P~ Ϻa] o ;(@ P(@ PW54EڣLwy5X5YG`FۣYt!9(2V+`z#?t}ʹSAD%Z`i"PV8'^k p G%00T ,OW&wnS9S2f)?EX%o;KʔyiubI/BĐ7;M ,};bΦfm8B ߷p&xBd ޷z5g=qgOxןBfYd=2s:I*ֻ2;瓁f-d[!Kz]:SdkyG{?-{z&F[Gܴtdd Re_Tװ¯Ir:#cYLZAı޲ k胐0"9rQTDl)߷گ2'6 7[h? 8JU#J"ֽ2ȒSg.rd1FQ+ki^eeΔ5IQs=jXl;8Q⯸-H_ֺҎǣw"]+X(@ P(@ P@j(YI@9 |]|]u''_#`lR;(}BRT,9b&4W\ōg.B]e.*;mv(@ P(@ \s@)̟4b#eտF>)^N]_m_<;r?N.(@ P(@ P@t%Om_+HܕlOwM|dQy$ VΈ?vTNׄ,o x:g?N<۴1 ֈ+X (@ P(@ P(P*IK|,p Xp#L@ Py…-lRS-PY(@ P(@ P0NWa(@ P(@ P(@ \_ ,]_'φ(@ P(@ P(@ TKFˊ)@ P(@ P(@ P%zl(@ P(@ P(@ P@ 0Ta(@ P(@ P(@ \_ ,]_'φ(@ P(@ P(@ TKFˊ)@ P(@ P(@ P%zl(@ P(@ P(@ P@ 0Ta(@ P(@ P(@ \_ ,]_'φ(@ P(@ P(@ TKFˊ)@ P(@ P(@ P%zl(@ P(@ P(@ P@ 0Ta(@ P(@ P(@ \_ ,]_'φ(@ P(@ P(@ TKFˊ)@ P(@ P(@ P%zl(@ P(@ P(@ P@ 0Ta(@ P(@ P(@ \_ ,]_'φ(@ P(@ P(@ TKFˊ)@ P(@ P(@ P%zl(@ P(@ P(@ P@ xRdBNnrss<Ry(@ P(@ P(@ PI ^^^V|}YN_M%s(&(@ P(@ P(@ PP9G5xѲѰ̬LҥKpT ~" $:_K(@ P(@ P(@ P@ \G~^.LsYBYYY C@vi`)B:Ν;4닠*ȇ;" f?r*<7 P(@ P(@ P(P24Txz8s #HUVɱAPWJJP1/S(@ P(@ P(@ \2Ș!멨.Ɍg*m[ /%UQ=`(@ P(@ P(@ P1ˑ1ۑȘOElvS?||*#̐REN P(@ P(@ P(P"$c;99ɘOxXxy6ed7+;UŚJ2ō(@ P(@ P(@ P㑱 OyoK w\*``Y(@ P(@ P(@ P(1+,%bڴ.^Trs]bPY(@ P(@ P(@ PN9,F/])_/!W6JQ Ͻd ,i%cK)@ P(@ P(@ P@tw2t:=R2}Or2|dInSR-S>*^ :O N(@ P(@ P(@ PJ&g\J"W69IN'fS6Rk-Wviͥ0+<(@ P(@ P(@ PNt&=fl$*,QdEh'/w1[%e%/צ+:\8TT59'0{>95n@(T,X)-оis2~Yf-C*8'?ISy, Y Dûasu?&`ہcUV7mjE=+~9i(@ P(@ P(@ ).=˵5g]w^,"TyhsH˷5 ̤''L9]{>܊ZRaC0El[4:?Gi-_K(@ P(@ P(@ T@CFvd6YB'{5*YHxR!x|iֱsYsu>$d`5㘌(c#qؙfKQ2楾IPJl4X^.2_Hkݲx k\u P(@ P(@ P^LbMt@RA&M:V5+r_m.Y[q%#`*p8V1gB}΄ pxo6F]F9ruh'1N+;l$:VpuaGZr:ȡ0ӱ~ZWۉnSeuy*]')#棦HOKƉūP l_z=Q&Fm9*߁A-_'S(@ P(@ P(@I לs8'+"뉆5BP5P#8crKنQrX ,ш:U=Z$a32p0tmwC}KZ@G+4􃇒6tY wv'@ ,pVa@TMԮ!VE@:pSM yKkM;ؖ?3xG`;k~eAa?곒KWgnl'8o@VئмDҏÑ :"̷׉@]X$ǵhڵ754XKvl7K~;0[ǡykn5[M& o <ҰsCv;Dߑ߻~bZ/4_*';7oFF8g/V@dd-mAEuMp(@ P(@ P(PbK -CyDrT *E~_$7~AF[z |YS-۸[RQk,9+51 -oqðzföda_(%{XJa2 g*_vlpfl^2_'J#;_~l{ku^LKrUbXԇ~ֶm vG;T'ڔ;?G>uѸx[tL|`rᅁxѱG3边-}xAX[KJkKy.̞4-ζf<5Ϟaq-y Uu0{S<[:[wF~tؚcV>(@ P(@ P(@ X@ows߾ Tj"i'"%\ӹ H۩g%[c՞45˽?vGVۖoR RA%y@ XJ{8r|EԨMk)ظ6ZI&5oZZCm}ӫ6W@IDATv#I %=A%y|{c'u҆ɠ_J>iCv<~cP)2hvkA#yaJxǵ|A0F P(@ P(@ PrDRU1:I_SI4̫?~e{oߺm%@/F:]DT磝CrPEG11r5fY({v%zK F<ueyǥIUXal#X>_vd78NNG>|4|E V7=nu|l6?Tg%w7=/EI|zw|lUJL9pF'x&NcԯpSbX>6|B7: ko}]"/߃i5vw(#_CEq Ұ=]^X 6Oj)XL1O'Y+<ÛY)=Pq]|B P(@ P(@ PFʚJH%1j(_\{2ri%Y֨0/XڨfX`Tx Zb|?[-z4Vu>ƾIHF/@/QJJְɻ#0^#bfZ˞^~%[ …cqTiȧ&č8%Kݟ_6Rު?6X$Z^,^v֚ !ʠ܂wsYw|X%݁fNg wD2!ߖ:"D/^ZPO؆u{Vz'(@ P(@ P(@$k /].Bs3k2]YyVt l&B˨{ GYPZR?Zh]C_=wkS;| 4t2A8EĈ}޼}@*k+X=}[+r֯9|R,ΈpfēYv@R& P۵(vu1pLslX[o\Č18,.tŚB%o{"RsĿj@xx&᳘:],je =ı5g`~xs*ݞ @́ݶ;{wړ?inD`{4A#lmۏ{A_Ӗ!\îzgvH3cяh4+ȟq'x(@ P(@ P2TYD`b9eʠ[q%ц8i)زd̊;FF#?$k=߈tAQL=hMK:Ȩ'^#F">%6c/A~h[ ꃕڱ1u~g{MJ{Tw<X40wk]^g*GRԴ+$-+7o"hs3E-I?Ep4t3d<$WAW ,a;H_Fmǂ׵%Xku&i$gG"-?7 FzhJ?LѷoԆ(@ P(@ P(@ P D`r yEnن] -MQPCɪ7k0A)uwb @LQ7}̰e-l3d0D[Wkz{2ak^ `V>hO7kچ ZJ{X8ӕ_4, !&9*؆Q 3%]@=%jevmVcĻQKv;/~_?Oަ2q[zƢi,yvuZ@@U FkՂg1}}0z u}ɶ(ɇ[w/(^*: @TΈ sBоM]= s`i9(@ P(@ P(@ PP2uɜl%/]*U "N'%ƕ :6gmd OVjZ!1E 5 cf`مp@4phd~2ZbL@"I[^О~Ĩ#r'ͣtnT#_M#n+N_4ym8aiOE[+ Kgߚ,&3_Xd[Vm/lZ.xx:2h'7q9<7v.#>}Vpss3PigXkr[ϽbbT҇hW?)5nKoƫOYc͠i{&wS_On^3l(TjQX?<]"zaZt}.Θog|oPZ xz ZßsQGiiR&Eo44]>(@ P(@ P(@ P\d Cje_% .CFޞTͰMyL#dYVd]2M-۸ۼyO_:x8ƏY{HԕuvSNa_BBBQ%, Ʊ;ws&Μ>!r>>vK&iwv[9xEgfl6#8U Qޛ~:YreT.Lܥ(@ P(@ P(@ PdKK%+yf`}m.sp)Ӷ6Qe(@ P(@ P(@ P``~}.k##i|e (@ P(@ P(@ P(X*0:{D P(@ P(@ P(p9*](@ P(@ P(@ P+ҵڱ(@ P(@ P(@ P 0tY(@ P(@ P(@ PvXv_;(@ P(@ P(@ \V.+7(@ P(@ P(@ P׮cwpLs P(@ P(@ P(@kH,G,U+k(@ P(@ P(@ Pם@KcBv$U~"У$Y{_{9Kבd=T^WN7}oxy|=@Gj<瞭8byL(@ P(@ P(@ P@ 0T(@ P(@ P(@ \? ,]?%τ(@ P(@ P(@ TK)@ P(@ P(@ P#ZL(@ P(@ P(@ P@ 0T(@ P(@ P(@ \? ,]?%τ(@ P(@ P(@ TG-v_H nMA|A(@ P(@ Pi̳30]J۲kS(Fba&s>pPQq~:2-r'KAs8^ |y|_~N9.ɧ$^ jJ2'gZ z;ŸqSQ:m@FY+aU"hM(@ K>#Zq[Xt ^M;d>N CvϜ~LUEDe _H \>סM6U3NC{jƷ[IBb?)+A/0(wF@3|Tf{yv Wвy `k0!mGؾ+tP_\0Bbz \+N&5/ʐ&~XڷKxW*݇^.\3edbX^fU'>_n‡I;_DZY!:foFAW/ _'ߎ 'qhokE(PŸ_gEUݝKȼouR(kYesҽ,uYr7lpHP$b B?cX;Hd0'$HJ[-E mjK[/վhKU.}UUڢTߗZ(j_"$.޹,Lf"3y̹9߳ssulЯ!bɸ vӚ8L>38l_ ղ2wз4(Tq*,k)%T*"R~ d\ᗯ't L]Uje/JuS֥sذf;>Zv[lp2 T08LE ӟeT D`CyLvQ N~p;NZOhN]#2DX9| {ڣ~g@AʱT@ԇ ' m)Bu>x|hu(?ϠWLrM$Q@1~1pSrѯ#)εṴR)'׮Zvh93_倀_-L8?Sw_gbփƉA`fP +.wl)vr D[ܤوH~@YCs.JR{aL "Yq5Lm)mu%_~+5t߈X. ҅tn.aʯ'0oPcfLo *wG5Jgp0 GRR~xhl?I+ clUw,vs 1ޯLd"3bSPڋW.م]JK.Ieac})n|Pnu`'>3JRIX$(i < D֪Fwx= xXQ,_}'qj>TAppD6Dp;ˡ qi$$!ʖ/@t8-Xa.R/f7Z~(-L$GN3A:S+.jy_$cgJUHjB[#ɾJ셫*=fObȠPLGNSȾ$!:V.55] Zj6/a Y Lܤ)DU8<}TNdq3`t:;eÁ 8GU}6XKS3:lJ+E_r9O6mӹ2ްSc"QsO:?eL.ًeџ ӟj!(4&#`:,SKfgZ?' DZ76-voWiݭ=/ Ķ7:-vQŤx,jSΦttR \t'RRqudR+W I}"gxL b-F4鿫#N'ڟLϸaV,QD*R\fG New{ڷ3ݜopk`ŵ4/qЦE4}f&JDN0U.mҨzvoE/A޼G\lQ"HLEDgs{t,'}|0?j[)H#{.<\A6OO(ޕطn6,[iV `n$`}S=? Wl2HniNh/pi);`af“FR)(RTW*_刀8_6m {md J%pGϑk0ƎRIذnXóekJ%9$_]T'2(t8+/gRIv| gӅ+ۚ=L 1/# _ D+)' u,^DVkX(jZa+X*g𯞲T*I`T*Ivoߋp({pIބA-lGa] y<'RIIa{G Wimɔ"8J,e֑BgaO$UE.ۈ5U[(ԔSL=)E{ md ,ڵ#_ 8Ilx;-.|Ra!`.'W[ifQP;:.dZbIKu3)iF;>mZ!^%"\F:mg*\Bʕ ^YLM[&VKxgff\F|0:|'-GZw ]p^߬/;[+wE!|9 ϣn<[Ҵ;nASxovIlc Z*gmǣU :1#l h#V)h,a 3F?6T%/&h)Wh`fSeUK0U?;ĥU L<)۵푰{ lCa=xz.Xm6H<8_=_$m95֝} <@UakQ#Mk-}/f7mճq5DbѣmllX\T 0"Wg?`Deգ 6.ׯ5aP&6 qҦJJ%] }ӿk'ƕ*UB_EͻqXJ[/JwP뱽Ǖ!&IJ%A__mW$hZ 02 _-i9.gFzoB?@T(1[}+ZXi] nv[1nBQRe7=!;Oa{R*Ng5gYU0GEfwL<H+E=C P^C\RO0MR* l}SgD$5mP;~rxf!:֕b~ [`"vW.E˟-2%/ȼu#ЬArE pK/f?_@ [iE2Q[aڭOnjaP)|l=mݞ5~~ b*αpd ;j3 mc?2&:JZfkj5k+(ȊBu iyUWm)o[mI `eI ~rH uXy8tB](؅R֮&]!:ɽ[!լɢ_c-\1<gj&G>i&l~8maLl`F 8_=و6R[ w+eB1gy1ZK[Ƿ(fN1~맊TCōc{&P~ -Vr]bA5": @ùܴ4u0^0ùPٗGQwkZH~Vmڵ# 5 []7G3r@Y0"U\Z7 (T~@J#^5@ TEZ֬z3_뾨`hlİ zlM$K'i.:ka˥vW˲/c2F D+z[L }cQ%+`jH[z-=Ued]>B>.38˘EtV)(win>h۽+YqyyՓ²j\X&m*0bwomϞ̆Cxp \/:?"YafA|LAܪ]V/I E_xð[yݑB"ԩ[p GN_m1i#yΙ]ۣ FcAҷ-V-mܧ 􋖾~?łMh=BUI$W 6m q=*VjE:np'gB/IUrhZw~q2L][ёgЬLe .˹ݦXltv0*dp\׬WOǚe+~tV:&}rP@\SB4,d?^wtƩ=ߗF]EXv;ґ'V~UXm2Tk 3 FbTK iFD BiИϦec38m& UҶCaMs5[Z?K~م {Ҍ_C-Pʨ]1(7膄UT-*Tc@nFe_wuJVU?f{r /X[ nhldi D p;0PH|M;`]0MuolQmGp0ui'2jVgv3ɖztth`؍s5׹1~<`X?:^l+4a?:y4a4Gx>yd+,d.8Άw]j^"&iv,k6[aڵX!N;1_!(h/s2?C-㯍(JtQ.]†=;agQ6I`㳽v⑳gXHOkGz)x4aJb/|z.}![c>y!`S505+j}Ga{\07 T괱X& 5 0+T(p 6" )O IӍJ;7mk%]?QllQ&WS+Sm01=k"]fyQz62f"uЩzRG'#RgP2[V#ͣ mlhME[Mό,LZ~i:Ygi;:"O-FpC[vĻ~=Nq0ϐ&\8 'п ê#~(ԫzst#=%V6@eP)E^=iIv>ahBJƏ\cݶ_ / uE}[SO]!_vkW&YwDj^nؗ@? %iF&a Ȋ j;bm5m3 _ecv 6[I.[Uhd\;޷xwZymZ,i$˯倀_Lzܡ$ f!NGVXT:]{`OLn%`cp#Js|S71SsR̳'[k_iWW,$ab/)j/tfN4Upy&mw!- g+ʥg7îxaa>.]ͤ* IY0/ ap:jA$Yq!ARx^|(%4\._χ/(@e,YTEviqSz]v% ]k p;f@aGi'oBBS]/v{k8CgV)9(A:5Qk3Q5231hbAD.̹N i|}`QH}8ꯝKP_'ՕaO `{h=)폽y3.37?z7pu7@2y3ϝ;@Az5,"ė3QQh#{Z}SD΍7Ow)N!J 3i4ѶF]R|h.8~ҌY9UK",HR+A6/ 2ʥ0Qzf6_íJU5NM.}VB5L[1&P >j{Z&u²W&Fju[x&l1-U]'74!Sݛr J2م"\_CwaJ@#P=)OU$_ՊdNz j^)iO"l+ Œ-E'R]Z%>2s,aֺך6}l.anT&e%⹱ p#q qUH|Z:~c5rLpWv{dL 0&`L 0&`L 0&P,W,y&$*U$9&Pq p;RqS\EWp`fܮrIW\4r92@yEnBkXrSDelEYr(W()v-܎"L 8JGI;&c]ul\=;Xzp9wb@S,;YZx;&JNۑ3cL p;wL 0vٿ'r 2dL(W,2VID_VF8` p;b?eLx܎ψ]0&JBՒbJ˹]\KB2MgKŒhFQR 0[Ep#bwL 0p'v{vr'v<@Eϕ=!3XF&`L 0&`L 0&`L 0OW,<( *L`r$Lvf<' #.A1&NC@IDAT\ *.!9\ι 0!PQ곥bKKvsYn0䀘@%HzN8pnG\bL ]Pp9ire xR}?>Z wh򭒗Լ%)y {fLp;ŀ 0v?`L@OU=N\ν3_9Uz\< x2R}74'-UB'"CIP) ̣;`v `%Hi &z|{gryd>W7N=qkoF5O7 0&`L 0&`L 0&`@j"Q]ʢ)K. cL 0&`L 0&`L 0&j{Z褝>uJ?aCNg"0Kۑr&*܎$0vKBE "29&=QrڼbBiNWru`L 0&`L 0&`L 0&7XM0&`L 0&`L 0&`L,RYDsK7= X&`L 0&`L 0&`L x^R2&`L 0&`L 0&`@Qyubu,9$&`L 0&`7@Aeo]pR R3LUѲ=cGtk,u;EWU'1vxW\?uwm_v翧a`cq\ε$YF 0 mw>{)>>!۴MK\`y+0]Yy7CyyU2'bշƲ'-v).#[ACp1˾yӌ\έsJoIxؖ 0&\DҜ9/8|ƍ{G 2aC}s9}WUHr8Lv= #"0&춫90kgK0.r0mH,bð? RRpn#zsmKy1F1䛬sp׈yo`,Dh:VXC6x+w d=V#bypxɪ%g=3ۑ'\KZΝicJN[oH+`Ns'Cp)ŀG5vi|ƒKqr`L 0&`L 0&n"H7g|Z}K4x˹Eyo9jc,`L R$ 0&`L 0&p@pSLl:"o0ꢨfyyeL _.E+Qn^9ͷm_oo.ބ?6Yfao?Z:οAOe]6og']͖uªԄ&fHMQ@%tΗQ\$T}6ČѴ)QEғ]v4yF&.熌r.$ԙ6o`eFruwE^2Q1&`L 0&p/:AT}` dU^i-KIXً*x |Zc~0ZUn*JJ!пk-rbiko=+)B:`\xLM5W*ɎZc.:Jgr\w[ͩtb3`L +\dL 0&`L 0&P\: ֛5 |g/Kq0lvߢX"_RGq|:) nExHvqes·;A7'ېY T&N{Q9/Ñ0&3X 5`L 0&`L\?E)f\Mej /[Ik&:HrF [ ] _9U q"W< ~zQ^(?6`CHI˵ ֙G"i&rgʍ˹rSX&0|]=`L 0&`L 0&r/ǸtNxuC\s3nZB0BI#=OZ%?9ɘL\ )6#)Y !r0fϛҞiT)7 5Ed.WR}٭ O2`kȶ< &BYSr||%!uGFw%is_ \νpL 0 G,W, qL 0&`L 0&n:s$9Lxy;ш9AHcԸ@`p6{Oi;gM+7cWJB@pbsAj}Uo錢`?XX/ J6C(еe;-eW۶sv\:3X|^ pnZ.9rB1&%䀘`L 0&`L /Yc倧/6>L}⧦ɺŁ| rkK1YA- QC+HƜ7JAnN݋Ic{"RNqFjA6 )|{.Qvkwfm揞ΛffeUʚ9S0հ}(S.EIt,m>\))z=j%E Pyfs9r^N(qCb=x=4,6`L 0&`L 0/% HJ] | e8z$jaovh!)aJ>h`/"0R 1+–M;F~Rcɑ-B?e m;I~穎+Fw12&Qh/%PlI@]z >YR7EC@#oj{)]He|Seul=FhJ*S)hś0ߵ HB!䖯1% 6TP9zMc|O{Ռi-` )SœE;u%!Kc02Lhf{Z& iT%KY:/ 8IibIzl`%L:>&kO>I? }v[c7R ad%Cݙ\䤝`DSR6dzx">VwӟU]XOݼ@ԝO. ?6ӌ%zIl?цCrN^g≭#]R*N&^C8Ju2yŒ$)VX`^CY8" h?v&6!'$ܷF:B҂% ~wF{N_]NWlъHK1pbm n]G0 S3-c/,-edO#P9WQ񻦎T 끯>@i)y)'[ơ}KsxYSyecd>K,~LznAldߣ%x=KvdNA-cP;eE0|:#Ũ[&4m[7$.I.Hf4+dffi|,dhkAI-r~O&tA{ sLr.ԙ6ƒ7ŤϦ#򶎺s^ cT7ѤW'FiSF=MцoB/ .(9R7y|i$zAʺl#.60Ai a|_}{s2τaw GaxN_[KDoa, FϠn6:?=2-\YX۾Jo,XwBu9`,$;!n|Sq삒:;y4c^Vu!7- I:}R"ah=Ki.aPAC+ˡā=8rU Avmm^4QzdQB(]p.-K,!0 oB3zD}SQ:0c:d;({wYX=gtGu֥օŻ_\NѴHiaB;ぱ1S'6o kM3}E\spdnFL*BP&. rumI)RIU^XmJK[GD/Y2yZhUt8LqFMɋXʡ11|ՄF `[rI"hQ| iMcj%֛7 =}g/` I_Akc 3πJ_\i1 m#;u';AQiW0{VIӱ "[U IQSu6eZR}4O?_F/X FGƽOa]LMmۥlTasτtݔp.~%oTa”`ZѰjT2uC&8-S`LS믢|ڜg/аmTy  *F@EXLiP}lZ]kjC۾ۖx#CUULu!k:ˏ2Ԅ{N1H4Olz eg7!inq9w[V)fƴ[1>UCTQm\ qs:La 6t\6ҙ=aL Wp!Boз9Mq>K^dLn-R嫵Σ '_:ym (0L< l8 M>@7DGU;Y߂nWC %F7Q4KoS@b^M;n+B8mw70fd5OdbIPЬVJL޿ ^)j^tW#xV[}޸bg e YU,iY¯?QoGaDr/~֚sbq]#dyX@8 }'hzėQ\Ω(Mhpv>]8 aѣ1mw W81 \^OEclwhEhiֿB+oui9 0y#W'BƬ?br_[y\H= %=閔09tୃ?!Xc4(ll mPj󀝳3;e"ygw[~n5Svkwfm#?z :o[ua!MA ~a0Xf ٗ*jQ)oC4>IG gw•Ep?-9,/81P)~\8Nʓ4$ꫮ9_*o>\eZi5D8r47b}uB:G9 ʷ>ړh =uR^$4-i]IC ,;L͐K0mx! ł"u @ɍ#¿@[ZS{BB͍s=~; ȑQ~DR%QMSKm.ҵyՆ#4L(y)Zޣǰ![O?9XE\ wٖK73%eA֐QV X#hu}9_t֢x䍡i2 4W%M_&#s$5k)zfuu=,`L 8N n2\aE8szAV7KCH+b=Rg-^>[nЁ&>yK;,p bO昼l$S^q+꯺VR UYOӄs|a0el,KYlf0ӽc7;syzc&`LvWJt.__B <7Q ]q8i"RhG06HCv6HO&ɜ-MIF$kjtQuy凜ORRH7]>-tdbIq6_7(b)쌗+\ ?WV+<+5ᦙ+#^h[M떡ԠU&Ҕ5j/c/)f4={ Жi)ncc yƉeҠⅾRrQ_]!ݖ)Jð`3 0˛&e)q5Љoo<-ZfkwFb҅,t[ 묬EvLTw`×YN?-ߏ&ϗ*&<;9I:7s} 'W}&i !m:kfiH KY=Lvj? 2TR T_wJͶF3&ZiRBYiS)8~! ={;94h'e֎(⣒AqVUHIH?..,_?sJn,iEoR,)v#mbGXY$ʩ|f P4A"ጤ v"I-wBp+oQtjҥgпcHEI\Ҭ趏e\°ΆwtXaD m l'M )HU\%J1ӑTfVj<8rSu#%w;*J%0=Czr ']y׮y㝤Y`k=Y/B;ܞނ%lx,_&P|C"i*_#j,ȉ6)hnC1K2e]m9N_-)ljwGx9%`L 0&&Xye hd{嚫Khwɯ,$l0OmH?NjuiPd.cҪDvn.Ô&眧c1UCΩDw7UlW-h5%3N3k{A=W+(b)Je @T J$yeѝW<]9igp핲y>}wdeBl8MMrEpb_kcҀs:yS1%!K1`H TW$ iRg 0dXANRl @-HEyv'%IZE%f WUs(m9_~ɼs0d<)Dl=*$298o ?X?v%~zw&QUgi,LH!$`D4AƚVpҺU,j)_pjAQB "bAׄD$ c]̝<{|s=9ϙiЉ*-ڌA+8p2%'x!s7$ލFyD[jAa@.O΢QMܷ˓!G_&j7-Kf~뚤$釟qu'ϯɷ淪l&翌iqSO=>~ծw804ӕkKc5&h~ *=Q|wDMO.:7G>gB޴g99ƴ2||fiso~F-yflJ(.|s4%(A~϶'yvvKo0toC,us@oh<nTY=߱})v9j| ϩSֱsUP8]S k6}JI@ ~(2iס Gp^j<eÇ'#B;tsLjjmg;iqGeu3< Kх,JD>UI9ghVx߮X݋b>ͻo{1uVZZt*ˇ@[c9!In9,nBa-x kM8ǺqC NWbI{6@/{?Ś#9#+W.C7٤x,v ss>@]%ݎt=CԾOk9.Q9'+Ofb XGL%%:9Ƃӆo_ n8gb޽Y$;YJlhۡɸ-%BG|H6_%[;w*>{rgTbrwؗ#ko|h Av >*oŁoq≯G${nY>}X{øi6OXy>r\8u7aŀpZT#+2\(56LϜ{7O7Z856xEFP~?|dsM:u7G͈u}}+֖Qk0w"?Tga&IYޮw#rNG[v=mgz艹xS3 )jw,ӁWs,dMGk3E.QDc Pi~[Ժ <}."x^W"T,{}Wц>,}||YB&͔֋~mXP|x[oQGo^I2FXyC 7ιwhbΝ{eHc=wr(g#4! )߱X8iжm%}oqu;@y;&>}8$ՍƒnP'Ju'X FHyڒI)peeEIdl۾3YDhj?'2LKܟ}4һub21t]0&j=7cupOO3O(IU=ݞ@Z. pEKR@6R)j8|],DVTMs:$[z6SJ2=_A{r:Fk;^^kEKVW{?>h@EqRR&$Zc[3Rk/=uF{^zoj]'>&`K jh[[R Mbtv6gZÞ)>~kՖ !G,y9 ũj>: V,~s} ŸMtR [3Ͱwk&ZF?cp| l.0&e$ W<.ZQ>o>v}.Q-݊,_VIUVT]ƴkijLg%S;pwkoW0϶V 4yf|Cι{ddtUF{Sj;?iAHPOֹuP8u .4\Ds} cܟY/[ϟ5BshhoI Im~O&T9Lۂ $^t #:tޔ ĭ<)J10iWz+geO%%?=Ny o"N3#}Cc0lι,: Zsms!u%T]'# xu&]v|Md'qXHlYwV GAJ,#\iiƊ󸝵,W%X7#߼s<{.Jb_ +~ߓ[X5aR ߞFyf+\|&vS膵{1+CcMYd{zۄbitC۹0R7'.x/%XRlS44n"v4f%|G~K ɬﴶ3vX7Sx!HqR/cL 0&`L 0&:݂GpsQJɨ?>>"N2[S-_|cDyفOpX2N"ƨ?wdʭțr3Vt^ۏt>L8>&O1J(G^8(n"D1y-;C,3!`p;׽XāL 0&Гtb)J`N/0vL 0&`L 0&@ ;gOTə .HI`\KX/\Yױ)V)Φ65kҨ^2A/%{W_ b[4)56,{虤@r 'J۪e"%vڃG9IMA^`1 s.xe'g`]c/KVfL 0&`L 0& \Q]H3[RLj Q@9ggl gSB'ad\D0qTJ{ " } msCF”.L lKfYUmߦd ~93KFY*7ιw\䜙`=/#q+`L 0&`ݒCqRBW ⊝޸ɡSo@Jz|?69dI^s*j<򞴟Y{F:*9jCirz5@Q7x݅,o N9c\pH~<ܔ *u J!`p;v!-3eL HbGv4`L 0&`L t;W4toʵ6Z*f?޺ZᲇhLI\ɤ$qXE>/E<'\_xտpVzQ Pk82)ߟJXpg#7f?FeUt[e(%#p յgOM??r_=E 4R829{-rvL 0Jͩ==W 0&`L 0&@!Qd oj(Β&)Vߺ44,b^ߏjW#/ޞX޴OR[Y޿IXϷU');Өi}D2 MTYEyn ©ͭQ"b8n$eZJR6DZ,ݖ#zN9s`L 0;X§L 0&`L 0&*A$X:ꐕ,jTsv!1;pbZkށwˤIn$$<ַ u?1^XK Ȋ QCAE2OW% a]U%8k5a_'9jqQs Hq<'{\ AN`]^E 0&`L 0&@ {ZC&쨺n؋;2T F>b/3jT3~b51phS gdpӉHdP;8 }}G`'̻nBN ok 8=\G %۹~3{9=`L t~RG,!`L 0&`L 0ŋqk?綖;W:IƳؿ-j|ЎC<.ʙTIZOs/{Nij64!hmHWN5/_D*3 ?s-K*|fx۱Y#|x2"4aF&v9wPl`=Xa7`L 0&`ݛ=ozZٳY`;M{oËsJTH߼'F-nתw69Jp?q(tI8R)ga힥HӮK%);{yJΞ 0&c\}_\\s(VBBwݢ޺ [$9&s HϽ\s&-;-`L@"-,9 _z'"m`oQYUsjq"Z ]b`FX8rcJCO#(5*!0Zsƺ8u .4\Ds} cܟs~t[*sgd\s;wͧ]uw6Y&(stlõPUyR,~`/ dn8`L 0&`L 0&9\hu.0nƿ|Cc0l$#EF`5WzU^|ͻ<7Ν `L tsl ``L 0&`L 0&`L 0&EK"0&`L 0&`L 0&`Lp0.}`"0KGz3oo|`]p; w`]XjweL 0&`L 0&`L 0&e"b_|e;VU3 x.̣@ t; n87gLNwKX rNwKX &`<Π%K]솱L 0&`L 0&`L 0&\XtsL 0&`L 0&`L 0&bXn`L 0&`L 0&`L 0EK<`L 0&`L 0&`L 0.F2&`L 0&`.Xkp).E 3j)v  9"u(/l{Mb@p&\E9\"]4ǎ5iZjPRD2Ud C2`TVb4d!>ڰ ߞxPYqH\W褵2aKIΞ;/MDֵcqmz?cbIDN۹~ χ>*eL 0v#CN`L 0&`L \&-xe8S|Ֆ lY19?v]TJ2=>& ֬وfq tXj؍j_:Bl|4^x3buIfPeZpD zp2bLAhC[vaÞQ3nrOJZJayĴVqھb1_8KRY - Wt}fi8/Fqd")>1|b ,ێ7?Jo:ddC.]0۹M=χ$`L 0`ŒPrFL 0&`L 0&.3>{6b@k.9{2ƌm'QeɷC~Nxb'[M:Kl0~1HJOEXxfJ$[. 1gŜ1+QEћjOu5f94}>Sf-+D7wbnpPI q_#EL'gC஌>H K,^ڲ ''"B۹=χ$>eL 0/ҋyqVL 0&`L 0&e%t["N˜/*mQvJ&J%%]ʍS& ,+ 9YH0"(>J%%ϐ\ye~SeoH 5;V@wk4cSlJJ\$4IcOW Gnڻםy{-#3&w tdȻrnL 0&z0_ ҃WzSsZ) 0&Ѕ XjΠBOJ_t!dL:Dchpۃ\$kaGڷ,UG,CG:eJ.˂1^Zeѧk_y3N1X|bKyOi|ض|>cL \Z1 b|lU&Eʧ+4>pRoF`&aO&`5d4+̅a·[ZnFI>wQ*7c EՅ+{J;sט`ΥbL 0&`L 0&J|Cb=VW*ɜ%Zfyּtǧ' RI2|BL1N7GqTF.¡"Y)=^Xbl/"ŏ_Nd\vNtzB;cט`+.9r. t>u MȬ$pqv,58-1I 3&6/1R BC< cRuwrEVRX};Mm$@0'JV5hX׈V (1[FI&A b{ɹf"+rҾH!~DAHJǫJ*Ez&bǁYQ~{(|Eغ) PʩRF|d'Pup#6nMmr%Zb=j$(b|vqv V,qL 0&MTORV"%~RS`K5UWB2SɻPGxiv'c*6a )kl{zVM|(>zF"VP/FEo~&O%xcf@P*ÐFjjDmhrG’aҘ~Wx$gF zjGER|ATS HIR򒲩;Y*Rk-0ԺB2)R-R ^en*{aAeaozw.PGF{'9sx]ʩ|<?$Œ|i:3U үFl3t'>a)8I!.L||(P V6Uڝuֿ̠VR K~iScad_ϮyoYj&@w&@M/Cf`QN梏[(1%Ljr.tfzTϙ#P{/S^鈀"10E0&hƿ׬U)T Hf1!sl;jhA3Bc luL@dbbEƆJsf-D10 763wXLϔfn=i,j7\s b#,xm24Mkm6W/b4ܲVXh[rPثuasmy;;L]47IMcm:mWhEIHiC{KJU 0.c\=81 4T~O! u œ1!aJ h?HIT(Odu5dTBֈ K@t :}Imוn*21 iW+#Ȏoʓ'բ.T:FK]%`$ ETuƓ]NxO@ TʞL'ڈ4.)#L4v-8; Cf׈&^:qi{3:!H hU=>S!fW0o4>C:|^$g`ݟ@ H)e7kOxA(sA߅|{RW\lC]}j|LT2ݓ1)̹;v6 6RE{ezXztRpz{Q.P-ldϾ):?v'݇Lĩ:Y4ǽc@l -:%$d0:m&FEР SQb<"Ρ4 J .&L԰Bq⋕ד<mtN鐖7[zknks^ݩE#rjaXwmy>)u d\'۷J?4&0B5]Jpt♱|rGc'~ YLðֱqX 6 `Ŝ1+űѦZi Y7s 8'{9qbT:Cs %wTFBSC#KkAmu8107B@me JhS\ f{I$&UI\{%GN4>!}14]gPʮi976>쟂8NmMaʣ%֙FZP}'ϊ3}h^ j7^MbbG&ªd9~X Uw}sb>1&(&-5An@hƿ2zRbp>$-8~[T7Q%!!ሠX>\J?^Zy)@HTU)窉=a@ssZ( [}0XB NGY 㑙ޏWq~X)J+ ݵJońB=-dlLyxSɷgı$4!eh*"^ֲ Jߔ^Z۫`&8yEyñ77j<ȟjk7$SI_+9a2ԿѸzd0U1S9@T Ś+N:+AtZ'-2<J|3nfJҭ/I3H}҉:c8ceC Яxcbc]N8d:1ny +., >gcƢ}Z*̕FّS63{@2*ˠ lҳX|>M`OYu3{ 0&3.|oheeTj|nN4L3XT廰~o n{?i"c&ۃ=/UwerrZ@z{Jjs[ذSǂ5IVMcv,ԮP/E[U ( k$'*j Xrab3`<^.id..4×Fi+mczpE䇟enG@}kABٷO7N*aP+W㭕bYhՃcKdd85c@cRI(`pVՠH \XQ*EgFѴ9*>E-$JTRK,<z˶r/,"U$SɹGJ%]Y\*xHLL!Cl(Pa9F?/<|hJxI`_y5SyL&&.bw Z4MS B Y6ͻ]XO[hT1~]i[fRI$O7jJB}Gc4D jޫR}객d*1zR.W{,XB|RRVʀ0Dg'La iF/w4v魧pV9B/J:a&|},Z(=e{{jl}eUk_Bt|#V*>~U I rYITR$$CBJKf4?U`U*{.n2tn痡bu46~1Bc342U3v"hq<ńHeY\䫉-odV,_$͵cڂ=(=i-^s~y{Ѥ0sVRҨo"U$ssHflXg-(c'~3RĄȈ ߯~Pl_):coeyY(D٭.d9!R|6˨(/h!C_V*=[RqwZaK|ɌoRE[sč n硢Bl]k B25 IZkNLTWuH`@U:@)Dvl˫ $3 o^LjlQmiyiMoο>' FmDžO 4+qI(+ x+ua[14T3ifX0yLPԗT+q$^Ġqqd҆ʢק߯:Y%L|Ky[.8z v[_A9S}yL?_eL 0A" EskUj2+T MCfo=pՅ5JLA0l|ȠHnj+JeMh>OJXLBhac|V*?q~Kiur#߼R=!՗V\ͷ>@Fu3&x2KZ}7#%Riq77O`K4WVϾ|`L x@3FڗHXE<MA1,g+X"}qܔofM*_k,{i^"MM˛l8[~ƗbFo3g-Em*e5F݂c93V,5xX$wCu([\&k-Ư$_H2Z/iߓa{j.OXGlFɍ6< lSPA{NJ.&P"%ʗMEŧ oGwkSD'2 eCW bSgj.[XzSP3.k̴ٌ5?Ƒ#SKs &(`L thòR"#3;̷r>O4ah 痀9O'3vX]_֝PU* 0==. jүkqHPoIƜByZ^ Dc' CZ7X|1)5%8.g*B1*<]]+E$P_Y$dNal7XGnyX|vŲCґg:|u[V c: WȐ2HUw`FEF%.ЋSŒvMў{t˧}m<Ϲ翳΢GMʹS1s.&_aL 0@@1Ѥ,Ū-_z@|! ɹFoWQ2V.x^.)ᣝ\31dWR.t!64fƣw[WRi΅ 2OY!P3lur#jߪUw%22w71@ɃhMem|`L (:ȋ;k*lʗ#mԶ&IV= {d"G{X4Rz|^oc,ZX8mcϳip-?WFMbaeϫϾ̸fY4}cphV}gyR/mmӎI ִnҦ[gCco%JAݘb?L-M~ Pri>ߞVa5K@UDa8D;. Ҩ5D! RTⴙVQhCP|Ge9 aCj C"Bmţ* |hG_P;sH+}Y>H%0EL 0& H7>8J?JK.I=ep@&k3G*nShY *E:=z=KAVu$[q<h6nK05o1&`^'P{+ё8p=q {p{bG(gmt@2wǤxUWҌXuLل Df"y(8Ӓ7M]giՎ_QTdt;zbR,DݕV_&T .iǤOǏ12&Alñ7OœL^ob3x_4e;uh2m'w)cL 0& T6uCy [fu2#;Sx.GJIqi8Ϙ&TɁ1DfHz M5-Fz@IDATMʲk3pXz*B}hP4M܅]]w.h\ Iڰۺq6yf܎ss@=}̡67S?[F'*Cю쇞d2m 'Ÿ8u&n^XY+5&\imGAqK@8x|S\f64R0l$\KL+K '"01Gh;hFӹXu' цmLM'"5sѕq;wuծ֣s2!TjRe̒$Ҷ4u413!>jƔĚl s9ŌO__JF%2(&/OJBiд~jQѶucοYI8T2J'1V/a<|L1)2s-{g."w(;| o@r<{3$W`n1 jz ߩS1LW:-.`'1?}Qs*K)U?HMĞKG?LZElY19]g0?o~/S`RF4*`k0i3(圥cMΝvMLOg WRz1Sup36D܍A?{1SKxO+qA=M(ե[2z;1%FLwx iկ`joJ} r߉|H1X/L7tf̳ɞD96SJ6p0:0벼zMqTLMǓOK3Fژl`3W*,yFul1a#t!/:/*u2(]ۧr|4S6O򅰧%dỲT!Yy?T s&p0Av&ӟy3s?dU b\.mY2Ӧa?`L t-;44@ J9//S)O::QyxHU.}4e+G "ZSTIحJ8Ϟ֊?r\k ux ,\ iS4qB1"o>7 K9E d̜o%BF#wTR)ԿeM=v$Ky5yi]Ï߇i9־+Q&eVR-3#jvdYFrUe8U3='ZOzQyj*g^b2`႙HSJ?":#D1X/L95*F[c99r04c@.5(V_V*B2&4:n_\A]hߑ+ms&yNWGYKᄒ+N~>)ֲ_b=Ui@Qyn@Lxӥ0WUuF=:*XQ[{iøVZ^Q1 KqT,!4*~q? le55pA( FL\?Dz4Xyhee?pALl|Oh,hC=3a%ݔufP/Bc^$36Hfo ).K%3!- A/2=sn\ \Cuq"8]2$$D4ߣz7GK;@# 2t綸]=Y }Vzw_3)>>ߓq?ڜiI}}s-,~\lDzSfFԙ+q|C}X; qSf.@K)?e£2qhF8>ˌbGpZ3$Vmʷ-IsybfdmX4: ̑ u)x6h!nrsyM犻@sTqd{974!I1)񔙻ukiTah/[͗] HI70&ēbhUV 25KId& )7NAڦDw|$!u0~LIF4eY+J% #U$ ՠS/pzF8|"iZW9nB9W[>4&Ic2vi %-r u΍h:UiZ=؛ry68*VVP(ig;v=P_@_$#_c`L 0&`L \3RɡJ_2&lO4fHAy˷㙧٧:%%h:*۲TGO0 v}jJJ~_֝}s{zs;͕ gL tYY+l\ǟLME@0}'B;&`L 0&G,ZKk'jV'VdFzf;@٣K 6ttM1tv_ .%󔢋B>߻?huEKF9s5L. 0&2nvC:L 0&`L 0C0!l .86'+0L| TBjN6P6(=m]wŢy!#K*%1ֳmd}eFM 4>K_&1& /U6\"L 0&`L 0F@7DPywUR}YN+3#A(9x$gFiɻ@R33j<,A0,^r/|AS;?êAC>&_5ƫEs;a?`L Oɫ 8&`L 0&`L 0&9 4%P/7X:1c\kUxfhF9]p?f.p}U&+X)Z\vn˕۹->cL 0Kg92&`L 0&>R)O>P԰SgjTg/لh:N Ť d u;K+ +$`OKϘK`L 0&`L 0&pI ԕlų+Kee%FIR`FpAW΋ F>\m9yMJcy!)ER -:%4ԣVnrN>έ[Y 0&; $)(Ҫ--RGAUU.VlRbHK+Qo" $HH"?sߒ{Onw>Ι333sΜyJ KϘ) #oVӋ„do]4xf:~ByWn ۽eiFeנ̺ BTi8ywR_i`9rn˃g$@$@JRK$@$@$@$@$@$@$p+ ࿫߃;P 1 ťnpo6 NˊWz6W]i4+=$c2lɵ}Ov  5Q(t{T0sGt,LC$@$PZ|JK0 xB$   JDN|Pqɱ]Fa!6iMzocHFc] Qf1H+YmЯ[䟎#r:r^zIHa\fHHHHHHjP|{Nq: zhLsG^l2<SX.gcrt7 ~(zN:8z0,nA04tih0bVfBqφWњ{J˹wy)V& JJKUl TEM adXJux0qղzg 7"Sk$^R]RzYtSr/hF%v} \P"r.Zc9V 2-_oGĄ sL715Й:'ߠ:Ryx$@%Hս9 x#"I9$@$s%*`9 wyd9g h8c1K$@$@$@$@$@$@$@$@$@$@$@eDaRÆ*Iד'eAMzG`BR}()7*@'HTHs*T XK*E;,P!0L@φTTn%           "@RYg$@$@$@$@$@$@$@$@$@$@$@$PаTn%           "@RYg$@$@$@$@$@$@$@$@$@$@$@$PT0}. @ #eWv}#iλ"BAt#"zs: #7'.^C8{pϫ пy-q7QGp}C0_s.٥oD,YxĸIѥ8yqfM j =0il>ov"ed͒kC#M+ݫt9* @˟ȑ0a,&i+Z.}EgîjsőOXJ=E¦5xuRr9}5=/>p\QN㩏2 瘱xwцUxgӈ R^XI]i5*k altUJxf(Vw\@@f}ySx9Kйz}X<M΁՘N{ Ur^$@$@^#ॱ?^ӇHHHHHHHo#쮎. n͌E!T?i̴1{DkW+Bgbs_u`bI쮷/ X?{X#xn$a{Kd}kaX+B>{R/cHvaoMXn^-%vxJ$@$EEE$@$@$@$@$@$@eK w1*iD=2)hs ;׬V”QZϠ>a7 f-}uGd#P:V֨xF;6a>uiLVJxf6#Sl?S27s2~Qw5*i}bH,N3^_Y2=#IHK%4      ( ~DZ%IQ9``OkPQwίӑ+ hgΜX8Jٿhuwme˨຋$î=sERbCX,ZBP$y)2K$@ϙ@ER xB||&,$G,zD?wDH,!$0(䚅Fs:6 AX?Q%ӨZ$K5Cf:ӹ%gW/]Qj TWV8]pL9Cm6TW ?$TWrQ*.3C$P! ~Si       َ'UsFGΩ MiRO(s׍/aǦdl$RΟ1&R?Q_N KΒDnZBٷbP{5&ק=i}x; ^bG˹U~TJ˜ Th>_å#:THHHHHHH 0t\uy9੕ѿy-'!>hIWhS?yKw`Ɇ50:.eb:1B"^bJViX>\3V:V3Ğb(* K|uüY/9 1gi2et6fVel]E_D@=כ2] 2B"^77ƻO7—,弪sg~$@$P, ՙ& +.gV#ꆥ.VϿ=[{ה߇ɕEW&J0DuG CP%w6µ yߔh1 %Ò@ʁ +i$rD(u|y6SWHXڶ Hppv&?+pD{Qx KFEE\Ęx)u$\,WrQk9N$P hͳu" M#׬iE>/(^qW+:?§ㄈQufBVmб[OtioIm]՟mAYe #qߣb`(~Z8A匀9owyaiX*g@e s:~-pOHs2V桌 ܾo/F^ٰT 1y( 9?ٸeXLaTr + aX vCvv3wcE0D+^_6"[v֥I0iKL1R<4YJܣr{^:˶(ж*kKb:S)fڻQux^X-Šs˭1\ݺ5 ^u-jb)6i'RvA,\"-WB+: Kv+Fb1uv.w vjW~)IHXs_&UGNTt\I@D=?jE|hqK$pk Ԁ4xRĮ}kaj$@ɯ&qOebhC () QnzCIa|(SװᯓG@_;f 5Fb4K~f$4v~at?ߏ":Xc5ŐϏ|n-IV,!]Xekƨvt*]ĞSCJ,260Z#ڹ}z%go/[cC{Ur^H>$Pٞ,o`lB^v4$ 2{Kd}Jɓ**Ò4SI2*ʳ ~]QKEꜟ6 ҾF%H> bSr(8؞OפYKe_wL}8̬ќ5c.6aG_`y{g(Zz!׵{,騺?E{>ǦJ"+qXu}L>umG25L@'X>~gR^xoS+:SHd' S6ƣ suƲcN]W]:BX^UN?=}<ҩ!u#tAg=8;ʮ>,wHH;'H9|ZjfT3oœ, v}'5jbfF(?Z,sv/7w=v&v7.WHt}HuѼ<5_1p,/Vl鰬&w?)"Þ#fI6I)w+BaB }D$FAqu#Oԡ:j-ڧw[X,Q Ӝ~w3;l539$@Tclo""5:v*oBBq]k5gTV!mѱX~SJHOHLB Xr^nKX>xΨcԯ}[dY:RDA60VnghF%)ZaX3*)!uNurG'') LOätL=aAm'scȾkۄqV׸.y=nКtX9 QIׅdLWuˊ-x}-ͪv]V.uhXGSXd`15 -_ [#)ʱFŰ s9I,LJצSmG'b\cR_|5[Qa I T-QInH^j-{yۧ;F.9g)~Ҭ!1g31{xX0bt(?^jxg7+uMxjH܈;GO87cg.VQ΀O#awؑ=K #'KzW0\/dM uxMHX/tN(K #@ `ng,Ɩ*{x}ΗWN3*o 힃81T)c;4=ۏ^uGFeԬ1(FX.inKڡ#tk^v3z텘h3cjL9on>Rm:Pb=TkcT%wĻȴ,V+״D_9u@"ӨFJIGҧثg6+W7<|t9=剈צ''GNOCkǰ?:0s?1\[gݿA^wN o I7y˖:E bH@"z/-St7o΀zKu3:CWmP-FRiFE#FtfGp xr)mn뀍w;{.{w{rG[!  (fQ0&,Ĝɲʸyhk*kt~nepB_9ZVc1Z!xi?cw<; ZE5:HCTt{] kAp;)o.xm6x vx!dgȽr9aΣy}947ЦPq?r_For~}_i#M8ZD xLP? }wؤ< =y>Gs$צ-?3Vlj: \|&YF#>:ӧUDŵ ފxٯ^s6`p)sbQ2 FvN_Ǯlk½V1]01 诏F OoPWay rw)RW`pܾ.KߙnwV6ncޡqZ-z7Qab5tw -Jp]o"v.68o]%2,i3gk*˱5>i<5}y6SWbn1ám;m&<[<39ĮBI֦/I R[9 RWɲ?Bz :-t:uC9ڬdC3&Olg :ys mmMFoI)X'/Y+TL Rc!-:"kb;Wc:]`k 5]>.# (n䝷ڇBramvwcIv6i.ۧ.Yu$#w? 4}_eBn63BΌvoAes_5'~3׬DlPx"TK%2,Ul{yLٰC)-%NM96Z}:u?nQ K:˾4=5akf m1{{7c@"#5!avM{5y6bo0R/ Sb}_DJV4?Yr̄F{;ë0emJs^a/^o !ެT- &aXoo$ىw6Ç}ӘahU D@xrLU(֔psnK$@$@$`%`{ؗbۣ}@\S 5\qlwZ.FwI.J$;{?[)^9:\0ޚn;Z*R77(%jźsmT h@;13i'q߃-3G JˊtV F{Ep5[Ÿn+HHH*O9M ~+|N?y7#V}>MiOʫ袔D,|5`er&ٌ\M 0n2#)ō@IDATk:'EY8t@M˿0=zW1OEޥS\3]~5~=yr*8~WL"iޭI~!I;$ppjC[1WB9>^-}19.k'ϑՋH{8N -+LZ`[<#F7)NvK &^wΧZA ʱk|~?uh1*I>7r8j, ͹GFJL~M F@eT =u0.̌W. {帔zEDLf(;s͞@SKCU@}<0-AmGOhL6IJy| Ϩ1s(_]7`j1U®_sak߸C GP6s(~,$?h\㼊xA!&5Q|)*2@@ Y>L$=ֻ|l_:GT)aH/hUP;DO|c!Z:zMjXKJМa־n\;{p߱H)r"F2:mgSn O+!3n ='oue^:6,} c8&"[0"2WR7sF9ieQj7phkfǸYXa-gn捉HoflO=Ir+.'78<34$i;Gz=ipkO1qD6 g~-\&K4-hn:>܎luPtDLfDŽG-),s V^e(+)Is݇Į]i:tCT]fhs4CӶ%)]g,BFvgλO o{Il[]3I^JVNDxԣAOqr.DL6"̓"M;F'UKHr0"hYF'b㋼یOl.xX }W F̤ 'iҤ _kɖe„4p>C1Ȗ ǧf_Ǧgc] %]_&E{uΨ.A0yI<3< ik%2#Gų\}e!. NZ?7Gtt);7YCLˬrbSOzG^[m޹x _#ň6 g{67sޔ=@h[{2]_8T_ |3H*ONQr7r)_Ӆu(Z\@JΨ._yC"kv}V;sFj':ok'q}jj% U_}},IbZ}2a>M0qxcR?+3Q nm 1LjAXi#ffַB#\òaug1֜Q5WU7ɥ]&E{xW!Xz /+wHY9+bz0꽅iphG=]3aouLsB D Amy]j=[EO;bTa"wHz\+r][,&'xK)q6vZԤJI]i5*k altUWIxf(V\ٻ0bTSG†x<Al\|jT9m.6bÞe$g2&KS ޛN|(t:9,\eg9 o~q#!lĥ1B,:'Le6ԌJOc5Xsb@rHl=kQ[AX;'lٰqC11p RrOY9#щ6F%)Z{ =aipo߆g{W REKǞC*aŨ”Q"v|t]:Z^~}niwj86b)1Q ߛSHNW Nh!Z"e9 i]Tm*{␺ O=3*Im,JoPQEk!`&XJ]aHxiy&>L/fU30.HbXNu%b<,{eEukUR{<;/t%$@$@U㐠N'      I:vK ~ys5qf^eSmsS>#Gb_ >X\5 6~#5;%N^r |.] Nbtvߘt }BqFEx(V)vF`I&2ڈG+\-=ڴBFb(_;y^svmN%)~Hr:5CpGHT?"f#'$@$@Ug,U{Ϝ T2AM[9:f1vGTuSvWӐmѥ{7t"J~_IW} Q F)qv8 %@P^F)7(,+Dߒ0vorSZ"wTՔ}Jj-NWꜝLS}umE&vƔTc@lX TY4,U[ό T6!][Z~]?+W~9. .<*fOIǁOc0 q~3GYY,G_O 7"ꯦt NI&DY8&VXi)>E7ViPgy$bHHHffIHHHHH*5IbYX5ݒf$/ig>{Ґca +Uֱc:hnW]Xk: 1q(`Γ qB,'7s%ޒ@$F?T񼮋Qj>S ?t꥕DLj4N/* !J=*[5dMGNn V>5[ PwY.:gTqH,Y*x, 矿F$@$@U-YU 0$@$@$@$@$@$@$PtyM <ČuSG 1:EZ:(l8(rl)/SD^k>K2G_8t,_;#mR 4RLIj~6U\O_Ǻ qQ acU+ ]!_0ro .~;vIoap"GQ>"Eڨw#Z.*[;bo&i^)=BXX{^t41]rnX) @D6IܭT{g-7e]$@$@Ug,U"@$@$@$@$@$@$@Olǰh%Wzſn9%L xGq[uk׮9Q?q쪩 |4&iRDgy2Tt1%`Ƽ%$%[Jbɺ@\l}݋,[>u!F921*<vG~vT!]|Y3:;jT ūk6bbzj'$@$@U@˟ȑ0aЦM+4_OM o9'os[$)Hn?Wo@fAd\:W.ܶ܆9YYp\b ,"!m9)3>5+&N9k4Sa!,J^s@s5usYOӀP{ CZD(Bksy9}^AAu ASwJ%_8w.^TpDE8̴*/ v97 QHn->7i|k.&r͋ eDgh       HZuD<|*ރ`> Ѷ+J5C2=eY]o`CD(ΔHFRsFu<şa]֟g$@$@U«%'           7 а&(#           Na)G* $@PR r^ P)`9/w aZ}6,E䌥 vè. ʊ<%           F vè. ʊ<%           FKuIHHHHHH pÜG#'q*Eh_4k ztA۪;|GqM勈o Պ'.(ŬL-S ï"b z;ܴZKR? уHHkhXJ "      2&ǍCЯxx_ΌFR)m.ن-='|u bFa:ߞ5ԨiV{q^_l&-ߦ={Qny>DkO%=r|*$ ^p_4cfad͗~3u0)M;o2"x(/4jvj`"X1>_78ݷ-g5 b9y~A)  ΋(HHHHHHHL #"֨aROl68[J VJZ#J=ٹ'.o G4k;~QI ´d~FPJ\ w8x7 +akT;v E8c*/˹Ur^gD7 w а]F$@$@$@$@$@$@spL/UwW4؇m){vu^%`)ȏ:hYAW`S5v34-@)wtYΫJ9gU Tp4,UHIHHHHHH'kRg>ʻ 9&BT0e"d#tq>sEI]E^xE弪s֏ $ລs+`Z$@$@$@$@$@$@$@^%}`|[U #_˜a5Ӝ9'f4-jhڠd:n VgiD # ao(Bׅ#ٱ_=)#ٰ$Bhg#F5,'Mb?Ũ2Cw}!>FG!<ܺH8 Q?x/nd˅smxrd QsA*s֏ -'@-GIHHHHHHt mv/ ]NM!-'Jg]]:\vB3 o-J?Ɲ1NQ&T&zj4yE!B/_QmGdeu!>|\R\3 ylXNrŎ㺻Eںaf ?:R0sS"]`9Tr ЛHʌNT q@: ]4 }Ь]4]5omؾhuxBajmڢUS4~s&R4RIok!KU\cG]?͈NjX.eǏ?S8!GޔVH:v.M\8N@8t䴨uPv 7iЖh tK^T;l byi)#>sH}|ϊ!Iͪ(L(¨$򭋦u;t av3wrlE^.vpZ̹6iFсʺ4;6$RNpM; d"BU?JV66혬qmZJ+ڞvW<a]qPV[V|*d|*^~:rJ:IK*M:I26a]KV?f6Z \8o}#H݋av#߬_jei9 7g~-.x8&H 0N kb[!X%2& Z|ZLdS4qȌQ6{9n&$Kҿ=ѡ\pZ| t TQ[ڭ>;[Ȧ2Q>-nd$ߠ/qIf2MZfLl jt÷OH}ė6Bl;£eL0d!.! 9RfU>wL>k+9Y5uÿkO6u-_tV ]PSGE6>xa,PjAXnA_n(8JKQmC5K}s_Н<(mro[Mǔ"Bo[_H:T5Pg=6Fb|022zʪ(¥j/;NJ2w 4t58izPfeբme ('ث_f'^獼?0.ʑBz?vCthc!2ͺ۵l9b$ tB9.]f>-#VQ zOk?{_5*%ݿcbNiXdNL\oy'㹗n*ӣ_? 1׭1Ep$h1<:b.$s**冉; Ō+ERW{qnTI:Zxbn:"SQ 6fnI3#1&P? >Zat;e6hxCX4Xkn_͍Uc8sަ#c˒-BBv\i#S㢌Quw^\s(؋A<26I[Sk̢ ;K{,^9Q(L£s㾝'Բl)a@jOmSBE#+mp( )g}zzD4eFĤޏ8.9?V{>{>g,MaoMzK1%%aט@M! X+ qhR+xcs mD\ XXxV$%a`T*(5ɳԈv?HL6hCeQO!(C8?7/Q5>KUskЈ3W}ZW'7( 4*g[Oe>Kdin?gP|@]zIceM;kkNgmo|֣Mh@{T̽ᮐh胔`D WqnVafMfDP 2VA34t&oC\:hseΡ42߸<4-wƪ"ro/* ^m18ʉŔDG"`iMKo{|[Vz72# eŸPPrjsN.p'>F@WIq#(v}DVj&BoP;ˡ 9;ɳ"i.vRIj :7Ss}]0_])2Cz=eTUD,oWв[K#>'W]G#Kqj#,{*H#;;<Ί-&#1VB\7#5 ᘭXX,2YQȗ0yhʨ8k7^[0JeeKAgNw8ÝxTF$,}-:ƏŒC'#4]sDxΔKHŒ1./WÝ\ԙ 1q=tE3ztŘ/b0 *D*P} g_9].iEYJ]? ?;p4U_pn3pkd& CQCNkwu5!bMڐDQoֽ)oקXs0hPKI/_R:":)?mĂy.]xhdFG26F7ޙ#mU #jx"Vf[cX<`;^[Fk9 7 [^j-I$=n ! H@IDAT#Mr1(x %KA5i phwp*$Ѩ$d b ES%խa1QI<^NS(p7ah'[ꢓ:NSչG>F%858]hT&Ϲ60@T2Yo̯#mj'qL0* WW U GmMI۾ġ/O¨aja16NgiaeeoHj؛3>Q'/C0$i7$S6%wS;V'zvC0mn.[P,͍Uթ(BڷakcGtfK)>c1of:B9입)# .s1"[ ߢ,Hz`5ycL5{G܋KE#۹ɍn&v9`(3v&]0; A's͌,kLR'; ھ^`􈠹VG?Gt)%.4k7& $L{ٕCόi<;VqIq ] |FF?ە&iy \]wս4&`͏@ʊ qWjQq&׊P?;'@5I,TqbBy vSv3)Q;65@;}3L4;ndm-n->n5>( ɝ!apkZ6{Zz@)YSugSs+R)@ KLRCj~AqIM3IkNn:55`WB7 Ѻju"ȝH=\6\;[l]Q9 KU$TiPd _Y&&"@$}A.*wH#/b-JtثSww8 Us)gh27!CrA..մCJ4|]:YS_ZPOwI#R0ď1&m懲@F%Ao< u#-^B>Bp}v|_uՋҞ9jcƢxMzĢo,Ϝv1b ~;3&`@YlSJ0 Cհ:+ѝG0+ M{h oB *$iVfkQk\p,øoxݭ xF])i=hޫ7EDDE>>z4^:d՚Z{BS۬ZzTM0 k$;cfo:%}s->KX6naQھg9TNEr8,4sqI " Uڇ U2{ZqAiѭ+~7R^ؗ_rn8$x٣C( %Ű{_ Ht(DaB"K ңfe̓rߋeGo9ҿɐeƠOWM9$=Ϫ,rjKPU0ǷXO[5̊3&zڣ-rÃyH?a#iݧFce{y_uq>yZZ!eo-fS 1&8A{X0 C~7N&7}L5aP>Q-!# F=lA')\FsSܴ}v!S`=ob-=W4lQ<ܭ&=7ڀ6s%?s?vgzN]КIr YG:=)yL)uПAlX~q:]`4i~*Z (<lδYTh5iߌ7BI)ŹuoGuUw[WʲQ5KpeTLn0aKà6rFꆸ&`ucS"Em麚J?Sڣ?ʼnkرSk+uڜms_=:7'9ɊIqA=>nOta%<P2҅G<<fY OV^t^ڠd_&`݉5hi9B8)#'4}KALs ănLU 3.U2'/>cq[ͽ KF_Ra}'DaU VaVd  Ko{oS36E ~T 53R;;E;#U~q-5&rz҈45ś54 {7=[ֿF^i%`(`̱KD}'F ZNٍ7BǤ}ɰ$ b8w w)kZ7`aM^$?cbMg{쥍wpz|3L0S ՜'_ ?{{Ocά,r6ː2aiY84΍ 7V)? Z`VOb^T{]9K9wl:pEX%&Tr-OxB߇ ,#j?yV+T<@UbzJ*rNuN@JluL'n2g_ye%KAF9fmn5aߌ[(b8X0䥉3ƺOiCYܤ"+eY/el^ 샊3"nmM`\_VsLuڬ^Ʈ6s7 KbŬl9>G_G6R[rNkJp6؆%+ӖFb(8a4*QǟˆH#FN!T]_gMa&f y7Xo֨{YHfu‡Nǃ2gbT9r2dq{xۛ:E4FgDcyH&z?5r.\X{^ `o/b9 %);43dT_f"0yJ$_cTKܗRW)^#{d2bd˻WQ9H,R,oF%=?=9*ߥ(vFzѨ7q*{f>&Ƌ#Zi逳S9NO >2ΞޖҐ&1#G٩HUَ8WGcrc?2MrQ ]27BŃ cj4}"LF$g#Cޓh-_hh,~~ M@d$NĘ)c;hDGWv}SR0 #8r,{y1;~cLr6*fߵGmtGaM7#RtHJ; 6(TEP.9qd$t5|L}Sn+n#ucxTEҏjTsAVF1 slPLOn~̙ϊw:jA~i%JPP*=:zk0] Wx"Y!!hfG(+JTt:j  FgƞrQTuY ]ì|@#i:&R j Og냺:BTubuROSj] (EY%uÕ:sqʷw#t@]9k{jݗ՛xm s [ OrY$|'ѷO ]mu>B\~~"V7;Xs*9lBoB,YZ/X\: ,94IB膧e:L~O_76guO{ˌNY,^)&IghFcgm{Dw#m/ƭẘ7uN3,MFpu.(S/],!y?-\aܛ?$7o ؽ|=i4Ͳú᩿I.BNţcj#+*x 7PΥEU _YY2Z繺 ^_D$翚jz),B Azh>"fAY/ ¥EZ3)OU0#~ {^4 ӽe#VӋt=S4zJ?.};'_kxf=Sͼfk_0>J~ړ U6&x!׆HubL 0&@-_qt`ɢ`LM;6_xv솞+v% ncL7}Ja%B:[ 5^2u]@k=ڽ.$*a|U5^ƳaWh̹Ca1.ڣ-©)1T =4s'n<+R+j\[fNjOghatiw]viRq]-hPPOfn3#Lbɀ|$c:$R*O+޼N=$Mc1 ywj TWY2S]sh*OKiytFҀ T VnC jTWQQ>VժyQϵ!*O;`L 0A |_؀rմ#Z V 0&@%#;G#Ě_:Z XV FOfu;Q0QHF%sݽ$CI&f=m(C8 2(Wo;1Ib}Ohܹ <w_\5_Iu+ɛ wK%[זR UZyCbD܋ ,j{Y]:F <ډRD*߲k,DNs$;^Y[ /W g~/y@ WܺT,s+.](Bqi š7 ?7!rXD6v[@_Tpxf>E@kMR4k`L 0&\O@\_m;\_ KdL 0&owWK$4z[x,b8$py/>gq韽d!KxzZCf&I:&p6 ȥ8R4!ƞ2, [y1J V:m][}?\}K{7¢_{ 9'u wG,J!? RnJ46gºC]87&4*)U BXd?廑 w¨HlsEG8-MN~%#'a^*3RJjE GM#z!&2eZ/|Pe(wU7.lUAӹg?Qo)\T4X(/@cJe#BAl>3&l$K xǵ?79=(⟄Cj颓saXdYFq,}ힵ2|{ >V/ʡPp{Dž`+!;t 8\5v5ZDd~56"T28 0&ԦN7W 0&`L 0&Z3Pzy=oi)?B9ETVQF":6}ɨDSCʎ)iKc5)^eXRHJӸ>9),׵;78{L:\(Lc5~{_k5Jk&%L]muiHUxl }狩tcL 0G Kms`L 0&`Lnfevb0 w~*C"V!#oqҰ[B[[~ &dY̦`i]?us熦.WI|:tJ# ^f=W@gu)N]10 >`m2&`L 0&@&[*VYijuwǦcn!Ӻ)EvYhg6T"k;Xt,M€Pyuř|,"'Cꇾ +|r'n-#tҏ\,Mj;MBs㍨~ʪGix(VIR,{[& !F?][@5壼Nc8)׫A+5ϚsV2ɘ[Tl(:7^8i P&h xpXG&`L 0&`vm*Z4#heH# H:'QQ& yƊbog=z0کd<1mErMIʮ|݂ ?܀X̺;/Xp(Sg@%?Q 8CMIs>tb6@)V?5zEВd98t,3ڕPe8w^X%+!dнcb0}b<4CMr9K9;>@. CqH&=7`L EFuH{m'C01*:4فr&hY+GeJ1Sm>3`L AV~\̜XHH]W)<\E0K#ms͙sU$Y`L@"`srQTU4[ɿCM;MT/WbsXWY1cP3%hO*hn.|rt@;EɮsYb䟣|}PWq:!LWQO- tUUOtB׸DLsb %QYQ: V&N 7(@P&.1 ="]'> >N 0&`L 0&@ #HI6+ f y!2YOQd:Ȳ> 8wn<}5iMyO@9aj{Y]G( 0&==8 `L 0&`L 0&`L 0&ذ`L 0&`L 0&`L 0&],\)݌z 1pI.~s5g"Wd9L 0?WJh :o g9_L 4h9 NZ8~3&sٝV uPYd#y;%pr?;-eK-성L 0&`L 0&`L 0&VذtsL 0&`L 0&`L 0&haذN`L 0&`L 0&`L 0kE K׊<`L 0&`L 0&`L 0F2&`L 0&`-v=iCQJacp]!JVOR|mGT|0`q zѡ*BO?bۿgcl.N#|$py%ی-9/G T^č zjzXͭEiؽkg"Pdcߎ?${q4j _XΩBQL宻4 hش[>>`9ϱ|ce>'bJJ'`1H.kCe 6{G/|^.s_>XeO9iA-[Ν|X&p%qJ, 0&@&7Z)d]Db1L 0&`/^ D NN3(ѧ23&:&ERGG;KP= }f ,$Sqތ酸!8޵zLƎS"s\s*.JcǡX?-9]", b灘5t8kĸMSmW_1E:-bԻ1gC>_޻!AҸk93BC|{ ucw`L@Y`L 0&`L 0kG  IMJ.1bJ25Ta'Ɇc]1J7ជ0Hٴa?jf-λR->#2(s35* ub-R;a8 d~ÞlTu菇(3>wkmcgloF%%W?7WRwΔ"Z/_szsN=c8`.'%#eL 0&`L 0&Ӱ\p _ ? UwCm#`u3h}#C-uW8D7"ʁ*δS3:gT$]WB`LlXraL 0&`L 0&d|q0uNup*NG~MmƅKRQ s)Zg\ +,ƸQu"`!F&.Y5!"4e4sѨ:oEyX&0'`1OL 0&`L 0&@&`83W:I█}T\4"'5 ]A OZ#|C^z% (cޱʑ%DL[k!k+G^a&0nMfeWj9uOWbx'-nkM \–JIC3ԭu޺RX2`L 4Xj,A`L 0&`L=2aa Y2hv׬ jR^Cz4H{ܹp߶ը$*+ %Ή(Uz^Pk(ѬվS4ހa#>VIEFco.L3{u^%ֽ5;g%uND[uꋄ1&%dAL 0&`L 0&[p]Kdr>m*nbc4j{-žcFUui'}'ƿ9&u6JEk[52}q7fx3 5Pk9eDmY }s'1觙Yc5^"Ae͝ݬբ[/nwC/f̼fyΛɥj0&LE_̓ 0&`L 0&hNvԇ>UgiØa4p8Mk +3_ɉs[W)c ?I%CƇ^DŽ\.bӶv1K0, Å_dCm˽CWXU0]#,A@UڥoW3:5\ԞSodž#iKzX3'9V{75aL 0Si,1&`L 0&`-@ɁuFRhy;M| ^n1Xv NVZcCc曂8#^N.//!~5Y9`L 0&\N w{($:WLo\s=FIruX `( s}KΑ76/$6ʯ#bɰt3f\IConKrՏ2" *0D_5=dr$)ܥ! XoWNeSco~ҭ֏|sV3lTVmfM_*V{5䀡6~!q 5l k bt"Z!y3ƵU=~Ķ,׍Ynqס 0犲Ӱ{>D^Acߎ?$ c(a~JU&ݏw 2{WZ+Ŏ8CV>.Ja~l;j$+Nj5TQ~T'w?+`c-8N^ɭxfbTwhاDЯ0>/8vĆO.㋝#DPB,^s!!|#\Q,`L 0&Q]1 :gAߨ@B &`J 0GTaSG}KL.+FYBG>w'#p+l }+B3梾Ks|ozCX4fcmy|i? ״攴%bP 6\֙TU ;0 gh5g;g}muzsm-p12{Lrk:OyV{%|Cތ{t4;̺klH7'VjzWLޗ π%䰷|-U<9yxyV{'6}zvgSQ~*-Ry<%_wssMtp[)*}ҩ ? ߊn`Ò jDj~AqE5 5ub./7z7HFIݵwp iԖv+{G+pH{ɨ%'6{1qkbU`xۿgNýqc\5*> 0b ScTZχ`LNm N@$N֟CDZ{d#V% 5s 7]%C鬚ֱo8{?NGipd)+n@vH5*9f%Si!ҨHt/jkۙLɬ=]ةԑ_QHHJq];Ú@s"pO7&zt'Wq$wKб9Sօ 0&6`=թpS> Y} LG?-:9]m .%p-8*ML)^9[o N[?_hUFӨxkDvƅ#EOBW e{3e*K lw6; UXI+x`DoD"c!,!۽f5\.MIKT^[mw 0 ZO]G|pi9BiU R-ظ3hho>K1Dt \3KB-3Fp)?e"SF^PRI2a;u9տ}כ#G}/&#i5yˣX6<7¬" Y(n~E{׸(uֽKP=t~/t5s-)N]Nc7e=lɶ\o3W? |AaH`|:yB,5i\UJQTa*(&@ -뽅ixhT2Kn6r )pѨdJ&8x*f~Qd8 1 ]]áW+:U!!|b~((H%[[~¨F?X=p Al;(F d'!8R+WMQ3?BjѢBQ=܇E8ۡ;F'Y-5>~:J:V,:YߓF0uO\UQL4C/ĸl9PI.y@28M-׍k5VG29 0&Zn>+Ց9R: [(?_G-Pt'9i=4e'Q"{aH}=5rs @Y_Jٕ@ƻ(4 Fp$ ">~_yޛ #0wZhR,p}X?J'/{{IWQ01q@WWhOcsy}a͙笞Z!-S+3ƂO PzUZ %RǒtexJr<]׏mT3q<8ۣ?Ef" F6Ua'Rc]16~&9U\eӆ7B^Qb:;pWCq>V+`Gϕ{fū@IDAT۹h~FVV_M*Hڲd4]yPO{xžl \-%G`KvJSM +J LyI94K]; X8~4/AdYڌ~X4/LaOmr\Ė??Q#V6u}4F,XID# ?wƙ6b=)M37ɥea8E>F5ANnۇV˦bluۄ)TulfRO/\@GE.2&@k"PVV(?4*&8Yw>uh\yg]T(?x4ݺbT1[O뭭R+wZw,tqc>KD[rsܐ 3UuGm>Z4 +oj.=|&q*͍oT|ZɳI8V@m)>6_ 9S~> EŸtd)%}&眮yF/# ˫ϕ2cbz2Qw,ǦqBTЄt n 3//\wN9bڦR8sΔ-יkN[Wg9S&_ ]ΰWJn0ZGhx۵*|^N>"/?SޒԆ49f]*J lg8/wIC7sy(ҔnO@?;Ei=s_ҚQh"Mfd>ǖհIQ1+{U1ܜ-cÒ3t{-hv>͜=\H@0FFʟbQu\aMH{&~hhp n;| ޟjb\rG弆?kCxA͓"֕QGޒiBdC?n.{ʏ<]q G{ FՁFYS^M RN^{fƨD~4q`^iX=,[,!8+mumxmh 4C G; /eT}ɡcFhg:ju0`L xNhFǿA&wX)` -f0I xM'[qInl_ڊ?wQ#1s"~`Dwöw4Jw[`讪l{ 531B@u 6-u e2_W܌?:D#NPoa%⺭E]w}ȕYuԔGݯ3 1;Ʌu Dh7 'F(Gs;7uxS]uήfŌ>[~o0P۞"z o-OEyɜnC\=px= a9^a6/Y5!">qP"$m:[L= WЦX}=>żQO݃ϚT?6iewNt7NdcnjHgЌ%Xpd-ٍ(pԃX z88pd9bJwRNamhDXyDPs[Io/=zd> Tx<+~EC;Dʾ~>,k ;>&KSl U7caInڃ L}+SxcZSj>-}O ]Nz(~ ^L#2-O?6]w =^G{i`L n?Ƀ7G'v#-ͻf:M>_!=a8',/N-;klї~tTLW\9t?q=F&F< cF-ʽH/~DL4Ehmw5k8~5h^~fs#.ݛ|v~ig86by26&::^+`L 4@f[.ޱs~#Q6OFOl_GmΔwu嫰eb@'N.ҦwT j?mbZQB J lՠIno0v^yꃦ>,,5H p߲Dbᖲxo'eMc7BkMkh + ,)6 2־S4 a#ʖ#u-X}@h\j RhT"Џ6d iCh-!!,Jf[+Oi+fc1aWu]LkTQ{յF%IώJo_>b_R-xKg g:B11_/)SU |noD^~F1=!MIX)E0sLIЈG//!ˏ:1C3R=z2f(h8z?9K2*)}1v_]c/`*R 0&@m5FV[6Ƙ qDWz4$G_ϑsaL4!W>= 8u4Ν-!%U1YlQ^1`LO}ݽ>4ehtז'(অL Dڑ%Ksh9qT%i~6PN hp} |met/s[}k q2\d/bbk)Jpu1s,,~"uOvؼay6K R, whuo]z{&`L geaąS K;EYkGo݂GuA?_c$'F8%٩07q&"|6Y5zT٪Om:$ .r֫J!̸|ZO| aœq37o봏GEŒޏ>4lxUYPR/ַJFclW.IƾIe]K'i%mͥu.>lٵh R,.U#vX3b  W79Lzi=4]4dbj{ÌW&ו|e 3vBf._\{FkOk04*%U$dOOG Eydw] 5N|:צC]l&zpѷYȬ xtF-,F0&eGPJS!α}|wϡx蕡L.6:h ]շҼpiL 0&p ~=**\LxVRĺ19ɫUebpWl!hcrs4G:Ǔ,&_\np`L 49˧gəLBLXMQwvLB̷Ƿ ɘ#?zgkG\kde[Je4*| 33 C(C{} 8om+D;zc09vrNUXT#1Nޡ0lꌶ^(+k~hH:H8#it@?OP$)NwOZp_̓[ɴAr7 ~~,GYr!m.M#<=͝3QGe|ζi|[VvI:cas!|l a>r6ǰ=_ oũJ%D;3J]m"G_iu $pWQTntjK Ξ-^\v6K~cՁ2KFg؟ 0&Z)-ʕ$/K>>;)7_@yRT\2պqhzO6LPV;nGg{ҠI,v IޝOKVB[b) A#FꬬMmUM>nZOK^@7/Dx[DRv6 y$g)>:WS%Bt:  ?ouy&"B`sPG̵c'sQ _CumFO+~A9oTѰ0 0&]4x>e lA$>l.{Yc㷥 3֭ϿrS扩8<}0ט:{F.VZi1Ï\8êR#V \QUsL݅u4ixu)Ů۱kZo݅a]/W]ᅫYwߋDo^pyVF9Q%bDcYbcIX[\:Q>[lj:kXذu?$NoQl8jo9iAOW.| fC"KNv|ב b&'chM(hߒU'/<*BٰJ͉Q>6c ΞE̅4wB̋8JQ/؛zԅ)bqMXŇʲ_) e\/OӜY^\~rV1o2?cwjU}\^V3g3ffgXHasӗҺkvǒܕsbHRݴ-|Ϳݽw@^NIt!=qߧKIiuqޅ؄a(?*nK& L xf&dQqr>31~`vu=io|ܱ\QR_P׿|3_$UE.{pk^aB- #މmHKފv*J%!_. q$ > a4 /Cs gzϐk/F,ZCtxe.啱sn.f8܂&i.`L xa,}Oo+fџS~QU&^# Uy{Z^܀ns1Sט=Xg{h ɉijE_wnŪeH\@rI?И@t41.ŐsAF)Ŋ/Û"_7na(?Zf+1Hbfʿ-K8Iz !VMag0ϜK?H3Ls ewW9 Կ,Ă48C؝i{ۓ NYSUlV O0m_lJ勑|7VmT|TMP~Gɱ)_6xz0<,.v/;M 'J!߄3 \+q:G34G$ԯ?ؖWqP%Js4vbz%!?[hיFи %"q>>WWϨt/4N\390T_apN*5ꭣruqXk#͛dcl3ت[v0Kb>RtJ䚏tJn?}~.&F4}㭘c?_&)\٪v)gMH+wm3:,$2>T1jVMhࡷ>_9NV&6anZwE޵?\XRػ>E "Q'm[lV= oBtyJag{M4Y9aR筇#?'E0Mn^7NS*CuȨewL 0&жa!iSNOe411}n'1}3KΙ!|>y1'1eS$TƟ ݀G`utIn,uRx㏑ዸI}=pL?Y ָ s5}Mk[1>ok`f2ꏂY{*Eҧk}oRZ0i tf׵Re?~UJ!MO߅vs^(4)K#8JFMtӜ؜p-*:,wwڝvj_q24|JԐыtR>+J%? qD@!YC_ՒQ/߻N*ioوb0b@gl>I20<өu t似ʰ;啽륈c!9m=we,{[ޭ'x\(qH ?x(F/0N򼀷oS>Gc\^\.&-7NDb:4E?> d]t3۟BCL .dž R>{<ϊZCms1WQ5f&<7PY]?v#>R~VxJIW{^Zl3@~yN"eU(n#a^8jTm3iJhP c Hcbi 5uGn.22;9i\7h' ~sҭ6hhYKψrͿAwʙJJ&g.)-ng+]!56m 3hb`W 4-w.?{S\yIy&>nu8u֒HC:j:6j7B}dU(dL 0B?4qW_R.c׺HO0;&Ko;oS#LyWKƯ5(1퀻!**S似c SרbfyZN._Ty>J?-r`܃㦕3Es`V(Yk4eȲXN| M$@D,DUpt5/ct ?sK(PGJ%}˺zšBuH?$Q+~oHR,kwS5p#R) #J : 7 &7#s0Cwv]shGl&,8 q{z[I~Jt* Ua{<63\& qiВrkh-,}_$R/P1&`L 0&`l,wv5l4? q$#X$H&D"ݹd&dJ' tUcrr0/8? z:hp!WnK:>C;i߲Mtm4˕BJp ȱ9,d|A 1bc˒w;Y|::;aB9\5]8{0&@$D;~n^̶!L 0&`L 0&yNUό2 QW*;a:2-Йt`JL,7N.K4X2sst] }U^Q%4!g XJ/;%ݵYT=:ރ}dX-8%3ұ!.)@ioXk-> Y$A=P+ M&k|Yʐ*!‡!̑9*A%&_Z,S -}K>*L"vr} j8JJyGQIZ<`cΔ8͌:0ώ:vJJز*;ߙ1 Rx5J%+"`&1h`L 0&`L 0k|ԫr&)}W&ȇj}6VI.!c>sf`4Scn)z{PkS(?qHTHOI4rBx-|yj-m<眵g/s+bnȄj.z{.nŸ>G8 FKT*scgaL 0&K 2&`L 0&`"u3iY~<O=T\(R3gh:5S7.y9qnRuvv|r {]%R ۀ1 }"I L*$sg4r$:1EPȌbxv$!c5).J 7OESi#a0L32Ud\?/.YKRCX04q2=πrq<&huxRkR`L 0&`L 0(~~ALLf2Y;5񟔳T/ښ3&KGy*v2qvZ(הVrCsH@0-ߪxq?QV4f*RČmz]-/m!2Hꇥ)bb$R䬅xfA&;eKWe_f/yT'-+|`mN`L,89`L 0&r-v+y'Յ|J֥p*Gh:ȞF2w@رq,!ZsY rUa-fаkdkRtljF'^IiA)sOſNFTyx (Dc݊)t{6,QwQTU?7ۑxC.!2ceq&hxRhg%`L 0&`L 0: |v^g/aOiFRHvk[. |& !{̳sXo9c8r̮ct gF=w2aADzvTф _*eMA=`h6J_|+E Wh&Y a24&R܅VYjX`m+js`L 0&`LmpAGt3f0#nq=GpaYYu˱HΟ!RwM'cy 2xK:Grp p~jsH;+D{H9k+a l FEz. a‡!\1(■~Z3Wk "(TC#z`m%r`L rXj cL 0&`L 0GBfڊKP^V3g1|Y_\|Ju.>W&!N'c>ggg_5psïtbM ^ݛ!sr0W胙Pt.̾ObP̕%ސuM!s@Sej|S/籓S>{W.º\)xJprQ\;Crri~{_,z[.((28xV&,įzZkB$S|պ]\ⱱ]y}]؝pH& vTIB6יF`ղqH\@T/o8ck Y>&nd]`vEAײs-HOśs޺R ߴC'`_ᥩ];zbp*tZַP0!JU}>3L7aCkx~}E[NQ:ż"g_S4,%H;!d`P%AMгN|`mXj=`L 0&`rTӚf镊r5v!M67n}U.~6RHEDzrdJ%?[5ծڷ;g7gR*/Cm:kKrUMsH#]Sn)7,^(3z US*u?s9Ud+`L =׭Y0wKbC®i ZE=Gc|@%\s&X=X$9&DZ|V.̣%'~0Y[rؼF9L45(>p]j+H'8w.FȞ7+RvgϜǥ˨,_{Rwx+/):?GAjը,pE\*UU`@T VA4u?7 'aL l0IVX DѳQ _NԨsfL 0&`L 0&@['hx:7@ص쓎} (*9՘QYA~ǔu8?1&@[%js`L 0&`L 0&`L 0&POX'0`L 0&`L 0&`L 0Jb ZZX<$.~ݶ3""0&$^p?o u~} 0kXZeL 0&`L 0&`L 0&U"`c{HUq-,d4 ?y_g'@QS >CQUJzvXu;K86 '!AyUp0 ;h不:a9{kʨe?OyKY~r>%ɗ~É`vp?w`y8ĞL 0&hXh(9#&`L 0&`W@"t@ܸQv58 lN{Vޯ.D_oerT<cbpߒ ۃVuƋs$*αS*svx&MG67@S{T/)NKؼ 0y}!a&oU7kw W~,ٴ_;ew$Lkv+)] j V*)q="p8JQnamq^ۮs1zB|`M)F-+`Lh΋6Z|+MMJe󙹦.gL 0jJ#QhUOG#6[țJt;gl797:_Nvz]O~dtS6[S׶ JvbdWy+W;L 0&O~9L 0&`L 0&ZCS2`DPrZ ,6̜EMo9±Rjpt~;YPR2Ղ2syce]ۭ崪C`_tӛk!mVW+y+WqL 0&P':cp&`L 0&`L&PYp+9I€P>|L#^nH$: . Cv|a+υL䟓sǞQf)SR8ʯWb\Vdjƌ@@xLg4"MgM]/$d(h-FqڬQtp?o]z ` %;J3&`L 0&hj2%e ;`ʢJ7ݚSݭJM9A @淙H?U$v2.4Fc|FQ{wg{Y=܉BOw8;2<nıe;+]_L++:Ech5}Mi_mAVL&` t"t;iA3:8n"7= 1&GPR1o.]s1jϯj™`L]T`L 0&`L 0&Oڪ=!\' MMs< [o^:nhw[HK]9nzs:=V%@IDAT6MU9/)KC5׌봼$ % RA0ܼM4~xWvql""4#uR.+e"yǏo3>{Uɕ"6SP9rWruVϯnҙ`L9V,9g!L v5%8?*3dk~L 0& |w}08.ɤm[mmʲwjJ%8.AܽB0㑙L]]t.w|Q{DDR͎ yU%bJkϸXr~K݆̞dtptS` P):Ry>$L 6= [o?oƮE1&@= 83VbƑ\T;VWGL4L5e4Q{wa+Ё;u 2!{(zamnO8wiZ kd1X3&h&G>g[̀hİbs1J<-.(H^%5%<p;%8>3ٸk`FAqwT04W3MJAMRR坭@dL{@x <ieӜ)㘴:}R^U$jamVG&ʵs|SY})_!p>8<`0nIV"/mJ=3-bvC{-):~at8I}lUeH8yM;EK3:Ҽ,d'[]?{:ney N##jO䨐XOz15gK NfeǬ6B+^tF`HdwS6@M% w_^0ݝ}j}lHFZltvdZ+lr ֮ik?OmuWBk? ڲKPx.'ӰgoyrJwkb' ޝ:kiL`L[ac!y۰8>`L 0k0yLܭSf+Ă3+U*EX8e 3&ĎAK(+)A3{V_P=tlo+y\ AϮ֊ u=w;vcy=#FET"}b*0 CfZ2s !wgMoZܡEOC .dxmBa-0 s}~tw}-ǜFfMSUs2}G2F[;"N0 V KOH>ntR&Q, ;ҹ~2T޽t-Au$RkJ%ֶaM\~d-@U<y# V9ڣ Z/._QYZ'Q*/&"mVָy'q|ҫu*mSEgqEqC;^XWU>rIQz[҂dt#JF4ΟkH<䝹 @tkGguD.0v JĕJd]ɖIcVF('mw7^HW&` ߯<֐<NE^>~ ]M;>1G'`5PSl"?񗑟+%zcyȢC|rN۫3b߀pJd>>S?)ߍ`mRLd?7ڥ“{riNY3mY.]{l%3d|{YHL {(m`Pp Wˎܮ}Zq?wkjM`;vUvUZ_?EdԎm#A9W@ %WT޻a92/9E,JJO@7cC*3NY.Rٔu'7D ^.{Igg xC1MGDFG:w K~+G4x+e9.GƫJ%E[!Kqø+y)+vhm>?dGMUzݸ;!+@|xJ^Cp[kc%&lF WHLyjnLd70U̚|`1?˱=]+̲" |(~Q ߏܲXr#N*I;g#Wo?#"t +D,;?xz'_H3L 0&F ~1cJO6e;َx F?g\mx,k\b'ե72H9- CZ~)WcƓ2\T ws6_S9M翆\I&woWQc.W|` Wة3M~-%k{]-i3򶌔F^r#̌\ް~nR~.93|wȓHKJ Fg;+с]O6"/ (;WJַotS5i<s 0O{x4rt @ ySc!j4X%]z9g^{E3-ڐ{Cm@IgthڸԔw8/g9ڀnaaLb稏0҅C8ev6w:;Z{믢ZpP.nSpLގ5Bʥ#(D;OGgOV2q$2:t4{UW@iv55d0,oTza/\E (B&"lV: (i@0n۬Z~F{IJRj[P\Y$Kԭ1q"2v 36/}]/+(9N/:>8VTx:nHE,KLPNs/zL7tD_t&qbƎ 0&F+$4\0Fc .2hpꄈH'Q<7t3:5$sC&3:v ?-ʳڬEU43 l6'q7FX} 3Fy!ݿv<|_'gCBE}FipD NA?G\Óe= s SdPjFGg4*X1tVR563:l4]mxR{SRX"HH{#;u (B.I9asSmWG[{k_Mnukո1ш$_Y^ҶzE$ygagR \-/<(O߉_1,q2]0[I 4 w:f"dlƞ Ү>/HQM3Ee4Cv.߆AM^/Ž5jƼTrQca;ŒT9U6Mo 0W1e1{;JX>b+%ٕIO!ّ7˯03*}]:͌:R)8D!*-r Td5EA>, фD`v\p8?mlϞ%0&."߯<LQgxBO7v/ʾ&<0CQ*I^H^B9K)caՃ<`Btݏ(SjV楩; JB:Z%`ⱥt')@!QLH|)I$y@p9l2 -vL 0&F_y =?`Y)8TRa@3&`*+I!c-$W*+Bŕ%F9a0\ZtkUnςVǓ3IΌsLtQoP;䒪kc]Z3:l4c:5E-P\LY4]wۼm}R*GtQ 8:vkt26tqw@'ΗAP$靠H;n3mn)έS4f͏Vũ)7#-H*v.߀>K B٤D~RMw X07^ݍs0%$ ,.y1*LYIc_ 9$d a9E1 47 j1t!\RF#[PYZ^/(y=O [\XJ-E17tSUoDX.d<],<aɶssSm+WkMO=kD:WbU5ڎ%^mw6ճfm ۔ 8vV{!u{lz"vP3~,En7"0kуNȻ2jIAL ʓs7!_QQ'޽Md>^Yյ/J NwQY[. %riIv%?! sNٱGۺG.TGt몼 T _I,_g5#`L 0 ~1X? +>uX!1]c2&h;ʐNV*9 #>at!wp[V,’%+U5ygttnL>MB8 }-є9Z7YctwWa劕>~S9$+^{o؄3:a%Iڀ2v91MgMÜ)JLe;8ۀìWaX,sn&RwHJۦ`mQR;\l[4hR[$Գg@`GX土h]پ?{[{$تw>yDl8ȥsqj4´}|aMI@}K ڝI(y%D[TbUQj;/R)즱KE[[-,ݢZ.T+w?j\Y0S70ʳ2ޅdU?0&0@k[5C2&h#Js]Ta'k֓X.DXzDRd;'{2?9!T%%X*`ґ@V̤f&xu  qEöq.W^T S4i4]M~NȌtmq|䟻Iby#k `09fe;ڪ=ңoc5%"] 0q2$&7|F9 s͝N \)c#u$v9;[Sv-y>^띐ORK+'nUǑC;/sO`* uh˾-"thz}i8QpmweaN؊A T(c=rA;12CZY.GTeG!]QX>d;L^Y*y7U@.(4ݞ}YdOs_,VrTlF1G^Uf+|Tv S /[WZTH+od:X!L 0&05(tc"Gjm Ԋ`L Mږf*q@;ͪK7t6)\mkH4!ԤDbeڮk(Iw&Ď<_Hv4^y& {QzKݹE!qg17;r$b@ 3`:sFQ V;`ʢ8]c^1jUNLÇt޻Kphu$pg YNRv6I5 @$,7b:izsNNʔK@/]-LĨ C0uxq,;q 3Exj/:r2Sx&]x [(QX4Xx Qrc̕/ 7I26‥Y;6`rZZ2$Xڧ4wKA&|0ALeQ6>V\nj IJ^3O{#>jG_.2:qCݓz1BG0:.^4>@ٟp|;Hs!ocViNGbn0e{V"{։_e(Bq/-`L!~1@UVy әΤ7[F(.B"~{@taOYi!~<nE cUN[" GGvܟg]R|Z.]4`%{ .2Ra{!1 gc4s/RFoNO-bcWp$-;_#3 СڵG;B٭9'?9?>q #7#GN>y&ǐ51n,Yg>~p(rv _cPΦ732V`O')"|qzPP<-B=WHC4]9; ]`j pP٭ig9 u-͡\F;Fg&!H)b*tV,4wC>?2'/9R[, {[c Sҹk;*tg}2*K 5 avGcitW5("7O]V_R#7JV ;e*5f Ӹ5w:[C#_]irׄ^=t&NӢE%FL'+nDχA&y'ݽdAf ܘ[odz_Y% M3Eq%:]pXGA]eMM7x_{g22p-yqGzЧ3 ԭ~Eeny' m&tJZ!?4^_*F.=1=ETw|c,}^{og;س};e)hDbIQ3Xh</CJ8.4$.(蟑 8}mP]5+U|`[2p ziX$Lz~X&1x ' 'PI6a(Wy94S-YΦa.?ٻ"BBH,+QTJ.Z,*Vtѭ. .QR+%A$$BgwOȪׯë}g6-Մdr6gu(]&҄Jtjkrey}ծc33#Mߙf~M- y?tՠM(ӭR«\P680#P=0~suts|^&6Z.ј4'MhR2069q 8e+TRtf{X$/: 8{`֊E ?p/l&*4JWTcMn* Q)kBCf/ڢZy兀qNUnlQ5階Jyalߙ:bֈ (y!*Uo>51,ׄJ&< # 'ۈŐu;tTbG#m9i&Tj}#%Ȝ <Dys8w/oOu2Nr`-ޑ>:** ]k 6B}7B7rpH kled>Q=Gmys+pw:%|*{J.:RZFWGR>14H逫-r`qoXWHu%}@J k{P;u>,s8rK"mn\ DH@=`.b7oi |?g;?|,یd+OBLe!u źe6,4Sut43߼>[Bͩ.vrvbGbpuN+Y0LKApe/C pI':Eh[to|{u>rE*Oh˕Cxϲ)ƑPKiX1ub$X:!ǜ!P8W)ozc&\;Ԍ|GL9.Eaun~RQ|Ae6N#u%b(bᘯk vbը4k̖6p1ᚂ]& kNh}tI.f2>J~*mէJU diϮoj(1-<901PItY&.l(ȡ(8?Ҝ~ak7JNǖuJ?iZ сK?a<[ے{b7U 'Fa' 9}tfI#Jl57ꗩrUӭ! g]2>Ʈ0ylډͥYc(Cժ份SjwRW1 Z5@se*4#P5rG~_+H֙l P /OJnw7CК>k-ZF`F PޯUUsC'%sXs4V2(&isqĪ8h,G-5]gߋ(v#nK江E`ݧޭ~O$X"3SE-Y !*Q57Q9[-"/kV]u,|sO)˻-N(gOƭf9I%YdmfN`Fi+*N 힂;TuG+LP=G=GÛx\]poόZHQF`FpתFs#PHItB60npx&oWsҽL˻·Ekr"|}ayZF锺CRkv^ 2\5G+:J*5dV,q4-QG[6[6yWN[ߛ"|ڔӟ+f:BS>z/r"tRm1+5=HHUm`FDٻD{0oYd4fN{5EQ;VPĚxjlmؑ9k w'RȿƔ>T>mjWk𗯲1*7L{1YR ܐcyhވ~[[ةn^hUȸF]q_[mPI%QݖFB>B٥Q]D*>uSC3 IO^a/j]h"+ 6G i$!} Zo~McD$M&]"#CA+>4t@8\=PqaF`Sg/#0#0#0#PFRZŷO_D6U24o9Ő:JtEUaڤ,dh+Tg j^B! !1ܚ Oarp"UgN򭢯BPށ3YNG 1f$D(i)HN*! hFJ`yʏIP5ɗdDaFG5!0#0#0#0nJ"R!nnMEupR~#[I>R)Jt6N C"zk_[ūyԔ/uZ>lZ6m7&j2ȫSƤD-@Ӊ¯V_kN8y"3p!+..h~57)%>-]/z!=_d? ;=V*% 2#TSXc^X#0#0#0#Pj;Q&KVd{z굂ZQggƾ >o/=7(W+9CFt&^Jj}Զ$:(_tb{O*dFԆo ڄ]6$T"UDZK1E82^iC_rmԆvԖ6$-(V'K0#ToXT/`F`F`FPMDZmQCϰ2wFXXpjy~g(zh(G(Aφmhv ߛHNAI A[z`SO4yfA[/+6c񟄆4e/g0Ji6q+9}݇ݻ,qN m 7涽i 0#p!`:eF`*֫&Q dF`Fnsq\j%6Q(\jͥ0EdUKY^9M+: xmZ<XZ eGi+Lzaݻ>&̊Qro[ 'Iޏkdk/r*BOJ@iyˈ^w5}1{ǶvEKCP%h]\%lh4gA[k5!:]΁`F`F`F`F`FZ!pm"ֵkIX85I9‹  ӑr Y޸ ~~A>s"Pq^ӻ &>JЪCO B"ǖ>}#I85{)ɿp34a؃KK>&uEt[rg2l_d27=_?L0#ܓ){sF`F`F`3.lRMVЉtj[saHâ1O=ЄJuEՏ:6!gBRz=.QU{(?|c`'-BV{ڍ"Vښ曆Z ZEĴZ,c?BD BS;&GA 6utb* ģm,mF`{VM߱c1~ f&* ŋIoܸIGy5(/$#p"ϑ{sB#$aF@F*>W WpL6j)/\vklΖB\9wYOJߗTS8T=oӘL?uQWz~5Y4GA~!\6@i [X޺+99v QkVtEm9 sF^>\f>khiUD՜e$#`~lX5#yN"lX>tM`Sx:080#0#0#0@M *փQz`gM4 dby5 IGtu󄯟Ac:h-~K37F`F$)eF`F`F`F`F`F`%#0#0#0#0#0#0%Bj DԕPuGyAẋwȽ{@y!ϑB0# #Uh@IDAT <}qc`Tծ`F`F`F`F`F`ƒOԔðsa<3~TM`80~VK xW̲!]n#`~6̠R`\F`F`F`F`F`F`,X[s#0#0#0#0#0#T1XT.7`F`F`F`F`F` \/#0#0#0#0#0#0U *^n.#0#0#0#@A%ڻ{WͿ`BgqoKSh$\B09&{!J]xͥر/ÊpTi%ãbS dvش-s[gxFAj &tvG9HLY7t$k漲ʓעs#Ϙңww(wI\+#D@P1d3I[, ^F`F`F`@udI{"*f"đцFſ fH➵zM9;}=_yK*0?uzk6 R d'YJ^ _7ݔ_9+m3cF`Dg#0#0#0#B5,^im1XqtUȧaS?m3Q4jϒH t\fn܀'ʌwFMF40,r2S%_lJu k_^MsCh]59IZxNcXKt%EZ=*@FʖX7Ϟڗ+> A:{efhǹͅ\t3 >eF(gXcev#0#0#0C=;Y Զ Ud-Ǟ b1yDJ"ß}ݔFkŻ=BHsOԫCqRPI)P}jWpN+z"TR5gYcWNpoZvkq1YrWkVQu<׮s1z80#P`!e#0#0#0@C)84.g>ow m"Ĝb-P~緳 \k=;G_;~;uUrvjK-ީJqǩcNTY):tG8F`F,X20#0#0#0U ZܩS -+Y_&7\ LCmjh"_DV3regًNGĦ#^j9Uj q_={on~!_F7o9:~翰w/wRs :*5ӵDyWq^iF7`Fm >gF`F`F`*f_B~m=vLi$"'@I בc&^ȡ`NҷftC 4cI[;js-g7^˪7X^e־晲%.lhݵkV.}8&<Ϋ8œF`ʊk,AgF`F`F`*1wAzb9$5EAHՕ@F&4iN>X*voMiB%j6.m(pCznu,gdGnB;W UKlz4!I_[IjSao]fn㼼 cF(7XcܠdF#0#0#0@B f 1O1/ZvHW7.]hy:.(iv;,ݼf̓qF%VD64o>nX5vs䛳Qcj߉"k h=>4k{%Q}aΊ$)-n{42oNósLTaSл^`VEx9߼n:m%AJ^3+9?+Pf0#(ldF`F`F`ʄ@KzIx{ ' S䓨]Q&:dSר dÇ l,Kxq)@u| _oMԄdkl B$ ]9 n[x9 Bŝ˥A3DG1Z,ľhSvh,^ ע,#21E?.]3'㜮EgqKF`Pg֩|0@%J؉ 2ޮhqv/txo!}t: ׆!x' Uzw:h0?]/,gb,,B%Xi;][ Sq.zNRKzy܃ݘBn#,z+x˅ЩfUnTC2O5.tss0qhK= H% 9@'XjE& rhxO.VoP$_BN뭏1HDrJtͤNݥ?b|Tq~W0#PXd RYϸj#߼Y 9Pۅ40Mv ]d݋h};c Nf `NDzK`$q*]KlE5}$RÆgl_)m( lPSF31#ؕ Xi& |ÊRE}y1ߡp4L29S/2pBzxha1MW [S1YHuD.;d8ܶuuZ޼=z44v`R4¦U*A]ɮY-,vk=@}'SbQL |d>ߏ^X9T&7׮mqCJΏ=Q.EKHX!!fDb6i՟c@z6^twXIG$9xW(<۠w5phNf +:EhGԈ']S+SydO҅kp䂨G<7)][Z1-i3m{zhnH-h >?w׮_q؈G]%_G[ CÇc`ϐ"6.t(d$‘4^ 7QYGPyCh" l.\މ-xXTU[0E]-8=j|Fqڙ M8e甮.z;k&LlMX{+xҮK-͏Ž#ژzHUg :9hu~>7]3@2A|l~s}Dcq ]l#Ah ΏDTSBw'azNhDJȒC`E![2N|y _èue=CK:NʹшWxIТ+o:Pv53K\^?cyV|t݂ 9,7 Ӂ֛KcXkʹ ;+i/%d <'ͩX4m$'#ar"LR$ٻz:,ITwYZMEꑃؑÖso鈳iR]6cA]5tj4cF0ͻ(d>ߨU0sтi OtK=Rڙ/B5]f+X/3^hojwp>d^̓~".ҪDpѯ.{'&--RN:RI[3F[ޯsu} KYU$#E`FG )I_aI1 ^!/ص}ڲ٨a9AvBI {~ G̽1*9=#NaF`> xo7ior>OWgkWy~lc~$X 8;;ٹ8>KȺxf25'죉X'd%n>55E;zsMRYX=]8nV)&A6Igvzn 15 Hs)X]4ە lAefZU3Uq.3*|j϶B%7 n% !k/bFB`coŴs?-@ͳH7➵zM~Kg<ѳWoch,.׫kIHZПv,x oGk;ףCyGGGjH”HPNL1E|erzGew<%Mp]YCWmԖˀhSbh0^@j"4 E1x+ ^&'\p(Rk[e)],W>0UЬxVyPY*xUq.zjcPM./No _T S[}?yencc6w-{[ظ`[?֞oWWTZE7#1ILy?qyf>ߜ@,9 cuH lӴ5:_iĚZ!'pIQ1aGGWjﱰ![MyƔK= `KѶO}#~w]oJ0JG־Ra"Lْ6 7zbB$I{j٫_ⱯGk&oONW-c Lyn۔x gQ0 nxwPƒg ׌ \}(%KRI<$!kېZl#Ym=# 6ݳXg[ P^oJBs5F!kq-iCafoEdCHS)#Ѫ 2w4o3igK/&4'oJǡCp1ԗ~m qv {"s[P}m:AM%y'po]"BnFxxquNRNƁO? ~렏Nȭiz);{÷?Lx'Y r NDqhpْ7imsV* Z ZٯuV[*9'Nk僂pk oI\4i`D]RU{t4ϜGv^õдmg7T؝μBSϝ?{Ap‰ ZpT& |z#?;'~B3C{idx|nJ;t޼ 8̋t]vߋ=eFj&Mڠo`.;?G'_y _B>I\4hF޾hwsJ.s;3s-M{]W*M}g`O<-\!3DR'._QD+bzVNYB&yq)IQЮLAu.%iG}ZD <#T:Ćj 2o/Uw{ ;7SZ'»lڇkVWbvWsxSUjDǣ!QBw.7Bu+5|P~{ T E-v^;LYGǞ b1yD-Ͼn+FKk_Ø}|yb|PIIJ"j? kވFcUS3=뭗M6gD`КM6[LxE4'O!jgyR&x|{hM4,HUiq 0C#d:"SI:.>>jܔXSU:",3^Zpg2Uhń׭v>3'á#KĦ]QLY>4~lL;-?zG>.#}[fڙxEp3NZ9[ɭNd)7;4aڻb1q.+Q{!mBRߨ:zFNĺE,(7ex-{_ c S%'F ROqpP+;^V$ EǑOtcᓴK9]!:x0LSZ uN%׮h/s;~wens:]濼>y/+vs;:meP Y 94F ] F4' \|h&72@`)7ow=Bs[R< 29[7">h/[ ŷkS[;wy >|{V\pZEnJqA>įd9XxTLzxZNaMC3~&O`sv~[z`vfZ.N$TRi|3^=O]ӑƮq5XڵۅU}rPOiZn̗?]3#un[CڅeTrO-ҿJ/s;~h#wWȗ4Zͅļ?:s$\]lOs;,Ï+x}g'I27O6rdZ+ܞJ\jtl[/ލ;uF*;󊺂̷j!lx~(2+Y"#{mj Sb T2&8jÆDAA6<ZMEC`<2l(j{b6FTs[ؾmJwjI:BS:NqE~<}0JֹhÃ}vą,t6D {^FK]= OCa:S6[UsIj[rI$HtiÃڄ녧{~IbKEdpK0cHFĠO`L6c9z#VJF}ae[Jt3ahC6Ŷb4QAy3շh7U78HV}qwW'sRm>A"%L#:g39UqΌh`F`" h#]emYus keGo4QSኺy9fs~"?34>22IKN*1oޤu#c^6bk-㗨xDw[ھuJt5r5C#uWk 7q|SYX6]4zgo_ Gc\?ռ!0m?1 XZ*/i=9 'TYIPo}LY#SQ:k.%?!k;U:x1faRB\j{|kŌ4˹gI %}Ge wB7ܫ_o.h%d[!9Q]^H/TD ]c5%qo 37M*.׊^\+;<-8 _;L*R-[ֶ|;`8h OU!l*9zɊV.\wtB%Q6"jRԈp:S'TRNJ9ǚwWH0pr+(ᮙ9\#0#8B>^ވ7W!""B]< 삋KJ՗H]_@rңwB.#B^1ӱrC,3ZYe%͜% zv&8n=I:!AÅd; L deL\V"&E˧[#P紊G=COd7TF`FhŸ{V?+=d2"#0sIhjxƥ+i#O %b, Wm|5+ lCLv"2X8V#jj-y?{d% ZYԒ B먈Pp>^ bE {#F. ga e`ʵ[dXִ@9Ab&S>ǾX<}2m_po0JWTϋ#ӏ=4&!a˳GXɒқ;6]{zZ;"\D:y1H \I%U_VʣKfcHEO܈D6ګ;z?ęC,&FIVݹOv;JȾ{i7HA^,G#'/&9i~^Mhդ[0Ds/'ٱ9wBviy/AO4rT~SLWrDe_{)v(gu48wD>-J4ֆocKAcF@i6ԟ8FT2VV|ձ㰘qxgR;W^旊9b3] >_y î:z\§#0#ppk;? dL;B>xAcc{ 7YV|MZ]#InýEѳ$U@93m-̮Fg >ˎzi2~Xai{ ڼ[tꇹ1OڝAАX麐o$G#^^0vL'ó^Z9aBsliMeYKׄJ݄Œb{4л^7kGkkF$ MQx&rphAr)ѡ|;ȣL;λ^UXI$!MuUo, -&+Oނ;Bn }L?N}/ `|Ne*W \FoxWӋsɹrEhml/5un+N 1tJΨiߴL4Q|pў%)1Z##`@{A, kA10́LBB]#,>!|H|HdyH|:jJS%))#.Q}s\΁*YTpYF`F4hkĈ~OԪ! ➘tʄFw!H4l.*IyNW,kIMZh5ZZȯQϢuQ-̑YƊ2ЉNZYkdM3(祣sG.Oͱ۹9Rx߸yˮ֐'cWprnBC0jJ ت.Y [ȟqY??Q*{$X*˚*ߤ%6MgDn,aI!6قFgF鴎SFx(K y4BJ8XNW66kj[lj OE]yU-d]qkS18ӹ ,K}zJ}ލiM/yn齨ްZà >AU/4a3ͧ ("D~$\?kqع}3*uu>GPYvH.yGqյ/ nCKQOu's{%! E>}عb-DvijE'ɿdEGD;c@Y0p/%ԄJJ+Q'arU0 k'"P2Aw#woMA@./UOw/Qư{ٻWfkdǮTj}Xq$t^X;'rz9x2'ȡs[}uC D__|n?"H(2v/vbw'<77v.Y?ޞ7JG<\ן{]Ć$,>@D$Aĝ8OJoв<6xN mG z%t UF0Ҝ&n-2ck:cy3 z4QBFH%|ăC4EVhD؈_Qo> m5o# P@X/Hl,TTh{,r5[&|~o"@X^ypZ֊?vU5Kri9R#%>zwTj Pm5H)̱uv)EƁ#wu)F߯g+L)gqr퉎QT DZ=N<(dF`[Tms~Wa@RM3Ɏ:Pess:oc> ?=l;Ƙ}`y9jmBB! -erB@LٯCq@cꞘq^>x:@<6|mɧ"XmJD,Lt0 !#)/MR3ʙZҬdXo+y(JKT0Y:?6PKHGRY?QI{cu;{??oUqx,X!|/wQ/kdYc>fIQn"4Y<2r&<7tej=Zb"į)0(nl,a(0!^lbǔqx {TĥiT_1 vi۰]Uđ{M/HlnP{mG֙X0ĥ-IݰY XW][oMޱڥOL,ʁ|޲ C*DZ7"S#wr(_E8҄@Oig1!3BmBIys *T HIJD_b[ۤyjrӑe'۶%ib,* ӅS~cp^mQ:g#m>9ύQƼ{O8Eyws]c& w麎Yo--{y]jF.{Cծ mtm^14Y7MbOҖv`fهɴهJ1E ĈnLR3o^kvjM@іhNy{Zv )F Z6Nbu۳F }Z6:30,Ӧ=d?;U[4$!!Mj#Eisou $)둩A^S%w3"(`h#8YE#y=&s%Рt Ozr<_AR\/$J6c1z77)><^sǎu- {v*uJ7*jaYdLFWd9d*0/ǣG+Qrgl_U5i23E1ҊT* S<䠉xwH6]}'K,ؓ6%rּsOQ*s䠥=16AQ:Q{Sߓ@ͽIFxXBOc#hRz*P%N=8Lr :U0HoO7c`~"T"ڙ`TN5jCTcFaĢw!v3LĢ͉94Tsh6WOʎ@06S3z2"UnivX`=dEl kJnI%zYb靨fϸfMM"Zw/ jAXu 9ׯrAܳRuޞ!H_tdN8Pɫ7ݰfr_ǿ8;pN+? sGZJtMߎ[Xx@IDATF fR:tJlHQ'=Y{ 9ګSZhK Enje/͵UiDܸ9/]1V>űgR8"x۬]hrҏK X[l|v|uZa} 搐V*yb, 4&t܉A++56h@V4?R]u0:U Kl3d#{Q9ZCb!,Ug8|I2|J7u~1,B%M.Y2d>V/HkRY:a\-,1y֖ciO])Q nmoda%bFRWgІP)l+w)/j֡ƪUwi?Ghs)cH\ɋ-=$L^E3OKUe?{gUudO`BB!! I H(Pbc[x*jq-uijŖVZPDĂJTX 2I2$?w;3wMY'{=sesThlnGGC&}nZ̗CX` 0j"@QqbmǢc=7һsXZ!Canl=˹V|[qՄtv^:f -UM 5+ Sijľ!=to%\U5&CTV8O5Q q胆G={`JH0ikjDsC â8xx92Z3@Sڭ:!V=2ad<-V|*)Ź\R{w CU:_TYQcE ^f}&Ѓ܇0Qbv<(+Q¯KNpk/O&sc Q?=b2濢a镇J4y}_;G }pqcm3(< ^xףTh1m4]wl"9S :*/Fy?B X#"*2MB&z'/7u/I>.Y ?G懢-֓ˇŏ0{hIJU @?=X5+0&z7oh-#ЪEiAÏoZ'7#yr(%F#l5p' > H{o+j7v,GBCG㮤ID--DsZ˱Q9mBg=-,:f]\:SWg8GAȔ]-K>L%=@8oAg ;R,I*k9 0& X2}CýX؏@sS@&ςSWa}sFEm2RQ(abnC91&`z7ʕ@o"PC"/OoӜfMj,/!Qz"qe | `4[Ӳ 3ǫ" 80Ť"b TdJC'` Rf̟J:|I4!1o K{8-$}OfNnb2a{_5xݵ5y9&ܥ|`Q,_r&7 Xncdk h![1IT+S.VɱC-'PIRkw] Z‘xX&^,Q^&`@ox{gA;֣^\ wJgID#1jct:5n{b )Ј:M+2ZY(ɗr&gNJ =)& W>ܕj&s!Uxa8vx[Jfr/*].qŜ3vʩa7(=K˒th3pTR(ۮ%fCߎ/,lɲwcIL 0&r,C`L xC@Ӛ,%~_<2YJ&&`}@oxx7UgOˍ_E:4@~a|5k,4ڄcPЮDQY&I1e4PpW28^Cm~ Pk0Wjqsa䚏/ #_ AYG}NP^4ޅGN5Tn<6hx[BK3a d6 ORIVbwcӿn,tQ ƕ?Aq{m`L 0& `&`L 0&`L@ٿRTK iٸWLZ04Fx]>^m&R#!jI9ۋO@6H^(7MOQx+%>-*6=Ks4kt&*Ȍb=+V$aˤ8VA9&\zdea&dRր,¯Ċ|Krq՜ُڹ:$'fL 0D{}1&`L 0&`Ljo}/Ă,hc"O\19<-,S[V%OHN і|Z~7~T)a3Q(ߏ6ir'6}~j}[$2]bN?^Jɋn%yJ\=ޖJg} '`7j;n?`L 0&`L 0F/w}?5d-hD8.EXNQ8Ty5|ȑ B2WXqgl\Z\GSsK["@0h8$ylvN#)aBc >tt; 2\KJs-DɥX~7~vtʿ#?~~)Slr{.{,?ZO&/WPG>£qi(XBۭ0&@ mF]ͷJ&`L 0&`L#N~F~_v~<&X6HTj AzCR|mgAqIrpխCE/`h?#@Y{W6D$rtfh5S h=r,̙YWR)YKbC$t,0dH\,+|w,m,~"92&@% ³v3&`L 0&3M 5!5? n0Z#''Gmv`B!Zd4Tʌrj})dah|xxɢXIk뤫uػ])\wZ#j%!g!^1(Ŵއ0fNEP4?(',@jsL 0&  [Iי`L 0&`̴֡Z'䆟*֞wZwAdAxjLO>Nxa/䱦QF]><|ß}`PV@1Fu,cMٝ岄>d]S\$wTZ0t@Gyr'Y>{^7ʥTmoQyJx㎟a\[eלmgR><܇G:k.,CPLZ5wMX]w¤* 0&-q1w1~`L 0&`Lh+.96X?0V7+|d/V'?6' { *b'b33K:_Nu_Ӣpz!u5FFf.,W;3lU~3|6 CPZ^'/ͤ}6H' Kw,|Ǹme>pC>CdLBþ|+0.,!(״~ELleNR,OӅŏB*-iU*9vSt,uش0$OC&+g* r_zFT$-`L 0+`L 0&`L 0&w+b<6).~[75M@f`]= s)NYO! u'0;[ҽF&G*;;J%xV5|.t?W+ֺV Jpcrl38y455aшIN}ܧm TUWz467)0'Btx;(ը, pYkDcK  HҪ0&`r?GF,dl"L٥O'0&`L 0&`L _`4_[ 9ѣ$}}RHe@h BpOTcqFe B9S 0&+6_< 0&`L 0&`L 0&`$L 0&`L 0&`L 0&DSxڅ-*\9= t~tI. 0& *@yFmK~Mn 0&`L 0&`L 0&`L zx!-PƝӢؑCz].3r\ w9.97 t9~t9R. 0~Ns?'7y?>E@Th XjbL 0&`L 0&`L 0&p`Œ1&`L 0&`L 0&`L AKmSL 0&`L 0&`L 0&`Xr=&`L 0&`L 0&`L 06qO1&`L 0&`V%uI*[Pd0bxLt%bB}hE;]{Ἐʄ䫧aKmSfTW!Ÿ"_?OKJHÊ 3ctF6&dƷ_~c' ]8vVAuEu3#%* 6 /GIk/F짗#qvWW_8ΜGqe0uŀN\r?׿ }S{;ñL 0&Ep. 0& [3&. r*ѩzG% vlBΒ03잢86=9~U^oE-NcX_d܃^:đBl}; VEN}:ڍ*؀g(pNwʂEyN`br~ 3*v?u='WYT5sp?׽`}yH&2X2\`L 0&`L 0L~Ȝ1) hǑ=c.IyU$=RZ*R)}BLK-X8߭g$j-sS*iK#ҧNåe@Sxo:͟Ma;i-P*d`ZwVG1Օi<91.+d\k8sNVL4:\p-GM<_Nq)5s)r?wf}1n8 0&+&`L 0&`Lb0 Ŝ%K5:Zwb vGFiS_K'λ &ʼn橋غ/o#M+%8+`a漡HHKAHͧxrJ$g(k1gQVe/YH|5 yI3#`W}JJA#0.^9j?d~ fJ%ܠdΕ[vclVTR Ku3+V'fJw\{n?Zi%L 0&XD<&`L 0&`=4*jAitM1Rd矡5A'rywTU\X FpԜx׈O W"|4+ mFq+Fz5 :T>;t91`L t'V,u'm 0&`L 0&E"Pj͉I^D]Ir^ܖ,vnj)WCXLfO/Վsg$E iHT*huZֆ_GIi, ]aOSt[Q)5CY[_sP?XeL 0v 0&`L 0&`@s^$!cV>IM!BI 64 C> 63Ũ<%NфʃPRDg\FeO;|cD kJM%;zɿP\թp\1uI.\!V?p=KfL 0K%`L 0&`L `Ol% 6>9]mCeSoB(?U$؈su]QGF}߹rlrhk Ej|g 1^i?P}0/?0/s/Kb8%lb|.Ef] Rs)\(`L tߚ (`L 0&`L 0&z [^UZv)f2iP| Hâeij~{ombUobʥHЬn;{QBysnֻVϿzgџgR[/TdkEPD->QNdOR*霆O/FPXC?C'HHp;W"Y)W3Z-+N]-(źfz\8Ŀ۷v&LwI4s&@_ `ÁB#!B'[_h* 0&pVG783< :;JLH;MLC T%g4gM'$!jC!v iMgPtS,nc!i]krEL-;(Usݯ*M ϋ SNxގ̼ K@1Q, Z#RuSpES4Jwc8۶']371nJ\`L to047*N8;/&Yp`9Z=|bDz̞>V|t>[@mFH3)qe-u(-ڏ>-1:#2ۜ-s@h9w7l KgRʘ`L \<b6 2XxkpJvvTކRILd A\q_ئBH"oRd?0@& U040MM!єLRRUlBrRzL= %";}es9c'+}ul/%λM]vwaNs?W~w 5J-c8׳LK~; MRNĘPA') ')+vy؄䫧aKU?ž/&Ld #cBr[m2~+ȇxLt%bڬ5Eއ{p4>0z&];qabk=\_=>FJ}1T'G\]V&{1&[R5+Odݎ(I#2,RD ىjoFMu*?;1?[4pWԧVww|Q[wіN`;̖scX$הϏ ڲKP}G I}r5&'dh4CT`*&\V,eGԃԤAѝB[['%xQT]3/*Ik/JVJƸٳcQmlsX7*ê&|:Ԧwm$MюQ SO86=]d~UuZai-둿e?nzNGnRW[S_- մr`&,y3fX|mV|)YOY(_$l罥LٮUOJF:)w?]S]C.q~?V\'O0㶕 Y'"',zݱpP;nobf?|s+dbu.(s{G|llfK `L 0&`L 0O"ڣ(u^Uc!P$dOAжu4H׈ / I2wc1ySl?gr@}]mv4S PVA6&76Zo=6TE#ē%I{[Sd]4^M)ј!b"3vuEΘ,8lBtcbd'^ޠL' p\{o7EEi ;]ViJ\bN]3޹~.3ƅP/>l1W_|G{窶t$-R q=G~R"ܕR4T`C>o!GxBSyduVܞr/JX\2cѼظK-KOOuad~^6(%8}l%M[*~).-/JYȝ4_c㋛ //h9~24/(JT\D$ aKxMwz >1[w' WW)J%!Dc:i]缧N͖27jxCi.ZPjvS;ںXҬfU;TY4C$$c\4PLq';}\.]?=_/hO90&`L 0&hHH 4F=M]f';Bp͙4CN{ ?5%7!eL(V! _!rtE[|PT,FDpZmڽ3ugEzI~! YhU(>MgQyW[kXH8BH9)$mRs ]qc{6[n4F1N\3zSsg953ƝPo BDXRC'SjU޶w8eG/HcMʙ.F`jZIWv9S͘q`U1 ~s6?DMw5[bΒ%d^/zR zKP.Y8R j a?(DNZ_KAR~2{Q\U#\'g`U%,yOLLFDwo-mONsKS|8qF3|"bp$̪x%wGoG}M8TU$!tH<ْ7Qz$H4cҜg>* 0&B$vPA>֑Q` CHhDWX|79x:БE3gbAxV)݈rYB&q& w# BDSiЭkJ2`Hq *]z;g}hf",,(}~hvs2o/* I~d=GȢsڷ}|+%f:r(n׫sxƸ>HI-W Gcݟs{>2i,BJLBDw#{>SȌn 4SU*)~0X|?4dx6iJN۸K%aod?xv +cr~w3]uCbOS5eW6窍GJ1`-y_V*L݀8'3zOhrdFoGקӴ>-> ܼV"<%>>)|9z_d]FL> 8yx* cn鼒GbpEMX2Ar_eͺ&ǢyW!݂^_Eʏ_$$:,ձ!,^>X=e݉츎ȵV%3jY\nyQ3Vwfh9xl^fhCH&y<6 3&@?%`Rz̲>frO9KΘW^MZ AMChi3\5×F#h[9ᆸf~lei!d(Yc&.gQ[ۄd>N}B#0uҖ69?W 0r͌3rsZɍ03R'szJ~*WPNC'cҁbJg1 {vV]9*t| /-G\cT SRLLA@)$աp{PZB,.OnFᮏ"b0H^$![atHN_)yW5]{;ZV4Ŕd/:7ۓ]72]4^ ܏FZM`9H_*4^#a)ً>=f FLd_y 5=5j)ɃI=QZ)=3 P_OX*;> (M d ŊgW`k"a,f۵bJl Ը8f:P^J52= dƽd[®ܵ+e5%ӚeT8Ԗ\V* b A7} u`dz&͂i]BƂ+_`BzAh6gl .""OP U @N!@Ǘߥy 6чЀW~A80&p0~o!}OзrQ"9&4/ {) V'ux>~ E] |:I73 ?ݳ=*Yjt'.f;y8{+ƁF1,W;:Nu}zw̟#2J FHĎԱJj1tVԣ SuحP*RڡTcX )aNpHGrz<\ Z|5j>yj^&&pو8;tw"Nn䍎h;Ew׶4=r izVY vN(rt0d51 90^{iZV(BܐssYDonC-%=SQL?0墢p=s]xvrLdΜgAޛҪ>/WR,<|eE4LȎwz d v"tmܽ [{g%LJ^aʕsP*_߮K<;]eG;Ive(s=A5WœOm$ů\xEZCT̞s'8> Ex+߽T,\~9 Fs ̘y-v?d*6M)x`V2~onU'@_:iTGg|1&`]GUh(ډu {$+wg'c:h}JOQ)C %1(_MT+ODxͤc!q}$)#%tjV)DZ^ڢHH68! בilK8' tJIZa[pb<y5% \<> r&%&u ebN,_Q(doz'NOGL96GU]?!PFO ~ / 36U}0/?0/s e *;=.t)obm4lP9?U$$il92 \lw'ݢ*DOh>kh¾X|cӋ>8!g' PT!}4W|!:NDNU$% C PA!Y 8#eNAlw :&|.RY~_Û;P凐,ܪU* E,H\X%ygகܑJB$GI3rWR* qmt|g -L 0&F߯ -? k:)4TR7DgL 0&GHOOҘ <ZHO F) YJO`A)e& \)ǟv|-6בGJ'ٱ[5h1ÓUkDIcVU(cfe+GͤLS\5ϺĐnn1O=9íV[+⬨6.6ՠ@$ SOD-KSű7XPl&,;WQ+"AYD)E݃F/oQW#-6KC)N> \k/>݇)m!=UH?BcNZ/Xw㝾Fڤu4m8dJC|⬴rXNiRؒB[cIQCsNE#K+kӀj][ *t*ÁQݳom񣏁)--<*g ά^C}ZOivXŝw.^?8v F+~X:_'!`L GF߯ 21ekoW"|`L '1`Z/+P,(] v$b5chQž>2\iƎ;VӎlyKEVr *>+W2mtjVSg?af潿q9qP ʗXHB`7Hgh̫[y%kC%ttg}-ŏ/.IaPcY9j(4]`oQ^{;b[Qt#B'Ȍqʀa:xKUb 6.1k69d\Pfs43p]Rxp>ͻruχLBp祶k^dwJ$\|"y5cŮ7dRq/ g,<Pٲ[KjuW}H5Xj{ /|/yqBdQ è)l.PMtET bBķcyC(%@_eR "ܵ>Dk&2uӌ=xiP_%ΐs6I&pO Z!DFm#4/sV> .KvKl+ak4npųֆui&LhEߧ*+be? V4+>A:mlg/Śh-SgL 00~!i`L 0CF"#F)82q" SQiHF>Ʈ46 `C0ԡ5(s=IUe8e __DG ~Q%KtvZ['u#躉0OHcK>AO*>f9qViV_/ʥYK9,f@\z)>t3]1QU;.gAQŵJrr NN}`[*pֿ^m MJwOKгb!c#b~9;A+B}0hE5ap\D!zk` cB]{5O!хW=Pu^. 9SL\L# AAIjrYql IDI0)>sl.KY@IDATS8\n$%QTUd SgXW L 0&:J@.U?"| 7)HV>u@(&`L h3m8窱i6cmg A\bZ=5=h7p3wTDt"5'XI>A-YjTKLHHPm Ў!'LtR, +uvEBy<T7 ܳ/6d2")8Ccw7~<\`Qml6 lTw׎8t:&_GRzl8 2zCQLM,!GD[+bb󪉜v9JNx[ Fŀ[wQ&hHPS'R*6M Ql~LH_/' Ր8 9)8`#-%# vAu [!cntX称xa#G{J5 n{555⟵^p_s!<>`L8 `U~X5.9.V_M.iR7@x$`L 0&JPUrAQ)d]ׂ SYoqUJBnMZت"2>vیFnor"RK)U(J`2&XƎot nUをbA N21./#W'%᪉h)='EE$ZE$X;c1}D:q47Xpل`9xyC|f912(Ӂ .nؽ뇇{6ה`rL9nykE!8i#aRXXQz@xlSw@7H (KBQ45=HKg̢{BP:5bo bJvyif„imx\?z?߀? ƍS/_05⼐J6ry2!6|.-u.ް }haEAPh_?>_/ݹ1,S2`.&Ԝ< RυB*v@+3K+|"21?{ [W# #._VTW LM4`L tk?hK(O"36Lj`JTxD.~~ek}5.(?@:9|oNwX.  5J-c87͚VEaJK-|{wcC9l铤o}DY4]8v&>0z9)\#~)KNÆaxR2Ӥ8&q;8F~xuoi|'bL t]C$3tL?@hGn3RUyq瓮LcY>kuf_+ΞK=SZ vab?qE|3,'ԧ AI$bKȞmh^?PCNbdMKʭFr4tכX(lc<}cm=x\RQ:& vC%"uD2Gb#+R,s/sz'+m /!o=I޸No4bY-Wt -C%Yڄ*(׉fv&TVYEcw}]mv7_3V_V~c@* 8rL)G%Hp+|0bJZE}hG;S])T؎4b1"ڄcEB2ʁ \D{pw!_Q\ʜ= 5[m3m)U1>Nbp5 ^p ZX^WlSbFI=n:g7vik} {P;9 7ֿ߭tᜠ 2XR>))>Ĕ3vd{lذ HLylAD_(Ermmh\@ . aL 0~I|^T0zir9M36!zzoُ۟^j%ǭQYc2T)z_ށ4 ЄOy 5cda14%AkOIFehRx^ۘqJ!4^2i셞d{ ?5=&$JK/OWS}J׋&8)l oUEP8M~qR6hp_Ktr Bl)gR³bI@F"XH_cd'{`ʻU/" ^Чw.x.X,.yS@9@Zndݍ^u:AKlNyv ^>v4̿a bQ)XsbV:iKr19Md3bE؝,~olĠK1!3 l#Ӟ %YX<+)2J%:/%hoӜ'ư-obǜ%vQ{n* \g S /tj }&w_{7Vރ+۱o$PB(jc& 86!!7]B\Jr4wD+>XP*[i8^ASy }Vܞl&d4-ɍi-P*d`ZwVG1ykFȃ+⋶-I\W0ϩߚtM:쨱ѯab>4yaBSV_EėNח%UKb 99|Ő''zmL(؈,%[^PJٳ;i |kAM$&P-O2<*p1FZ8(~jR :N4? d8Ʃ4*7gbAxVH]_iw܃.S~!~::cOpSEĬ.))B = De- wq7.{1 ډk3:#󦏣k]q)u.VR4K+q_+%57>:.出å>{8W[bzuc 9hb<,jkp2i54ban| cD, >hyZ0,FB,eܹf3s)>z4T@Mz y|*Zٮ/y+^$λK'9J[-[7ܻ%8~]2VfzS}k$K pUn)db] ªeܕ6i17mބgrҘEo+kef,}NqvU7wDc@#)I yimeX}ߋE,OSr9 =OrLcg֡y@2848F)֧HaՐOZ $+ :*>dUl7d9aղT|%ѣ͉yR,O cTNJ,9#ozr[1K"x-ǾZq$[Xt| 3Z_[ ]-Kuz |D& b?PE%Ie&3G0&@"~uL(QH$ws戺{ il`CFO⛦!NI9ǻL 0&gʫ'_{ & QuBpۼ6hGgZ#y-h>.eyY8wV.n_NDڊB8hd:,`՗"njqAFJ;Kk@I#CAK%06Rs=C$/rm>akeEV!$RE*3&@$?ip%`Ŋi ٰmM?Fa ňL/ :<VVZI>OG&90&`$ޯAbL'W;,H3moy w|wj+U)Inf*mh;ъ V&r.Q)3gM97*{rozrj;_,j%sw"q,$$jmvbR0#+wg'9)LH~+'_~z x`L`&" iI?R_D>d[75&`Lko.@vQ2 =|gHqrnjO:m NY1&SF w,16A4xU2|R [P}Vfa84GDmTJ1O_oͽٲ.ǜ4CO}Kl$o ZW-[a ?MvQ+uj" EorŲO&Syk܊_w#`L xc0L 0&`L 0&@%PML1^>TD'4[ؐ 4Fx_f3NRm30(D_' [+\bڽd'X0:6O>QT.o&4mL_19;,e_W6V$|;)io?EU#[^R ĕLjq1 KP  XHBE3&@_"n `L 0&`L 0&I zٷjLJq6]4v$!.2@Nc4_[U,S܂Zj%b{pȇF4dy B٘(8LNUlK\Φ9!ߢwd$&z8,zndq!)bIjVu;w-R /ޝ/+pE_bI6"&@'+}`L 0&`L 0&lK"Ly!n!q⠺LQT+>Ri?Fth$$Ђ*k]U55^jw E " @UYOjSth?h$2c9:u[ N-_.)(s=IUe8ei1*(q'gz,qנɯÏO*>fUu͜ 0&(^G/,7 0&`L 0&@ b."iȎ3I)Sej gr=!0+ז| KDIwDKHޖ|'?9Be7h֑7pDmGɶD6dN@JZƤR')IJMj+,SH1$KJ0q )`Lo`R߾:&`L 0&`Ŵ[.dt¼ec@ͷN6(DBⵗ#BڥS 1VfDՔ'{v)Eqp. x>pT[Ui:u a8 s=/{Q)2y_ށ[0;{UzLhTR4oQͣ 5 &gX.87 0&`L 0&>f+l:=#L=jm61ڠ^^e}ic4_bJu$ cSa*gٮ|P:115U;!~>2x+z6)JTmo>YNTj(Z,`3P%Eӽ⳩eamQdDkBװNc| !K=XfُՏWW͟{%q`Lpg 2&`L |ONYH I ;aߪPQ\*ZEZZZp[Ūh-H-X@+"eD"BX!wf%w{gYsp9 @$P5ry\:yl|@e :ZSp;ĭz ͍ UEX'/5:tV$ͻbگ@Zf<櫳1~n7eХ㍓0hlu6vf!'/Xlu5f3ꑀ<8?yϽxh"_ANcpcSe*?&1 ${wnOxtQҐ@ə9OΙc.%XXa):e%OaMpoB?K @$Ki4 @C&lȖ6!SUYNn^ũycR0_0$tl*{mNfq6]Upi.OTC:OU趔±J;<Ğݹ8Q 9zt{'iĿBtOCL@ߕXb>) +`Xqb[ W0ySg G,HH @TE;Int=,7)iz9k _ Uo2+ 8Y? hf5EkzcbDC( ~Tר bi%<Q[{(nO,뀶mcf;/~v{Pcf&~;p[m1)h'3W`V/j#_uоFL0+R=w_ 4!B^W1X> #iӸnMEZ!O/2jQv Km?۪WF;wTb&> ;Zm5~_BGma$@$@ @2=&}]yIǩ2j|[xKh*Ǥ;qag3TGs&^u]UYŤ!M5ҋjJY[&?kwku.jT_v݀ms_͹DOtߗo~z'' tL֮8oD:E &_N%)AU@@P7>e5 Yoņ>%2sSY[tT5w0p%7kjGMrZݫPrvpuTVb'kf0t0:,Wcb5` k?-~7g?OE5mWd 2+5J4~P;1}陚OǤB[HO:TUyO $:h}? }4IHcU&M! F 4ĀX3$B1qى˦ &&px)\!bFJZ2Fe<:,1 pIl;T]?G(6E"2b{ m2)4e$c1AmvA8X F=c;/vb{EDeL3%Cf!rٞ--%~}aRcN Gd[.1,-*KO'l =rKqmՙY${J0۸$Y6<2 ]ڧGF=v EѥSI*-8 )[qu)Ň?bŏ*2VG^FSM>40 ''Kvnv"WcB1>XwR8z[iw l:ɝmnMc vDs0O ub?*{hJ"X9yҽ^]ĞLw{9a'990<R-`^ ӯ@ 혪7mHyT[ ³D+%๎5t-#,{_d.+*ںwdӤJNO9fiIĜGpXɇ{mG]bM=*?;{`SThuM6((FZV֑Qz s{xLdma%GIHxíاF'(yRc_Wbb2[DZoqY FY:BVGk1ԟoŰaW֙[/i^ך .k5. Kb.+n6"o AMߡSх 6h쪖ۤzkr7x yU3E{x1,\ĎUk0cбW1=͐7F=k`3o_gvFűdBp۵q]X{oSpf7x)Wy;9ӇaoA<' L/l%m# [?}1VMJ@##Pv;uG3b=+V9?DzpaQMuS3aɗFRr!^]}\@2N dM3Hr6/'L1ދ {ݾVZu|+N%%q? zW_hFF]|<ѩ i`^=` ݶʬÓV9gWH_y{mϒU/MVBѩdYYhceytX,YU^ᶔUkl̜.YH9vӽdzbTRȯ\{ʬk[~?{ 4u&{P^cjZNL؅uĭZHG$1nU[FK,iEkq}GXg0*-Qڌ~5DEec{[cjxGnG~uo1#J,ַw޿K8۽6kռ /5شE}CEa2v$>Y̪2K$@$(4RE×%ziY~"I9EWq/Ҫ˔`9h.i-xI,c"\ Z S~tґ!NӆXu1f-ʶ varЩIk,"ے,Cn膯VANuUc,Ծ1)4Բ2&b/eZ*4#u@ /!Bq:,m]뽅J^LOڎTr(6AV}߯ڊvK9;cMd N11\|,ѧc:\`im10b5stx;g/E}>D]SIWe8&ڣBncw[5q%^Nd5vJl1\mn,TB1K<.ل3IN. _.z>ӯC'l0Z=uybAN հ4ܜ*+c0Y>Rb3[,(,-7[r1Ƽ6_mia1亡qXq:pq%~i-s^~2-oqzA4ܣzyՅ\,gYbC&Mfg` |>KR_U'?r,Kj:dhz ?VcQT zŋ>DH2mXAm:jKlz=cS%Q>IpmS޷ dA!J>WuHDJ4On%fVYp1ܗHo~ibڋV#}+]:{VDc0ZF^_5OG{̭F5(0-"Ľ^?8^<~S}4}ZexcH舷´ն=Mx#<4=C,wD]OԯGq_ '_ķo~±,7s Q%EcDfUӖn硚3#q6jfFhk)3ZTٽ FEzhOU%4kf۠Q@=5Y{B ,}]e})4HLd2p ۚmaf6IÈ ~g֧mp {$`3rKėW*ϝƷ'Bw-cJ[~9Gl\mw5┐J[-qtIlPʳkWe8%|bEFqjؓ@'[%l߻ :G(ufAfE?W F~}鯻mGS6y`ׁSj LZQڣmjsK廎>*7*`Sۜ%dS$i bj[|,e{.˞e30Zh1%F鐅,όZfhSElG|QV}ڲ(g&GA^۠,L2~gem"C6unj|Op(ÐG\tힸ]X1٭4.!XTsuvsY_Լ9w # } @V$Kr^dvDT\-<k㨝Geb\hiMŀ4ihѵ/Q>2 Nݕ},9jtB??fGR76)2+v&ߵ.O4p{…* Kcm6J[Rs܀Lom|xQ;c^ΖzgmzYk`,K{mOۗk)ՙ{¥|Wڶkž(H6F^îFZG$@uA bj C0ĹЮ.Lp!wP|vWN(1gdJ_ijG++5hW7&gI91q5x %%Ls` (m3t_-E:A+4̐Ng,Oe; =TE>JXj'$ 㝇pv-MJk@>«BG#7c=u53,aȸQxLߒ#n ೦q-""QtLl"ZLmsN1(gT6XT1Wdž9v }ᅑzE"fAO'[⁩7b#c@W۾?w'%wq͚h,R,FWxMط#9G`]ZB*P+Ǣy-~-%oqEQ<& lg/BIV%vI`4CS<.T_wO[Cp>q>sS=y0ݛl6> mcJ ?![Nh?L@5U85xl? i >O\cW>r:zK%8y:m(yzK~9'GaU+vVZ4  BZ) JA$@$@$@$@&K  \gǜ$@$@7RP˰~K]RLG$@$''*Ҿ5o*tum c#< Wy˛?bJ  w֥]K1&@l"~qt 0'ak>5ܚ'O4ϊ)IHH 4`-~ʯʑ @uUk>5ܚ'O4ϊ)IHH 4w %jeW# !"|k,ۅ5Oi.J# qS h<% _W# !"|k,ۅ5Oi.J# qS h4M{]P??&ǕCHH y⺲5van͓'yKH}gŔ$@$@$ǒ#           ?!g,'F 8gQuzz M]IH'+|\il:(a cEuP]yw&,f i[b{_~[V†;/'K} 1`_\w+Ǝʮ{[=}lǪ<>m% !C LP:5cRذaQ0XT*E)ӱdK     !@R5%z#cUKZ׉XCH q"<sFMB}Y3/m>}1 lp6^Rkur*B N. 1mRɏµ.Ŵ1 ]O.Շj&ӮNyq Ͻ6Z)eHp,yY"_//iqXcJ={> 1i뭞c$`^fLFH(>VJ=苓h“7yx=񌋟BB$$ \z;DI_     kcɯʑ!P}RpLQKsEEc+~1>Sic/cAV17_?A2g,nSIեs}qz[)9Z;QALpI!`Vy6R0iB, [x/͕pN;Rz_^Ier=g2λtt/=r}p,qGx}z-c$m&)C1$JYgA}0SQLZ4Z @]0TI@~21 趙xkC{L vl^)`xN{gy4<5U}ܟm[|51ᇐwü\9#ⱩYO)5!Yx}3ۣyᝯ^DwKTZiC[}_Nn57mS7]b!nc ǧXgޖN2 }3E8d3B޻I;6VJq}E1 2i&9g3:O_"fDJ<}.HHHHHH^ бTY 11yJ_ĞFqDեbߜ<9RTU"S_:,*~0̸ a |oyQ_.؈{u6 /r|5B% )6\|P00-xv-Ӧ[<h.[~p?";Spc;1"9Xv56x_RQ! 19w<=/u$NrWؗDJШ($$6ɡC]J2ncUEz4vk>ܷՇPى(Ͻ4Ċ*JQW.sc>H8bS:W,y g%D4Ғ%QKwF(3RCЮgtN}pa]ز0,%E%Ç  }C2ɩ8tXmȘ!'ھ-?&i(Zsc`HSl>{ KxO }>i bσp0Ozu)Ig 6w<^Y(A[b~IvyZ܁±b|l?Fk& Hc6x]v*Cy{[R󖊙o(ڳO?2v;[81>tzʳf\+]i0w Tw۱6I `Zf"1SgI>`&xmg7c53Ƚ*>˪f>/?hWOWJ#O^VgˊHã@CFxYyg'rܘ&߈{6ZsDLClkSx?71pށGA8O9Gx\lnJOڋc ǒ5Tg2 ìyd<11r&>Ld=WsA28{Zy-[%h׽B||m>!,NX]rVg"w]Jj|jl#*_9Z ǷBJ3"R:[XC87EIUTː}֩!Yv1{j7Ӥic)!9FT/E~JKվ1YTQs~C:S=({?ms*&+Ga`Wk}k'<{V/HHHHH%`?2$#h*iJ=n6He;;p6ck(F|2' ZRg[;fp(Y (.$so1?iksЮ&vݾl7Cswٚdvo8/=sǼ'PӧO߃>%dJo]5pz*17z I? ׁF%N})/(̞֑I3Z{v]TW<2͗-Iq:fDgk9[ԦSdv?߻,^ O۱n @%}g)Rn0XKN|Vf'_6 Ay wjy>λ=peHqGȇnEZt0L~vH}JpUo;-R-f%yˢ|eN ݾF1_a}&=}%4P:K7=&Dz4]~<{}![x9+oAv,4+zTq"IZEIHHHH|"K>cfh$ėkޘb[M8f[D۟w TJRlProܘiMiTsMB8;ɋxONaÇa@v+m1>''&șW=1t0oG\g:.EP h'_@GDAI둿nܴ}v/ldl1 k+| ,V.uSI  GopkwT̩"K mI*({ךs==nFxMI O/TNG]\.#39ũd ;+3s=17=iũ$l{L>?KRVDL+?/D1xh5Kg5 T/ŕG7vr@ ~# qkoufʇKFӮ=X)-ثmn7ݓ Fh X/,@1<9(5/:4iS#ǎb\k3BwEegΪ[ا6*ǓCzQu?- U49?G*6$*UEGbxuy?kFwe6KP9mޯwxR]x~\ I:0Ӷ Μ$g{D,DΝ8-]y++q0jZu=wvU$F4#چkBa‘u9&'n,iFLXK(Y37"'rlY )e+o#B}:ǗN/ ";I_la+?Vs}l墤QGAM|'w&YǏ@d}IZY$HHHHH|#`?R<&hHIQy><عS$.bgs<],kI3|`Y*kGasu͖H zYc`Vh3I0nQ#$6#Bl=o;6(Bwiz((?T%qуٲщ6w_ީ \p0ګcAHh-˒֙wn-KS~ 㶼>={Wmnc`RG™vGIlIGL֚5L;J$sgKn=}]i䲤D?XMk|= G7 e%ć6m=(Ic;lj}GB'4zVHHHHH$bXKF$@ۜJC/Iئ*U5:\9 iC᎛kk`ocqrY`Hu|ӗn•F_N3J/;6Pspxc/c \u,E!"~tgʃF$KTR eNYeC@/m:BeuhuQ~XOSa{R}GXep=w]᭞۱EB T/>{c}X}h)ٔGg߅J}թt0cx)mT -5^,# rnGl 3|k޶qͧlI3#G+5,^zQxzZoĦ_MzPE@ d#wέXZ[꠼޶V9tw^'uPTXd3qd)JPNnm2}؎k+$`eg\(P,);Džlvwyf6з+CW_s |wjd<~iL| ݪRPcp?>{/~?{f߰hXX-JxV$@$@$@$@$@hi'hXfR{F>Ȯ\߀6(Q:JRqx1G:~kS!( _/5{xO鏓AA}F ^)ւڏG`@fI;:i~1i,/)/7TyA?[̫`Zav+敧H oJԠy5l8D95|w.N}ys:&xv<s\]V9'El\XN$P)"кܫ.-b$Sx~cJ!iގX:juk9 ''ܓ2rrK$QW< GJRl129u2f:O͘:? okJ徴{6`]z~h 'aOc3r*Ś^T#!9;apZyVķK[{=ɓrh( -.W|)@2%ieL}P']>{ɳ5$UN ƞK6A W/GḁbEkhߵNe/OI"dy/{ }w۔;Ga=Ġ]W.CڅnH ~+KŧGta:ȵcWp;XZ<욅b\XD>HaVr=d{>?+JGijiSł)4ޖWqj7/KbɊ3 wpVvx8(N'{[5ۢb293 {WmW۷T!MxÕޮ⼹wv,ɣoV_V]rFWkFdM}Z1 xȲ3\ k4Nr0L+ݏZR0aRܝmsv)U7TzzݎC[/oP #/}Oߘ |SQGNGk?U g>JٿF I> Y2)\u*#5%_c >ʇW7N믖v((]'/MΔ8O7<=1Lj$\3D3c]T8-x6PRO    sAs>~q߾TXF,@l$<0qIRfbAr4EG"D nx1(fv7V6U,w8N: bj1")mZ'رqXXDWEs@LtnaҺ:Ece)1X76Fq HHHHHH@;]b.KԌHHHHHW>9JS`AG9OHHHHHH} mHHHHH=\=NLE$@$@$@$@$@$@$@$@$@$@$@K  G%81 4z>dd  F\T"B25  SKv,ٟ^qHЅő(e4Pl? bi 3>O\'|k,ۅ5Oi.J# qS @0ձdTTU3HHHHHHHHHHHH 0XG[Z (_TyEQ= s| qͧƲ][I4pY1% ( I @ɱ$$-sW!            \>9,{'ն̝T            \>9"B 96"p$@$@$@$@$@$@$@$@$@$@$@$@nhvJ&$           hXjOIHHHHHHHHHHH}vK]r?/S @&@ z!K @K~         "qـ.*Kc1Ӡ$@ @uذ rq䂰)]_~Oڄ2K{r@DC{W,9'VYq՝2%\\ 98: mm\V9*2U٭Hs#'bMX96>ڲ? =c5!€7m>NˋN=ؼv6]ώn[s>Jfӧ`@ۨڊ7* H@m'7 @c%`Xʂv 4d?+6*?XiWڧ(QP?kc"|B8[cA>rq%:_~: AH= sbZ9} rO¿WNFksoọ:QYŵRP9V<^$O/Eo)V`Çߖ"_&+$ "-#zr>י9oG<}A"#/ږ/}x^ B㲦` z =2 pڢF FLyG ţ"7T bHf!-ii/ڦyM즮]tf?a$wV⢮))**((AO\snp~3<{9w3=4R?Ŷ$?i1* )۷iQ\f=790g,{V3*,oJºyP^E4UOEm-w X[>R,ҖXvF%]1s([Tκ+#5*)Yf]Lc-H˙R2>U$cgs-xHHHHHHHqXrcz V 4 w!aJtE[.`U\e1]AO,צ`Z!o>8[h4a%O穀wsRx撒^Oqر7aFoR$?2y(tQſHD5)5{vw}_MLRo9frp8#yOy*} _>?:v8ŋq:F|vXjif C?oMG9ҁi$`C3Ѧf^ T'-ΚY Eް83 @}!`@%G'!twˊVcϖJc0kL|'#R?Nه K-2dрBgkI1Xɹ\`9!Zqs븼=J Ĩ1쒋T^S d=Bc kx*WQ˚܂ȇ㠴N0KG9e+3ï'ω„3qf,bXιfhlyLtAÿ!z+,(Q}[}O'*ژlCX8/ nwCbSN/_ H>x^xC6A}F\!|O0 @sc vz%o20<CY)lkEm!S{X4"UJ}% sxؚb XDUă0s:<ձ7Mwi9 4ou7m)|e}|RiuF=~_cy"g/VSȳVr  N <h@xRL6H3GBSgX:jIhΑMboDR?~@0*==|<,Ft@U ߮L8ai)ܡ܋C9`tp.%gnŋ @ O*([beyѩJh{ x GwkWzO/>Kn=@)7qpQIY 0c?K9RcJa?)rhtWVuxāQI(Y''?\3*EƸ{(oAxZx$Qɠ'5}YxV.rm@A7:j-|Ũd#F sԌӝ:%ϺԃHHHHjݬ؍7jOL$@5Lxl+&{y9 /=K e{*g][WMJs1][+Z2#$P_TL/m=L2y2o%,KXՓkR^,MK z@:Y)גIJqZf,Z d ^mGQX 3oAk;|0]~51S8{ׅi.hbIѩ0Y66N^Uՙ$3]AEHHHHH gAknG$@B U4WELy 4~zjM*;~"2$L~[fHgk&K0^^5o$r(i6z̩04uşSD,d֧PD.EqWrvS9DԹ|3@ ޢ}kgUEMgb椶>KۅҳN2ދ`,w삏HBsx&V꒲Yg#|04PZT*ަF?KsH^ɢIJ[,)}k2H?_[ kQݺ"ϺԆHHHHjݔXAH Ocݧsi!\[pOt"QkEMN8Ϗq7r<doJ1.~dZ}-U.Kٚb{ۏ.QFaU&"btoirØr$t#IzE&k*]֨o9Ky&teIxTzŋ^_oKˣ}ZUjuKϵk}:'[82 y@EǘaшRo Tv QwF-rA;X(`@Űb *#iT=N3* [ )ga=ѱS:)G._LCFdk(wցk'ȳ U     Lΰ$n?7'wʱiϧ.D}#?Dkˋ1+(w٭Qhr0^-'Β*VXs1LvA ;&>8KQG:3ʅPA}.H3 8'P*q@C4dKITaZ>6ȷ."[; CFxrؔ\E!r?\\uߎZDX+^JTlwQx)]3\INŝ5^ y{L$@$` 49Ya̭`r0;Ò|L& zME&dao^o2pL.pcO@+L 7Va9a'f$lF"'%/#=[ C$ A?Id˧)W:GY9#?'&Cݖ+ץ%(g,9c+<F%tϨS?V(Z!}]<-W~ MB6hKճ{1QvNV\n,|#hyI<,ݑ?i.RlMAqoi3BpwlyLX ޵" ѓ*I%X4=cʰ>VMD]1u&FIKhh`ÒQ| r5פbŻ0qQTLQIl=7wӧJ-ۚZݓ.ʯZ6CcA[ㅝV aK2|17*u,R.Atm*N k 0βY_y"Tk7DVhn/[9ږ9gQ= Tݶ֥yTu"[?* Ïnm'wUذbW›rxWoPTSlJz!)Bl:@y>IQICc;3qW/{vJҳ{޹V>]t~sgjۢ 8yYѪȯ~C-[W.~g]D$@$@$@$@5BIb;z&O^`6j]ɹ3Uob '8~`VI&r䇳hah9.paX|rvJ.E`B JUխ2yciΜ>WZu43m`dfe񾅀8é 0W.-Cީfբ cu'L#mVS o.]m Q7+ū&Qo󅟟gͪV8LZ)B&am,FR@xu{+fCy*;ZG*⽚( Ҽ~M6$&*A2~okr'\M"OWIyV+s@$oԺv/ e^v(V]=U`UĦ):vSKuU+$@$Ѐ xFdJ]\xFrvU!۷%:QLH@$@h`eWּd\pu<ϒ\ Vyu {5H(ӥ{5`o {D9N^[:rMu# 0nWY @- APwC<bM$@$@$@$@$\L$@$@$@$@$@yYp>6LuC nܨ`ZPU ǮA)qU9TIHHe)K$9 K7J$@$@$@$@$@5Oȧb>.E\gŜ$@$@$P_c$           Z&=۷eĬ>PWTqޣ$@$Pw}Rq_O|]y$%Hu|Ί9I@{!  /T_zz @-a;Փ @}!@R})I$@$@$@$@$@$@$@$@$@$@$@LZVO$@$@$@$@$@$@$@$@$@$@$@ K' 2_]eX= @} ݸs}P: &/$J϶MX7 *œ $''# Ozi^=z|aDטY]Vv,HHHHH\& KϗJ]`F LEڛ|^MyTд|=l_ju+zx c7˕aMHEn@yN9H~ܳRv80٧Y3sv_׮i߭uzEW7~F,|Y"ysZDa#arHcY;rwod CuLD/..f w#wݝjMi( ?"}*SIc.sk _;".!զh=xۆcah߇<~}E-qaаdC$@$@$@$@$@ٰ15% O{D[SwivZ8,uR6AD?˜q`_9i+waMDi\2ªF"%Z#wN7G@}e>$oUx|s*12G>IO[VK97nC^E)ޖ,&FxܢreMXO勇$PM<T3!CT\0 (āuKo v?cZl7[ĻU ^M Sy+%T ~ @$ V%1!ҒSV ŔO=ܧ![W _>?:v8,E8`TN 39 uxh\fuԥ;̞YnsADU"8R&zI@$?[UYDŽxG \JY /hv^>eU$@$P u͘ȡhf "%V9lQ&Yc­{=9֎q>L6P>WI1(1a=E3*exaiH0sTS̓/l]sZ䵤T ^tꉾd#1)> 戻(55A6mQ!ҼAc$Fx,#GZZ"L37MFHft,}kҹr0go yٙQviuDW,7!+(K"PF:dEP]#yq"[StmVx'kiCoր$e%񛿚ɟN?@@ͫnuk*C;yee~)t^RR0`.m_4пץy tDJfO3KNJԝ ?IHHHH<"s#"hLrsd$;PWlZ$'mwdiK7Jk`<{qozƳ;yc ~q)$@$p 1={2 s;[XM9"+V;0ԇvMzNi"DR2Om?@Pԉ~#iDŽxnddšS0y]aJ\19*Dž{|yb745 ZY\եpiIV:,H Ii4)$n1*3pafv)GJyPQ) cؠ0%_EVr^͔#JREFT 9~fT qwF>39V6ʋ>A66 (DJ BR}`č'6ʡ$m.F@]m3c־u3*3Y9 /~rܪnqGϵ }b!Ws'wZi      O Q^*oi!# Jxl6D!:U$9wT9'I+ Jp%b$q=J8;'wW(|{Z(΍qAKr1B,9= I4:MxsmʙJғb*asC^e\c\|%Iƭ\DY^O^zH3v*~k>?}1㖙$O\AYs)s0h*ޝ3A'0iNQh>/EU(R{3=V<:RZ|wSsjcm!o~ݘufe%rfޝ=5Q3C/dc64G9+?L|[ wמe\űI).*^,倷"׶xM$@$@$@$@$_[7#a @}&`:a` -EmnR]霏խZJT~pfja"& = MLR~ٱs4ubc=:{K5#i$PS)V gl&&ۖ;rO]Ps\,:0𤲕UM<_%X @ [oE6*IGx[z_Jv%ۧUׯۣXﵒjdװC0$*D3*IGBu~\Z}m3bY#zjb_9lCn\v'?8Rl^[SW    nCGzGHH(;#(lj*Iތ!ٛy OjE-S/zِ%a6'&V;_?S_%{b[./%@Šad^'_=H5%(,R?{sVݔ7y ԁ"ko{E]@3ML^~^y~U6@c%F"b># 996N(:P =v)Чp?Yƻ󭟧z@:Y)גIJqZf,Z : ^mGQX4~LTZO\&K.6̌0;Mnf_qnR%7|`KŤT ,FExw6̞`HHHHH XfAkjVH$@C U&WELy 4~z^rwR&W`q{X:ElU-3O  ,Z/O&Rv0E3*^EG2S0me[1# mxĈLAϩ~x"zyr2([[K82ª#"Q nQI{o`6`ܢmTnЇKUUR׿W}Z {v9e_ktf=,M3 E-D^iJ;;#X(+8O[)ݮ6QWR}$CF5aap@i._ۅBSMoMZQ|W{88\]A zn wZk+      а!8 O-F%Xt w8'^|^(5E~]0k?1!6E`= B#  ^S*kp傢␲#FN3mp8wJ_<!VK8ґ."qPŨ4azX2veQk-nf}.bs陊жd+Ke{$@u5)Wrb.pX= -W8bel %皃e >-3u޽ A1fX4"g!-ϭE[Q=(G%b?W$i߹,CCo򡖅,mF %`ͭ @m(3'S4šO RFߖR6w艷)·_8n\`Hvܓ [ѿGG%ORW5 O%`KyOmczKX/m:vmvP|(F%1i7KE=}.$1n֬ X&oI2s_d @#Q!'4ϻ5\9uT34l'rx;X(%-VY{uQiؒM@ruo{U**ժw'    p/`~ ʱiʙ<̍lߎfH!CdsB=K?.z~fX|I^/8sSlo0#ONaV,}](ά\L4 )(\f(CA/`nr9VVd[/ Ơn< WR{kp,b<~Vnt} } P-&.js!JҐq/%M6 rʳB*Ҿ-ej^. uߎZ\ Qyi/`F=]v,MJj~)*suk WNƹ;yg +hXr {Ɯ4,J5 {3Zd|#gt{{T] <ǍЍCb빷a{Jph.Cevly~zIxlpO|a436@EJ*<^iVrؤss2B3cУ3td纔Ki&ٿNJ dd N)THKKP\\91C'+k5ȑSR[4z *3FAMZ 1],UtĤ ^V?OykJѓ*.!/`ISƟ#&ah-ZY H8>92o2!^lvu#[ 4&ݎ_~GQxgBr-{Px}p(-F̸ؙS2d]q P/Cm\,;9Bq9_+P]ɝ4?HHHHHF@?X(M$@u 17yCm V{jǻK#.Ao~Ј8yŭX^~[ ''&1!\L?F1[;@ѫY+ O-4#|sI%o㱾ABS&'Eqk^&r̲H/IE@p8]b QX@qu*ҥT I[i,1ގ^VH̳2*AX{GY9Z8@:˩`ax5D5@q^ճ*􏽊Tכ<~𔞹H0xX>` v⤀:"WR?߀ ;<*.w⡻k,ZɅx6MMCl2X\G:) ƙ`T/j!G6_g'!PYūe-ۚo9`$L +=bT)&F–dbFoT>Y]po ͊tifmuUW`B3{\^vr&\&У:d#juTk7DvuRE@KK;DgsV`Ś Ï 3]P_1aFUذb›rxosM]`e"]רw/V"XJ")49O7vK qlt}Ps;|}!B$aR9yY:IKW8Qx񲧜Ψd {"Tmށzj $w{rwԔHHHH4& KoEĀcAh-;>s݊#B~ -S9 NÑ}.ŐNN~UG/<8Cw>%;M-ىUK7 Ck<5-};8)=|͚9ZSD܇ J8%dzMd;[Da#a}LF}r;;E{y8T  j"$1=z'/0ܾ}5];Ŝ `ŭ؂)avBF!oSeoD1+/®}V㒷ab7UzQv0^dž/gc5-=u<6zD3(&nÔގ2G6MQ"$a98w/8>ڑp׎JHCsps6ܩcjX 4JMly6&a:4  +(4c@l@nh::lH,zoA_?OZJf/S1}92یgw$*?wΨTiSd/&a@|;.ۋu,ɰu!KӆbՈJ,CdQ?oGGԕ2}$Znl< 0[T"d;NtYg0><wX7}l ތս}mJl v}p93ƎHHW~9#HHHHHHH +V"7.*tizcW93fn@bkc/XۯM(o>8+ 0݇gf߅n<61I.>cQѳ >ɛUP q:%_|A0%zxaf`NB/ UaILHv i~?BSv*}f8m:shy6k՘`_\ns&^G3!$ w#}ӨD983 5 @&`<Ws! 1] Z{5i$%D NZ)" Jp%Fax9;n8+DnRBpKJ@xU4jRiM"L{G:k-z銜Wm j*B l}֔Z":W>_I*{3ZUKn^! k߼’IHJKU%Hy      Lg2<@mt {NEu9Тere"}◰{{&vͨ$tE=+R/u P(myֲQ;?w_D j.a۷3y9a*>)˱`m9-y|ٽ:ϪX p~(Sp;jE 4 +ƻAy+i )35yudQ   @5}$O       D UYQ) n]Ęa1p'q&Q׊x끮=laMnA@p;Y08c0z0 aiWa:sa,wjF25vɰ$ePs ٝ˯bï|KbDv3h]8={tbSX5~$RB',;ӥv8ҩLki݉ph3jB$@$`M@zf+ h LEHKG:kClc14m$  JpgB;ݜL6V:lwC!P?bTuαNP׹'%Keap22\3Yҫ;6oƧbZp|#^߻ʳJNyD>GlO*(26AGw$wJW YBĂ%`i@gX,|аÍ̥K*vߋPJ.BΨ4]D##ԡ>37~qKZVHH4,B*=|͚1__1!;JD|)Džm!Avչ} g='K={WLH<4l$? ^ND:T$@0aDž=@Ò{HH-zm>?͆z*N )ӹ¨X)oKD xOߌ{"&"ߩ-nS/ql>lMJd#KѫZ,]stջuv /[|#?R1 ߮M@};Q.%Eqk8ZȈ[Npދofm*ω^9S9@!u&~~ K|<8Cy8ku6 K6\6RMe\6LmquWŋfa >%dDʖl$ep阴.[V($ME SH"G_orXf&   o7sP@H4` r,4K(Cd6ʄ0|atB?,HITPCPOCĢ+e"ylxuщkq0OV^Ƀie۳4b3ZcGz׮ǹV?cˬ)7:jύ LbH[f@ُ_ỎeF=*>ƌ[fiyH[ k \3_@^Rus^/mS/O1[0~gzM[&'5U|{] ϸO1wK '&Y/~INdWLg2˫k\ZBe:3|yw=9Kc×*^Wѐ?=W)b5l MQlg.`D<:tpX)3Qr'~K.ǜMI{?&e+C@t;aeȒތս2R*?HHHHHHH9cNdaoWu[˰_!e==^ <LI@GSǍC7m0}LUťb"j?c Fuu& p_} \;oS VF!ALt¬B̢6 C$S8s31Lڀ ?g2}2MD=beq],UPU38<-cC^z0h"jT* ՜Β<+܋xͨgV&[J1xsm<:^VL=#r˗fy7˳j͝+꓌OF%(|)oŏS`lK.AjU0sin[ηhՉUsJr=;Tax&L€=1v\>^yYҸiCjp 7Ű$E.6vvU;O.$aHeE4R܊TaV-Az"vDLMsXa: s)}6ܧ}uHHHHHH7`ikKvrxw`BZLZ_#45eh+V,D/@qI•XZ)w9yK"I?%eHHT_@&h*>#Gy1{T|Gk)>6#;~zJ@Xcv K xܼRԖ׬Ǹ({ͮ,ۄ۬WJLh#B>~OSBH ^sb,->B0k[?9)٢>= oxR=-nSͼ/u53G{H[f6m]qq%(ܮ8cǧ>&x8ߨo䥣nOwvU a.m&8x@~FOьJjxaiH0R UOtmK̰"֯'ω5~;X}7{XT}`.C2,y*I. y% ܓ =Ira2~WU+ku{0(ڙZciגFJAK[tIL@u@Ꮣi&Tͺ CvyؕWV瓗`H7ǞRhcwpU+}~gE@t @'ݎob X;Ai?ŇbԅˬVƔAO-_y kXYx1dҪ}3Rτq  W~a\lë>];sW#?E CS!tA@{Rg9HqU43L%%bV[ϹKy B-pK@wB)038w\.7EvbKmіKuW'FUGXOOL-~z2m'urԫ6=a\?6R-_cObTlbڋ8(z0-Fҗ b3 ҷm@jlԦtm`;:姳tp/RI8nV!$HTO/ҾR o|0|q37 @#"OBw!{`ÈߙjLĕ* ~^ۉwR5*m@`SVTX)\ ^&DwaBVw.pYT ( " bZjjVvVK]+쯕^*uM,ռa%Q!BEAK" x933gssf׹74Ō *7`di (O4G3WI`Z4E&] `wB.-A$qgrL9m8HĐ=M$067f kZ+fGlPd|1 ȓ_3 >vu\pd-MM`qiеK*m.J#='E)Z<,HCڗLtU--ӱij.#0eXt"_ƕƒ9 e@wv5/_ʃz¾ |Z 4n1Z|vy%Zc>քJ=ÆGȂqxr)oиj=4<>QuޒQ^ҊqWleX]oұ+ضD*u-UTYӄ2CQ|Z PWbabو!K$K&NGP)p4,5Pi^Fw,^?64yPfat*z' VP#`L лRضHOH Si݊g+m) >oL 0&#7&{ |ړJvnҘ +1n_5g(5yކ@%}YOG& )^Ւ(by|'wJS A C;٪ͫ M6_Z)+T7zV.AF-/XLᵑJQd@x+cԵb[LlZSh&rKIao`,[9A }mP[K~VH!]ϒb=aWHV2 E[TZny/bբ[H޲9rV#T~zfAzx_H=܋e{B%=8S$36]* >Q%X_)G?`L.0~>JA,* a==Kו 0&@EoR/ :+cZʴzv+^+{ CК[E5EׇqKڊnT3-vgo- UwC=Ehb?gYW'%Ӊ&j: ?ی܌bkQOrH;XuGZ0)ueEc _ktYWZk']R9ÞʹT&//8UtvA1k6AK.;F:u&!l-|(ԅQV=0ou5Ҝ3ضv\ 7 ]|dɛTSi=w A ›*Kk푱@L 6:B]Z}pc rΨRkԀ4# ;verPt=k&@E~GgtZJ"; Pz98y1q%_Kw6@KD[4a9Έ|2pS`%+¬ k$^b6a:z7L 0&P }r@j9qrv>u~3&p$`jJ!)( (aMiuXvIU;cdM3Nm$ 9p2zn ڿ)ߛĕLjRcl4Q?V|lQIHPh/ŬG"OيKs_QO37z-2`y/<39V[jY5e '-uT 7Յ V-mJ(=SNt[tuâ_JӰ2NM*1/m+öıwJtY$XRV"/bb[!b%>τy2SVw*iN- 5@If遰s),6ƴ)"R~6v.=,TpM&p0 Ѻ^bQN'eNƻC&{ tJ&{z$t>{'97|o'qZ<:IQ,9ъJzB+V3>좟- ЦKMTjPf `L 07~>DZߟ -ML 0&G-qo׾x}a!ن(HN)c< ~0a&7fmC`T#V&{[1}) COI':6: -Y!MH.GNŠ>>xdqC_$uњJ@esX&`:3㍥1tؙn\[`L T2W}l!r_(i'A'OWٰ`L 0Jxc _'7}y>/D&>hc?XCێ^vB'f o՞~M2WvTC\dSBݩopJL9NѶc'1ͥ+_]c29Hʖy|<`[3U`[I*CaƳ#Pf36b*+Ϣzڊ}?o襴t>_iԖJEH#7p&#؝Ҩ DEEpwGi\)w^OqV9>Twa3D ' ; FF~la ֤Q2[,mČk1d؇E#Lj~٧j9`L 0 `Z?G~gm:}hfhN`'{˽*({2&̌7A~v{=y:`RqulOzkq3Jvϧ2 }QGLW $V,:@܎A8b |j2NW=c2v$tM;D@MF'-ۻ?F}5\C3ϱXe#t9wp)9sL[r9IM^G+gm<⊥?TuM3E++Bn} 5 y"r8a^ T7"}$_ϻ3IgbfFmts?bƸL?n0(7'S6h!^{Sz?ϤguT9헽U3S1cL 0&P̾_ZLgyY B]% b2b&~~S''7y+mՀ|99[֜8}L{.܇盟hV{yF}=qϔCO?g_WV> #f9OlYrNlǢw?CyITÐ J//O*u9W{{"hNuO7۩Eu'Fҙۇn:GUtm\9o9? AR|zv1]S\F07dWJȞ4AV1T|ViR@GC.fY/ϗ|G++54 uBr{NݸbΙ5!+0(=/LUЊ Ǟ5.F6D R@+ɧsYjYJI{9f )n+D9sl_]EEILW _iaS%RmvbKo bJjmϛww\sw@ m 5U,/Phǯ;WY@Un0w%N T!\,oHg}&GeÇc=* JL8ޞ7J7y;zOK >. خ೷NW׋sj塥ba?r8mZG1|4ce?c,+z#,$XR̮($XUx vD%!xs`fFJk X"f/$eX}N4.&̾_kJhF6I KV$'G7& Гa{ \\`&}P9Ő(C5Ղo+/߄g;k1 mMƻ[B_>o2׍=@A'95+4S6͡o ^VOMǔ1/lm3[n$ʷst0e2\tڛ vįʴq4k6$I#G0!2}L/܁;D{ ꫙-K.|eW&*Q;+i놡W>(&+R|iϪ! z+RL;$o;#+5?q2D~ $ f/L,MAw0h46?e$GYyA AX({F`/OxqC.>7C:LuK GCʪ1#G+2ˮעkgPfʙ{)>Q|m'wa߰"VnUI^mDK&s)vJ/0ohoO7=cPzP*Q_lhێw MljF,7{&o5}豃1hsXq;f 2*ӄEH}NJ=+^PkE{>Ǹ=>+A7n\qCc0h`篤UOiB^/_IR5̿hX^?\2Oh.͜UP^XltÚξ7nGD;,}J*5}cX~%|0ڦW%b<Z m~MtO|)p֏ cƕ-LCGed3S]*z_RU v(;vL7^H}%Ε"y֧0T g4Ln Q8$MMWFj+zRc/an+3rM+1\M: {ǿhPIw#NJ"ÊXZ8Ƭ M \HXjV]srI؏vۊP JԄz0&ZS?~< 'J烂iNMJfbJ<=n*+r~³='Iՠ8Tף=IźhxM)j`ͨQ0;Y߃V[9w.Pz#u,JrJ,iFKD*GM+^Di6DaTrVL_;m,a/ljEp14 li}}篛m`&gfsmڪZs.ka=x7YRokXZϾ 2U՛ Ga;;S]>CJrNl+P 6.;WAzK9/k%aʖfl`Wpxha`~0Cձml\JB&ܸϵ]=N6Dh+\kL 0&+D`iQqUY0^9 jsim~RB* GFR^Z߷Sϣے1: kallpl7 JPi۲=VAAic"oY/}VsYrS:PZ-3{,a2z|{dF{ 0uPHJhxǗlR7di䦍71vc̶Egɶb7Kg6xǝ- HP4 nl2#}BG1=;XرSY LBu7nLMgl+0JV B}\jQ`L& ךPƚpL<ɘ#:jYpCRLaMHVo=ow 2/޼Gg݇,l(ju^aX_0~Q5cW' s yB[%bݒRBÄbuڶ"OOnA žGIXu.KDT߂GН. v>E%MEAى 0&"XrwZuyup5@(s.=&yc[~aM)s+ݳ;Rm e 3XǮmP>cL 0&tj&QL8 qU/؂}U }XW,u/f͍T7@ڌDBhɘZV,*L9Ol"C0R'`̓252fhOc+s(BC4 C#&Y8:6m.̅{;!ޖ !BZ'#Ep6UznbՏT?Q$uxbq 0& 0&A t|NosQP\"c5ҬVoڀqƁੴH r PL+qA;#Vk`L 0H&_kBkc:7P|鯢`EѺoB4Bx[mqu?cK'̔M kIex_|O!6R'XZ"DSW(p0Eqy30|^F2hS g\ T,m1ܝ+N5v)^/Lw%YOGRO]ՒPI,텺 +N`OYhg^,k&*)`*,(@y<|#L9w~܄{C5o't#x|~S:( Hg.9Alk47'wb֫+EuoN»cr\sږ;Z1Aj㳑L$d:LBcgcGM@ݱnl)^Z!d[5id'jk2?? 7@j] Noϯw(;0&`&`L 0&`L@ʿ`U4|x6*p~RxbcD ,N)W9ik,|Rx i"2j F+{ m1yy0R˥}t‚\Ee ۶=TyZD>< ,vp997f^ 0&^T73ג 0&`L 0&5 \=s.Gy^3aϦ|EoG*٘gXdmNȩ|rO${q׆P%; "*duQb%S(ԟo]zGlW*yjE(zRϳ9SzPYF}I.m  +92&@]%zL 0&`L 0&P(›rjﳥ3B^zp0Z#b9Qu뉭X+?C&d64PU}a]W#X oϧ7v? ]XW=,. Tv O`l7"Er,LXeO5b&u)k&oZOZRkyOѩ]fyyX.ϒvI61cgsh-FM&=I'^´I"`E[_M}bn M6a6Op,]eqGPoMGM.|cƒ70&|ߏOĉoA:g--Vԣ3"0K#us͙@uHut`\-U>GGCo7umaexy3LLv42d$AzIC4* Ua**ɟջ1JsK >VX/ \TG@ j.S #3.塰Ty#$,>׈WNfRs.\B%+ɚLD8 `& (Ǒb­B[UkӉ5yN 0&`L 0&\=}gI1Ӭ:Mm!Pd4gS4{?HjlY<|#~FShgL 0&P 3&`L 0&`L 0&`L T *ƉC1&`L 0&`L 0&`LpPdjK=:0"0K#us͙@uHut`~rK ׅuvm 0FW,մ+eL 0&`L 0&`L 0&M"bT63󂘐_I3x)Gd?@רPp*S[,,s R{9շ2c<`DwTMs2L 0&`L 0& $P//),3x== ,!J ~HlH @Q5kF,Etn,XŲUlK'8x_كoAk:eXwW w> $&8a=w+ ˨%75Lv.LJ_tIKO< !c0y3a. /Xmmf1ؑ 0& %'`L 0&`L&(<Щ}O2-6씄~6s!C_||P*E>8s+(p~YcV8)]hni_?6 غ ?vFc~6M1Atl\G)]Kو [$Qʫwѩ׳ci#_V]YgTG^-r+v^jD/`L \5Cp&`L 0&`LF(؇_>ID O>֜tqO#!B/pRC(ƊBAr>>Ъl v#y!X( f++9+Mv(+-Yضjlaа5K1Z_IHݑЮ]uͪn/n絫_)3&@U L 0&`L 0&naeIۜur aQBݜSU_-iR뽪`/GH?U$VX˹ՑGCl/QXVh*fK>W7@DAtQdۅeķɹUK|d<O{vͪ%n焵R8Q&NՑ4`L 0&`L Lִ};o^_p}EruqT%.D.^a&-fMC1JQc?m?ЪˇC}z:E)oCf}W8򨤸n~waP(ԧ';]rX&z§zK(/+¡MGEm |VBeڂpK ]|u |`֟:Q]z͜p;60Μ 0&88e&`L 0&`LJڈwQ&:fhbshNpy* دc%)f-hmǤy\ÖyJZy\fרvr\aݵMG .ZqJDݺމ&ϴHGbB2e?S~ɔߥ\!!ԵF\/f\C{}AD"{-*Dkc'*zuA&39vٰ*uǃ"TU76j 6Dr'bc?anmQw`2) ĭ}'ywy]?I`P,]\Xjiܤ?L[e1$m˒+MBtJ<]0>)_\r͚xLˉWO8VI?7~^Ɯj&`=OJ[zw7v|wm@\{&nWaߍ\mSY|@(,E)TǡGd1= JG{cmPvI0NKrB[rV͌c9;oхVcy -FvEU`$^^ʹhY)ԓ6ݬX&ТVb\ $U}׬݂A/JmZJ?cӼu]wRlZ9z6ţӓǘ HdJYnyv.LJ_*_-7Bzl@*ϷIyI q!}0s}4cn4?/AiWU&P4S8: X5Cu$1qOOC(kPkWrV̌[k+~L~bZ%g?}$_~=t/b/cuPf4қjLͼ >4Sf .聣лC ~sgOm8ڰÇkINPIۙ`L X6[ .֬4JROZa3qIvfL 0@^Kesslgіє^ZtV^!~o6u0M)rme+ηҀK}_Ry(.ڀb~!tZM37ƆcJr-D r{(i';PjGcPvydR;hcoòtFh~*oW EZq\d41ZGUkX3-[-nbMMcUy M+\9_ŁT _.WL 8B@z0ڑӻaNaiW0T~&nSiB%yc8JĻ΅+Qkg76ScU[0Tn&T2}qWX"uþ=1`غğsNTڕ $ {xeH3UtT3h#Ҁ0te15B!-=xkagW&` `L3\܂EX)͢gUy#xv (jvNU5q$T]"GB{2Mq7R', 6]f֜1,y#Q5gF siU]ѻen ӺQlpI+ÑMPWPz9yjP/Z_Qj.?scع|X7@P-K+ĕ^%Ӗ\tqN`ڙco+TMƜ[{s6m<2Yq< ?:S)-]JJ|(";,ǒ k-`P^EHK:F-ٯu;ҳǙ}k_X*v %ܻ޽Dzƾ>hFA?_kv~9uN|N4n!=TF8n_/Ьƴ #4PZn3C!\L--dSzr߇fg_FH+ԤqHj,WH~%>j͚[ #U{pfXuV7p3"B*F̋/B?jPyK.^~!d" H}JH:6'At.D.JFw",#a6AD,,Ro/UpW<2QtW}$V(MoP5t)oH-n0qSSJJ5BbQ6M"Ht:ɣM%7yVoMb~6&)'wGPI zo?D`)|wܣ!mqF 6z@[ղa8< X;VrrL*ٱn<+ ޓ!Nc+=B?H01xD*I}q+AoRkzV#K2ʹ.d92i)'${1K T>|BmZwĩBc ⇵+_ bKcqWn&G:vWrhX=b쐻 u[[_9mIi6mmϙZ{KY6v}>Vƪ,W#7?mV/YSFU[Ӱ3}4'>fOMA3;~) ~ƫ^|}݌#}3[g: gvmb6i;&n>>\ܣ2kvvNiSk;UhfwQx3/!'WH}`)j-9fOW 0s͔:ԣ6ffvPZ;7^ɹOW b)Xچ>Sq6v;_*ۏdG'ޛI3`1'̦t|(_t,iQДsf^&nʋ^9ɇp6<iE7&] ^;Bl) faüEh:w" Ԁ(YB^ }p'PBN&jOS#S2+6 -ѝE.SM0ЀW,@Hu@Y>P{ Ǐ"SזEFi|YJ _~=UqGfqϝz]╿o2l!0î )ei&ThN]Jϖ,gPWPe_E!QJ!=jڈӍXھvUBZyPrx1s?!Z҇c^F#t?l®4WHKڒ]dOY'D\n`@,4l:& s@zʶ)y QԤz&nx&'A? }o)unJW07*^(nK,?~;o:_>m0q\w49&z3u#nEhY^"?sfIx N=C= 3 3\u /Mαn22\#v+oMКSAx3WNǬD5ۊtN3y%mi3Sϣuyim4>[H}/UTЁK2~(+ da۪* q47 T2 9I|`.-reףmm$|c4"AZ(0BV-:}&M}Gf!YصcZcbe'G*EӬW=~|ym 7i [UݵncdK:=>wAŐ]9͚֡Pm:QQe1G%zub~yTveH?W}xmSoOANGqJkJ~ipyֿMZKH=:6U![1ӇѦ(Π2+91'=8AmmN?ϖ#KO-3g &P4կ6%p7ChG0iX\<^ɛe}|LoiaèGAO/CH8.Y_]RX(*8NۇFI+BT( mj|̢A"~ǝ*ƿ*F{376ѳ7s$?A3m|U: $]/ت`8حM u>ڟ"J+ҭx.MU\ܥgn{K`Lf8@c G(? G@1&aP2gXI!{O}hMbw""5wG~t'/Σ}︄U =@f.uT,/Zt8 iPɨЕmw P m1@ EsPi*{̕,*Vl?+er{>pH3[sҥ1Dar7YQ( 3װ8JyD{+d q4n%Ȑ6!ڴc69׮EH9,<$PA}f^|i܅eDl@"PvD PIq#HCM8ghw<194ISwPI #Ɗ3ANUQ^Р͓d%#pP)WvfH~@ ?B6iT~8PIN:pMZ߾ 1xnPIG!A,>%I$yDٳ&r |#vjn{5'al_+{EP35_Q'I$8n}߷І6B%)RY y(t*\Hmܿ0|%AM6TV\կţp*J_YVJwC5!9`:N?hYVmf߯mJB$TR# >@-)zL 0&PG e6 yyg0&-1d2 `f-V#_-:R%R5~3ZdZQ4Gɖ0Voq{Wyrq_P6-ijh/'9B-Xܵ3[NʍO˹2mEeޖmb@w;5ԫhuV͑AR뿑j Uohzߛcwi pv ضp M+=G21N<5*˻޸O]f&p (Ȼ.NﴩBXiloAdۉhv{r;):wt_X%EwEAȩTr9eRKj^hRSZzt~Se>%+Nmd_ t=rntF.hBTCGthk1Z|WQPW~}lR%aAMV{1䊷1,;Kp.wi/a^*ߏ6- dt^ҢIڌ5NK@pO;DYe 诿G"X Int³;V`QYkq2hc ) 4GO>u|RW}r@j!:G01n? >1[veL 0&P 3&w:8Q6qįV"qG5ʦ PQ1ʧ c/qsu*v|'j.=Hͬ?~[hMA7>g.ec˩?gT@Xuw꫏1p$b!J&-}{[37v[_NveX&zN|_vrjeI9Whe-cFUsQ3i*uuQzi$[Ha[Z,KJs_O-Ҹ>Sx AHyЇV- v*TU1bNѤ_2H$ ʱa5@oʊ }lR#vr68 +u3ӋlQ^-__ri+B^Fȃʮ'xݺ(pB+|4~@fR/5}S{jPf F..xnT SW ? K'^~aF56 %RqrUo6hrhs&qk<_`ϡxrֲ:_3BS¶ Il?lJmdKcɮR;?ERAS&`7eGBI]0}E FR+ pl/1Yw}\I& &Y@` 7[eYؕ*FcQNH^P)R!jCW4l6̚[7u ع yd ( m=)BRcIUbq '[VځF:|C%ۊ!:ҶջSD3 \M^?LwF)yS%RmbTV:` H hUQ?oQ^J̥nɩIh1[BE8n>rK\_ .gvU\顕SVZ,&;ĢM)'e '{½uoSE}>]U  1-ض_t!tJ6n$_VإV2xoƝva t &>^\qt tBhi}S'O:$MCt 2'.E[O/@[&GόϢ1WW4D߽:K'2ۆ Dfa(zjVd<x+r%3ʷVȖ/v/CjuM0{`Uˇ2>`asBi6Cf*Yٙ5B\!fl*HJq&akƍi_HUD8θ@]SUM#Sq#KvaB}E_6Z^FaS݅WE:j˽SR3nBBB$ *(`]jֺWZZO_UۺՅ>Q򗪴  *&DB !!o,wf{o.;f欿,3朓Ew`qWr9̫ t$0N/W`eUuj\v/&_H.3O;&@U'xEf%aOd~}UƲ]<_;6B*#_Ke}D$@$@$p* 8zV=&=ve<+x髍xGce08>#+oX\ wmm ߦIAb>f]Z6I~4=MHV']+cJrM+ סmB"2&Vk?P6zB?(;%Rr|]+e`rl?/؁*: RQihj[u&MyuvYu#\{$eF(JJ)sVu3',q彪 k\#\ #L sX^"}Ͽ2;W^ExH4}IXWkd@6Q}0GHϭ+>-5mܨ9֔SBlkXwVKSK Vwi'H~ӤΖzֺ[Fm|TKZ%!:i$Kǔ)Zb{&Gh6xHnm{k+ ^'5FdZΧS~}rsg&󵻽8Q=kkM.4Fpuxߥ~+C CO   OoVa1bX_q1&iz ƙg+w?Vm> ϼQd"Ѹ񣩹Zx'趖}8,{aDT HCY,auA& u6`k"kcӲl04$K_eUN߹"ꊧ.[lԋ)FLٻK`l=|%ZGMXd7s߽ԽKu\eՉwJ۠춞ה.3 uHctB\> eӆU `@2 5זbK3Eٶ)5 GGա e58D:SIg H.Rp-һ O߷\Zn詇#eƅ%8x%$>x% \/(]*}ao ٗrMsNW߬9/a(y!SHo]$~`KSڶxxŁ/.}R6Lhyq4s\Ib i܆V_cqwkJ<_0+P-/b,Eɮh;C+I_zНN 5 NY?E.O|f-x>"KMgLʞxBW]^yzH2VcMyd݇t J ,}xվعe#v|lT/_*љ#F"]y9= kvhaG#Y=U}L||,2;˱aE!8l(?h9>Pnby^LR}YJ`ŲO-|i2v q ԡd-(dc6P}&,0}䛞Hן-Qh tXۚoO8nVZ#DFe,'w&<)%e"\[PV^tG4bW>+r,b~Fx^Q|.H%e2]pm.T9e ÐJHZW!Gc*;L=B~#e\8>yHWk(ߍi2uϩ WFbAwy7(Ytw%Ov@§P`7akұǬ昦nnMI ~1n̓"E 㯹{vغNT^4 .X/*c n`|K݊p皾0nsw=)GN5ip5Kji(喧Xv+rx'y˓ ŠNB,Wc$LGt3#B{4A_v+&}!mE.,|7P%ɊaI E%zh;ɸqʴy$Uؽj=^v!VƜj,t ݶ8JO%\ ҧtknEPǔ]IחrE8Y9ep1,=_2#so 'Gv7]c:9N}}oT>.]*,#g>2,)L',sw?Zjs.S T4%omD5~P~K(o>՟L1j<ϔ.!Y~P۲<_SnsF8eDEx_|>^*#8zǫAZcq4bó/bIGfuzt쉷w-ݽXW!x}C^g!|dI4i&tzmE1"kXz5ڸ"N\\86>&ސ|i~e|HȞz) }5& ?rׇ+ʑ1U[] ST)>Ə%ƳhT2V}BtY }k1i:זd!lo ~4;,&hY6*IyKLhS% V?Mq0}$L17=8{Q]ٷgKE0k\9vY7 w\4a?x3fڻcgLǤɓ0皛^!嚬:5O\ ]H? 8dI! eo%`N+$}a".}U;)^uN=_eח8]HD/ۣ6c<ˑC,PށXr; Fl^Qs?@IDATI; z|ۨTtٵW᪋ JWϟ̡Y_mTJ(Ƶq*jz_壌X2/B"mT2ꮟM7j݌wm@ '^q&Fȣx0*eaqwPkD"YJaTJ<?y>y%`ԯm Ve(?"0>m2Ni2*]t-{҈E j3Xt>$05p w W5˜FQl>[iF%1ըd| CŕU5D ,UŰ!yCO K'c ,%F@6~_ $ M?,Ϻe\{Y~ut[1r\,J=?Ǿ)ƞ6ᖥl}M }5R%%d!3HHUqSYU(*&)ќ>3=,߬ĵD;aqLxE$@$@ c :*2.鲱~n1_؋59QH8=5$ Y]nPhl/Ǿ>>goUѡ Ȓ[_k5Rl);F|Jƞs 24e,uvVl)=T-6+ͺc'3gȥ>^ʒoI}]'O)B$@$ z6uGԙHԱqP}ۀ&e1Yw:.?xG_zȢ}Ύt\10jx}ZE>#99Nà}36 tQdՃjuΪ5xfIKy#wttF{wӑJTk o݄Nn +K#*_Ҽ-,9Nh8_i$̝=;:~fXM Q2CЮAz OT*K Ȁ{LF2}P'{R4Vzhˆ+h2(F .9EY8q`3R^C+.aZWr))#o?q@fF^Wl~q4( C'bb=?QlDɛ3fYR+\2@/j"ЁHH| @w%PfjKLOm̽pT/Gz5!3LD\f3d#%I'VniYw=22qV2.w6`i3jWaDMXBlI(_,<eݛ(QIR}}QIlk&\P!W v'!KhA$@$@]@Ϯ u!      h'xmjZ`]+Ѐյe0b0Ei"6[ɖ]V=m#nE4drzw[Ɂܱv-;)LGd6|Lʷbը5r\>cո "0Y2&_\5H|՚Qi0.r^b'|4H=XUHHHHHHHM`7];(.n}]| hY7\=RR?Ƴ%m -bAIU 8t¥%[t4Q!}eB:k AA] Bq,X.¡6R%w?Ue8XgDS"#Ձ/E{<-zdɞp)\qW!fgŇxG(03 tQE j @j7rh:Fe3-<لϟ|!ZBd8j} \l{6ԡl~Է2(5b.Ǹ( S!Q}WQS$M5_/wg*E,ސ t3fTHHHHHH&F|/X$7t<߳ZMŭއ\} :quVE_1rΜx= qna,~ ڄԖW1| TRp񵘴1U %9YHn<ʃU=Mfui$|=wNO.ۂ}s+pl~` qbVTj30l..k5[M{eI$`k.B\YǞNM'hTKj=ftcNeǧz K]"kX,x[`m`exHH[nYTHHHHHH+зd_8l{ȴ47 Dd/zr;gKP7LѢGaT,3r;%8qCwbJBȯfkZOSF z=zIKFW[x}uػ|Ũ72-2]VF Dg!ٳI!D! V!mtVQ)W/1-$@$@ݚ@gdiin.YٝHu!ii};m'           NBcR';h1?hh8=Y,8xC$@$ >O"|/ExK<3 NODHH^BHHHHHHHHHHH::HAP            hX%DHHHHHHHHHHHHa            N^BHHHHHHHHHHH::HAP            hX%DHHHHHHHHHHHHa            N^BHHHHHHHHHHH:"       cТph1 CMlw]][w`D)ƥb^ԏH:7>:wQz 8)öO"2)?0rtYEhiuJOENzHF+PeQ QEQQA [.z|BKçqHK̔.X"aDr(p'8QyA)E{K#Ɗ϶c:T89\m_J ;#oL$@Wȑy tX&ܬ .DwOc?֋'vޓ 4VF#S:,%Ο[?ӥ|U%E_\Q1q\yF-I7;N4mFp:7~g{h\[]S)i:h, ]/:y993hd vnW$e Oclזr=^^e0Hg|hk/&Io wZI5eX#+AoVc2jx.ڂ}!aW~g!)5v-L>$;lmC}WjWugE7  LHHHD ^rSζF[f٨&ЩTw?aƶ>%| 3SI = O%M$@\zH: t6f!業|(Y'sY졩K8\ֺXJێCdO&wyHk$ [wO7SdXkHfyqG:9? o='-}l9 ׂLs]-nW'x-9Rʻg}0v7H> ߚ{QZgD">>y9ٲog*1w ;\p>:t & ؃Jyged)&,KIO?݁ ղKː}ԁd׮^ }0cR쭽xO$@݃UИZ y)I=.<" t^ '=fϞaTjK%=mls&g7 7 WLʶ,7u8{4݊[~OBʺ%xnYT$R~ng PN>F#ccTf4dA}ߦ/W5 #3}Rez`v<8'|y)*/Mr⻳\{5Tz.v_.WVStߥOACƭk36ߋCbXϑ2@vG3/l@^r`1~¾+6ʁo#V|~X)sܒW ^hEZzBG韍Q?=́[8SDu&#~\hFmމj<xkзp%~:[)qdn"0GaWV' '=7]K4yz\^ ϰTOwVm;IlZ1^;ce) F㏳rMi0^nCy\n?Oe!{놥0 W+gIa&^Y16#M$r[=iT,2QI܈]ʫ2%yhX{P捣w?o7MmiZw4OEZ?ëʾ[ռ/;DDuF[oWC'F4Q'~rfJ_CqeWxh9}XsY܍T^-c یJ:e6`Kĩ9ܪ}ma6HI 3ͨdy>ާV0Gm;T&= y}ڼ縌JEnTPn܄߭;0%$yp`U6ga[Hm–kKF%M#ٌJ6gIHP_+r*L$@XΠ,z쩲<*=9y3ILGlY>}\ލM:oC]6rӾxlfi$m -~̞4#U^ uӕ%C=2[#Qq䏒d+Eߣ2?ӵ4Qks3}3o]+g"0~v,-ryKFA: u]o7KɃGd}9|+m4 I_?\5ח]z7w_ =c^=cn Y xnt(t<Δ(>N2<Xmox3 ̺Owh1}e'k{Ybɜd&a͓o-Gm^gzE.h}eք*<}ʰl ;/ʴIQb| VR5b_OC]>s[>ʬ'&c;Z/IoWM>(F\F9>УėyV8fݚC#ǻ3⚑q`Zp>"_%;q+sM@­vpW2sDCRM%ϣ(L{}P}c7X.3\;k-Hlr{/=^e,}ܘ67BUnmp)Iу1:{"M(lyk)^A9:z/:YhyԃL$@݂@1,9Oھ,K%E'\$N"pl~rKXf`!ph W^@$pJ2k.E'Ep/^ޮwni =3qAN}g ڥn#M0_$ekbWi֕pfځW9,T 3ĩO^HWfiɱRt=%]7{~m>.{MQ5eGqx@!*K|  Qt^qnۧe5p! (p4}|^;]G#~s2$/ޒS#L$lr,huGXCg]F><|i8‡x-ϗqc_ӲƫӮ e[T9CbY^j#6Ss8OOk6*)9u}i1+} GgʵQ pP-zug['wWyN?O>orܾnN?=X]Y ~K7CK uz߈~5F1J9աg B?+G=p$C?ُA5kq`_r%ic(~!`|C8-A^Kk.EZqBn;w> 1,})ݸeґP DZx`nanܴ}F噙+gW꫱1+iyK=-w IՓU`>4T_[BOٕw΢)(Ixs]/(?v#'nt\:Hɻ OfP}W0H˫z>u'~L3O?o //ȗ%t#F 6б1d }Bve(LGk*He$_ K;5|g̒Y1֘mm>m]B6s;3"|{²dZ=]s@K!~VS{~'~ǚr wX?:R1QVX!Ǖ0clG2]yX ~9b2?(#Fs@[ܤOJ9:PsWvOE1X\5bcqxe0uǔf]k`:f̶%/M\WL&.z$=OLegu7XWZdoYNWߒvexUۍ-9$-Esa.G:q7pm'ŠHǵ7q~ E'$HI "if| ׎KS{+Vֹ:0gL_W}>Q}40 ٻ[rT%WJ+k+[.C8Nz,2k ߚҷwbh&Db$NjuZ yHǜ<>߭(;q]~NT}KR` vb]͉.ހbm艚ҽ8ш-'08S.yX Q]/og~ނ(LB{͘F[i/x ȧўbtbP ZWuW$@$ X_;taeZ )$J[ۃ/ tHǙ2(%N'їb&.ЂFoʽ[2xIWl<4/Lu[Yϒvg%r n~ӵ}TYd]*v9JnZЩ` kƕ+X~g$}iar9U%%~xٸ IK3[dt-"jOyAF3Kb) ']I>%ik0q[j- (y==<2v^*:7_XK;K>cc=r$N¨DvYYtb"dcTW{6ED4s|ãg*f<}.VJk1`ZYE8Kps_ *;{` Qى׍+܉˕%o7vm0`?rZ@`8)[R.KیaјPwu1FzT474&#M2D}i޺ CYv}MRim qx+جi>3?:шqe==Foه y._4:K+I}xiR>"Pܝbmϻp`>t|,˞o}ThOSYaC\ڠo'z0Wӻ$@$MtRO衬d9v6pF88nI7$ЭEE4[`Yy |aopmR+nCZjkʽ] 6l#Jj6fY?][T5OFʅ-"=!Wf\d N+Z6~dF|OK{c'!K2XҎ쁓RG!~lZFq"}bQҰam][dh+p=8O-NfG3녳Ǧ}Òmf\i@{D܊=4 uEN6#?YK{{#^|upk^jcz ?m{ iȈ-]0ϣ\\ |u mfGca^[r[ 2z8q) cEk۠NvWW= ϳU8XYxVY. M^ 7ڤ4/IHhP@IH@@VO lEc$Y DlB6xef1P[GC}ɱznŰL\C,e:zo[g )j/*ˡzK ^1SҊ9q'. Ra~̜eR3tNTנ23+MvRCyCF8*iuMKEԪΊYUmyFX*5~=w ?`78z TFJBھSgt;em-b٧5;!:n_b+~6Wv }crxJ#sHRs1^ Kr;KFJ5,k?;wi3ۼgX?."Y6 Ɇ^0c|[hmC/~j*4]qp˻ppQ>$\]”z`>:0.,d0H>ݾaۓi?2Zuk{UGO?ekl5!i#=0|,%tsP^/_Jzy_i>Z?-{OIU{\czwY#6Vۮ)# 8$@3V/k2t)m]ayyW21F;y5gDP/dOs XJ~K1Q9[8vH3":cƏ+7Guwzn݇=dOǠD~y nb0>3Y?&f=118-B7G4wczbL$T[Mg .uz.9>%, 3egeMmu qۥO[#ʫ^fjO-ܺeiM|As] zELi}>֩xQz粲>lz-CJ~#zV|U9Xoq+w4%Nڄ7<DbHUe'\!ZfʏXEF"@96._{K4kh_=I'隭xl°~At96"KHjc4)KfO01}/KM=42(^L@drGW22]&#ns A ߝ1?Z^r,^L!lQQ9XbهO9? Gu0 :uGo' C3XCIEig QpهxO>p@dGck v;qeJ]KpE8M#S!  tÒګ!cI{;mZeQov?e9}Ӷ%nO@o?c._D|KeUT4ke tv\mL6J #Nܻcρ;Cz)i)F(5: //3JEKwVR ʵ詴u;ʩC檍nUaMQNٛNS%b!\g`8V *}P/W0x=x}=N)yۄ^rE`LyoqM+Ǝm2}xь6xc{p|."EzK އ=/]?s_n_UfzPۄu׮g}ѥH6n^.FjG$@@O5/Qaݩi@''3X9im|>'3`&"GU,Y6x9x;c&Eޔ\9w~1mfam;#@IDATsęϰ|P`N4g後ru è}IfYnvJ ECOU9c0Gd9bpps# AF;7{9\=ܺ<"~/?~^z#-y;bp3Fj>54ۨ;޲ [t}ؓNꝆcP})V~shvٽ˗)>o^gQIև 9δ&~ J]+?Kږ7nb,\,b<½%N@ehcőga+8۽6'ۆvΤ<jw 16x">m2R&șe ?27~\K-Xo~cG{˟K=NB˹ˈՖK k:=e`'Nr=dxH1D9+pvl$ARk| cDDEO׽>@;UOS|"/{㿤NoǭCr#pT!՗"%O^L''pV I$@$@@ KL%Ũг%ѸH2#F瓅%& HB>tW_֋O4<Ǔ ]֋$`j$8?bH  jX\+@r\# R\^*O0HH>O|Q?zޒ'O /F`8+$  N ظ[#ZH;~:o=1.F(! o'>՗"%O^L''pV I$@$@@X KKG\ؒ՝$@$@$@$@$@$@$@$@$@$@$@$@@X KQ9Ó;XWQp@vv td|/ϧ^ɓ[VmBJc19#?G^yw-]b1qD.}/<>.Rhqm֗cR *amn3n}*K>Ɨ[Gg~քk==غa->pίAl|nƕX +‹qu?aa5+s2<5&ʵH%-r۰{ x:{}>.Ihwއl[b\. 0t~1њJKRC5ɽR&, 9DѺ^x X>*lrF@ gRp&$'^IRܱW'xܾޯrx&Z}CgM~ׄv$t%Q) ?bq)n5X<r{VϺXx(q)=xsp=QtΙHzыs|q.i-`5jŗe=jO_UwuE_ը7HC2kI3X7:sg5pͳxxw5"ujj8l^+ʑs-~mcPӬ3̜DӞXxXu9jF%-D1I7WWaV>W!?s޼&ޅ#VSU&1ǧew7hj| -@\`LTh5&i)ǦMV"; GV匈GVCl]e6ddΘ?{_I#*8!VDXU%-ml]TJZTT[ڲhQT,*mVm5F#KCm%%$H!ᄳ?{=sKNNryK|;{{gQ179y@G\_ Oc!([OS #x9gPTBo5D=#}WS5|Zᡙ"&H\ݕPyWsL 0&`LF;WJȅaL T3ӥDM6S:U Eux4 Oq pb(xC?Y6RFxix*[dRhv3eɲl|LE" 6*[KⲀ_,W sJet5]R=|Rkѯ{o # dW='pL .%1e ޚnGaRZ8lN!>{#%gi"+@NX!],'LuT|1KN'1(Ao#) [͟5 ڨ㥭L'Y"׹C3IX?1^~Җϴx> "td42| +FZ>1;6އt"qmyWsL 0&`LF=jD3p!N|}Eϕy_&>MRx D٣f[x!tt,7 { w|R*0l46ûJʹCk@=N9/ۇ :}1vQ.hbbfOp5]">E3 ]Dfs3["妴*A9_&j1}o=̬T=Twy'fA=<l3TT DdxDea82cz"[qxڎRI8{A IG{*a7Jyѓ#I^f1IT{ U߿j~qڂµ3u:8Q¡!}zOqgV*zS ֋17R"oEj/)-`L 0&`w+ v`r{\<)NNjN$`M6']eG!v4D$+ kI̯?CgޫEenKxMU"9 Qr(+[^%sū{uj:K):jPPմ*tLIj0{kxqûٳdЉKf2 'QGU,oy*hOD}'$D#qO7x*>7z{p dbT'v5اw„\|xWq}"BL$=4]6J Jڿ`+r2`ch{mxV + Agٚ~r$"`X KH\EH e5qhL 0&`LjbLyi!:NZd7fzƫYLdai+: L^zM쿜XQ_!6\,'ÕVrnàpt Gn4p\$5 GJ-:KR,-EJj: eƞh ;ν77g`;\플2d3RIAeݠQ IJ׫3X:MYPEgJB>(gF?곩*yyDP=pIIw%^L؉YûDB`I9D|{PU$PlЪ;xZ[xSY#Nn?+'?̳6  0&`L b`LiJѲI:*܋ 0CclfvyN)oDdI?f*Z;SeӁx}(VY>gxNBޗZ;Lw4}\_E=-Vi=2Mݰ%ƝG{C =`j֪#E#tjhm_^&jAoo :"'bv,Փ"#72IڋL%.lpo JH+y%۰dUc=NP|a'ψMZ>kzvIϣb][gϟÅ<nj.h4GI¤$0#G BP+vH\;Y՘ Yc `L 0&4(ΛcL ߯FtY@chƙitC'64XUr4QU*yy#Htz"b'/~;3Ֆ$2E?M1h@hw&yṰY/.w$ޡoc\qKZ=j:U@e<`f..n}Oр^Awgeip_;[ ]6~t(rV6\lgo&Ey)X,?xf `&jw]{(I[ nC^j**6>(_mG| YGh@ώ-*[@m$rs.hϝ43B؉dID~`P :NS<.NVS1}{:yT\+faHh!ȑxSؼvRHH\5Yچ`L 0&+0gL(Ŏקɫ|t!VMlKA+T ,5/Ƭo(F᫮qܥd<,z5R0 "==`fRN+eLZIp𷲭LY=~ל$h]X**YӾ&Šuq=D\&+\(r•9 p;׸U$m/ rvap 1Yڧr~~:I$$`k>x*lMB;n\+Pcw{R ԇ%swi\]*6<{Ι 0&`L eX 3&pg$UWdgXO> 'J4>i΀E/i2&EȜr@BV@!)l.݆̈|9Tp3UGB[ EN 1Ǖ`)TV+<'Bs}AItx /n9Ҝ/K) (=p.H&sFmr^~F5jaiNNfSt#|v$Q͢r_2o|OeiP[Q9 $X'G=lkޓuM(֫\'uvJIʂSe,2ggL 2>z2HiJg+ !:8l2 xv<漒RIBC -BBT^wInƒ7_GgaÌjQp[~xhl (:|;OOWr+gl.`L 0&̣w 3΂ 0&pVA@|-n*szw\pYbiϤ$[bfj(jbT"z}VM7O7g{]:8#$R.QLMĿLg~VM Ǩ,cYg"tꃻχ6vO=:#:WǓ5r$orh$ˡS ɡ;jΕu-aS;Kɘlȱ:EecIXo3 r7ՑV uM HA-y U T s]Q 9 Юaq>X's;1;1>U$= ^@@筃]h_z.)q9 7߻8 w,]jmxN%%cWy5BTp6] dt,4GLU I#9>GgcX߹{Ĭ敓h ՔYѷ֠(SU6ۄrA?p &S*0cx.RIwoΈXi)JW0hjb,*k/)),W+H\%"^*hC{[GQ)9}(ƥYl*\ͽh^U&LlD;3yC̩"q ,)U5OrXLvvp\@UPRle]?/оCeiX֞c?`L hyDHrhRH^wr!J{6G@VzTmR70V'cL 8C1|H}~R]e%e WgQO(z?2U猳aHUL^4́"q֐cYC`L 0&06wsvL 0&`L ".]duUNL 0&`L)Xr GbL 0&`L60ݼ$Mܪaef5A8L 0&`L.`Sxw:g`L 0&!4f-0vS0MwgSsa L 0&`LXTi, 0&`L 0Jb?ZS.Y{ڊK`L 0&«., 0&`L 0&`L 0&`u+Xru`L 0&`L 0&`L 0&P]XT]dY.`L 0&`L 0&`L 0&V,ձ0&`L 0&`L 0&`LU"35(\ZHO-l4.2`LIٍ|S_rږg̳j 4&<8ϊc2&-xRmi).'`L 0&`L 0&`L 0&2K<.g`L 0&`L 0&@#ɹLKL 0&`L 0&`L 0&lV,m{gKPŹ)^<$aGos͙@UHUd9L 0W'C+ss`ur=ם]^T6>`L 0&`L 0&`L 0& `w&`L 0&`L 0&`L 0b)L 0&`L 0&`L 0&`LK`L 0&`L 0&`L 0&"T,`L 0&`L BHEƏGp2;שMIi𹧌zFl_!jßG>2)S)Ϝ‰]_ģo.ǐ1P RF'Ï Ј.g_Т^a?M93n5jd͛ (pZx(8}ߧǑ'{Ⱦ­Lh3M1j[{}<`UD 3RU$Y`L@"P} #eZSaHl{ #Tl(4j=kp\NDwY)%G7qʡ0lfWt;WwC!x[F!톝~ff')3rP0.jo]TIQf?;E\pr=mIv)(CU«R, 0&`L 0&]$`1{LŲZ}4Z3Q@=<$L4+imzS:PzRJ Ixsؼc+֮rIXm΅ fw8پ[WؖUb+Y.tB@vmU0t풘B+/3U k+G_L#vz۴[.clq`L T%*SEbYL 0&`L 0&K<DU|4ڿ(+cr,fQ2î/osKq)VxD,M5n(^|AtyxIP=`ϡpה~x?#fAXTR(+C rPrB!9zth~DX* :cpMlR( Y$Y -0'k0}Y'<:tT*)C0)&P>*xk3%\vuKt~&rXr, 0&`L 0&@#`3שRyug]tx4L 4J u;]olWNQJ >,Ck#ϯ{o9̀OveٜW*oNURvAjPUBw$*:HL`LXraL 0&`L 0&PkݻSR nLi]c<D{Yƫlޚ8QL74rT I-Ѻn{ݼ q:S Q~ŃL.Լ=k#%GPpI",yj ;핑 c!>ߕ*SŒAJf..neI+;W Y $ Ɓ_EJ|<S_5zm[aH̔LX WfjMk9E5qI`LzfGL 0@TTNO">T`L(:]GΡ}Ax"AJ]WL0+#y4h:ΦgjΘWIt%i2W#pމIu& dCxgtjhȢ n^<Х; / ^HHw؀Y u〨Xf4;]  ElQkP.DFXTG T"=B j3Rws~~g`N`Œ5sHgjx|fC? ; 4PlMiJ MCuBѻf)m/bXSʱBBͦлMRR=(ԆpԳu),Q~!(CbA^d5(i~\!{Ƣ 8u8ۅ7`f2iG}~9p.MCd7' GRSJ$Ϝ#vA8'aL ToJ8]J wvL TF-H ][6r*2bL(Ŏקh [dHmHV e~1<e7k׿<2&:S\r( DvXBv<~㺄k";sJr˗uOGQzYaBZlʼnrT]ZZJcleRd| +Dv?dӪ'>IKdN.K݉i1xc v4 |4E]b5)Ms*ݜ8*؛ Sϒ s2Ge>5i`7' 3Jk of1}jgsG=e;%K$GJ!Jm $vi36PJFm{e f<2GsMb7ZSfƾS%8LBۉ>  ǽkcF7w|$ڋe_J\ ӵsrnetޖvlI3e5Jb3mB)aG*LfV$L4+iRs!OQԄ0&`$PrٍvfecVeL 0F% #=i8Wt~9^Xd6NpaE?O~͏EE"/@=QpdjN8g4Y~O?!ҘYX7PBoS!x~^jH{rdǶƩyc`eXtQMOSQ.W@=qpE{e!-Jcp?j:Śp"TZ$ƑMlT+/듶bʙP܆5i Z:Rۻ-pY7"1)+VǸQ8g ޝ*L[~| ˑK@by&Ek?A8zXKH0J,x,L)sdHA9ZXJI{={]CtJ&>V"\K ov.y1&+,hbŻIeBt-<~nhu 5ucKDSۢ xc!!<+Xށn:#Sz jcPW$[߳p^i:[]ut$ueCzq͙oz3AR!N ^EϤ3)A[n:WgG94Z O~x?#fAXRxCy)XIkME6lG\݂Gb\h5cS1f272>2oBG߈"v*?´[WVc) \l6 **[^Z/si8Kt6M/k^C?11Z۹.iO[7wmTW6˘,#E=c7оNJ\ V_*J=96+(/4+һiox(ʒpD7ϺY`BJ)+| :pyEh3Z/{y(B/Y~B98r$.!gd8}0%ۂ{Ъ,ۮ@~O8yP ѢzXKdtV:{9 +8u ~hM`;ut(@{jq!h>mzn'4 /ˡ%-;-yz1d~Em +%_&P _/\y yD&&G\o%y׵G.]_D@IDAT}QT[ =@^Ԇ-ޗMl1&/|"~ƃQ^eK8^/fHXO]!` A% ^-:a wrܽz4v*>Mme:U dHYCMjnldY4[VP2I|2?GŸf5{wHcx48T3L6 [6rQNݤY Q/y{)nxi\<ӳe>.5*G v&6s5'sܕ{ <I-_O'&ytI10Ϣ0^za'|FfEt=sX>>ZD< Z{#&J%9BLi Ox9h߬71vA %Ic0BhN1 S+Rx,K֑;3-+/ "nSo̰4bJ7Lmb:t"=,FMQJ ?XK⥁#펍UZPdח_ݴTz")wͫ x _Jta[[<8]I8`B,$2%O&2y B!qdRBl7b^t^ eҥx ˴T5[ /+& ؐ|ʉkaqG[tOEi㥭L}߁qfI8oas*\2<6V%Y($Fz.݉jR# ) D\ up@;§QWGCj\˗o!hvh_`NU֝foD[M:̀o@ 3Oyx.^7o4h 'Ne\^$Y^4rfNeW#q+yr?WW^əJI{38d;1 S!Hn0 v,})}V#1JYڧmZ;! "ʪg){~l+)$Ul\ rv,᧿X,V#ϙW+Ι.fw|MHvj^9W *J{׀OhosEѧISp*pxa05~)i*XMa՗p;o~UZM0 se _"~ȿqM܇>FR ۾oi若1)ZYLu7񪀀=TN6nF! N#cBґ,’qCKD4+ۮO[ ҇ Z.I|fOlyc!6f 9J2kB0~Iyakg7Sg"0u>IhQJNmAQz>N%"]?}Ē̜q@`RDC|"k.q#M o< s,1C^2]'z8{5FP9}>^~>fshǠ }Qw]YV9A$ zΚ)zgDD\MEvߚ@ͳ%Bz:XTtsO_BE}Chuf-$ލ;fwnՕbZV@IŃe~xlfzg*NruxLر1?ցxb2Gc6Nѡ [׆=oo0wbi"^L "e Yf :GJZU:29@%M{T1fG.˜MzYA~8Gi~xѽ{;Qx| 稜[{s\hٹv3lsOwǮâH!MM&N_ 4k/5.r\$:G5qЃ~Y;7Z('xyZVkej  2-_.xRj/jPutaq÷HLJ JxUb$?2>mo̤U=}^FR)fͿ7R^eD}y 0HY;^7ն+ذN1ܱFySDufRICŔMODXF*2?xfrFc7RIIȳD iQ^ۼCNRvxʬiw1R0Qj/7;(Jcx,9 !qaMUYT@/1`m梹E(£gE[ 1$Q[rO4'_W7x*>7z{p$xŤXR^5_a㗴Q=}v%4+wӇ8a)WYEbO-!߳Z}ϦC4+hφj%dL 0&P bGP y4)wR17U9GW3_’eT8D-U;dj,9]+߀gcQ?J~u7jd5&O!1XCH4 tqՓ("H'H Og,D(8J.1 euui( NAQ0&4y8҆wT(mbu+A޸閰At7^}ҏmj:Ńrgt}[J%(m0r<ؔ{I%@,{]Q*I!^]i"Ot;qx|PU*Iř=p/S^XDQ*!QxmZ|ޙ|{$bӺ0IF4h,_3ߣ0*4 ҃{ZŤ?XoT>awI$xa?,Ri)@%|9CL$i)϶j-tBRvb*GK%i=љG93RT* q;<9ØWF.")Ę29MZdN~Bj[|wh+Z(8K~CW 0&@M#@+SV`I\LܟǪaX7\$5 Gmq_%+1׌T3cRZ j/\^ / vWo)`JOuWPxQ"iCO<]R+,Ȋv"@k~1Ԕhtn=ѹ<ս-7gq0Wt )_NI[ rL$]µ Z]wWJn߿]y 5݇V^ekvIZ-MLE'qK&D/@}k43I5[?PX X$k6`mfn^Q+\g?=LS%x\db.Fy? g_^={XgDq#dz>,%=Nq%V߅k-BL]?䊥JSA!eeDn^m0(f)M"ٺQ05nT7iT }_e9(qP] GC@UܯXHv_O_n!n !g'lhVB#4(.D&/{̹:ᣲ+7#q6m8"7Zc<ήK-KWSK^ V 0*&g6uӰi3 *L+NNTޫY~4ٕT|[ |wkh']V!`L 0&PysX=&BU*LX=+̓̀]IUK5G<,[R{ VOhqY{̊M98o4+9* c0eN$&$ϘoRFAek?z#zj|}:<}5N[ `rT=Q-丶|BSmC^9xܒVl !߯R)oHHEgD8'o[2oj۹N-GE{G,M݈`YB&IR*L-Z0ÛyU\2'ǁMh&-\M)Wam! 5 &˽ԣ]:cXT.kCt%iu3L4R$5zhw%<_A \'Xln _HJnv3륖6]-:o?4[ڄZŠk \M;v |qRjY( T7UHUFd͊GѴ*xVtKG{m̝2~"-_w )/~s`Ln(D()nXY:àW7xC._ǣ_PC.`L 0ZJкXlA亾T+W J׵nM nN\)n!F(#']i&7Btj H{W>iLũ %RxDHP&F?{w?ڞN?ޣ̯V־5Ԅq/7ƔE?A(~GJA/eŒ!y쮣նs5Ti! Aq=G:֏z0sv~͵ bߞ8Y=S)Sxy4HH˕J5<]WQhnv@ V~z#H+,O혀1[uy}Y$! TORݱq?gFom/9H;kٿ34#7#mɁg BQ:*/IXr1öeic dy_!AUU3 "U[׽3?k.Z3&P)&PRMS 6Uٚ."we>˱sT46߫K55aG^'A>_;=P$? u6H~קe_f;@x$`L 0&`MOcg!t Xر~T8W?MKGZ*?k6'WkA짩áԢ;;jPJc9DV@50CM(nfw,J%{|]Ƽ ECrHe[7:^X%Ƣ=-]t{*QZYɿL z1aTAr & F݁?a0Ďa5!c ;^Go? .9m$ #I]Ik!x2y?! ՛~A,k` XkXoM_tC{j|3H^X,5_L7>'hUm%g)GH׹ D$Zn-c>@b2ɭ ̉\ Ab^@dcg?䁱=ZaLi޾GᓅOvEWy":*oG}]i!!GJהsK輛^'ǔb ̟y!?MiyrTX3\}ַwb@Gɦb3ZzwKO\)KQV V/ߌ i&bл\\pkQ->{g Zz}Ġ".h2ݲEӰM7WRɿtزIc1d=ÉB+iLYNք$~>6z<;5Ά ##3⥡?/gL.><;/%w*iS~j3GJPܬm,ı/mrW[Jph똫(Bg2vZכJ)"ęష]Cڒt1״ϼotD9>-=S޷^ޭǿSX,k.@T,-I! %.MUZ=[ƿLNh!59B%,Eܠij9<(GdStyLI ;Kh^,㙷cYfUWprt'atmuogaIb Casy#  u s>tmkS$. =}I bI6x ;{-"BazEYbLRAzhpqYrai IO󰷄b}2#.s^SS=EG/D[|ʯ~YW :LvsHB6LL@2Fmx>ubΰr,fpZ[Rvoj!R\鍟0adZ%{BzܻA)UH^kS!BcBŀ{P_~wR%POD`CU4am#IrkirBXҊ:K#g"5F6G6$Troֿ&-ku&LAװPKGr]Y$>54Y5QK5g{u14kJv3ib M]Ct%T'0kl1 9:!W'R8PIB~l]@y)23̖h[$*Մ4fn*szwsz\ҘUrh L$Y~vsO܊WVO U&x-գ?2W95iB\I( ۱txjw龍 O6+(m[?p6/)'2KT!nhS9b{f} V= ø 9a*VHA܈`u#?{Ĭ敓hf < ţ֧7Vۊ[iz_UA#2r^|{<kߪbRKrHN״trMIUDAX|7llVhP+1+K̞2\> gRCG#rIO@KO؞զf?'AeoY>h숗s)!4s5<o=^Q*&asFf[XY>( u 6r нi樚2gl,Y j/מ@(:;@Cq_}4H!O[砫گ35;P9$97~`7^#~V*u|ι5:$L4+iR^94,o5]KWV\Yt4T;۷#qB#-FYs'B?L}8ѬT6}3a+b|%f`*gks4aB:#R()rAbTR ^\;1{-LÖ!0eJY2WQ*#b}vlZ;S5%L^炽8hT`[h_)e{ ܚ5d*Dd;kAb`aLUQ*u_}8F 4O?ʨAUdK,lbλYi˩*隶;KRh@Y*bZS6RA`n0gU{9u*'>I~}ȝLa^*&+vo T,y~Df?>Ew>7w4 ܝ`+fu+M05[ ^e Քw:Uh%ʽR;?[.&"*}hx6sw/FCwR|ޑ }5MT6B߷L]$}J4RB9\ Ptv7ĺi0h=^c=sݬ,ՔYld*Y25do:α^dyEՠeIXfAήeyrd ^?5u#b0|z$֖96\ Pn?wAfei"E\q7Kȼ1J箉4/p ЫfL4gMCbh?K6l+|@f,6W%d˸fNoO{ѸScg+;l>UOӾM_#6PhU6_aKKcW@zݧ]빅fϋ;PXMURٵS[cŷ?k d70ZW~Z7ϖ싂V6@0}vcT;PCPR{5V`'[/ű=?I^%+T[}&RIR]$;.tnT'pGuNN׿`.pAhsID4TAxwqENyWωDʉ9J+MD7 PB NȉT@AFQ)ճXO=8ˉ*'!{@P!tϾ-ow_{//|GNo>3;o~;3 T*a uuoaxt 49kgi05hT>F*nlT~+L,2v?>k]y0.E]^tBu)b3E&Zf \_|ğٞMF ^Ut{vR<;UnC߶ nW#ҰT+"prۿzҾJmWԷf?_(s!ACQ"/ݖV0S) ~ݔ *Aˇ'$@${.$0| :^cN$P9J2bVøҗ_w=cX3wy|Qx?z2/Z>K% (m:׺\*9 ~ۼkWİWKcYR8(>/+? v;i2'q9:d!l,Ih%ou9S%%elY.waɶ E$@$PCаTC*$Peֻ LzgţW\2WoS1{Kϱ$_xE$@G8K~X`#C*<_UƩ) 4"iwrb]sgX5v|mh NMZ:.bIǔ7#4|b=N׺LXJ 6ltgDfb9X 1Iy]b%׳ mT;`\X6"ĺ{bb 1, Ĕz[]n-Z۹1$@$@5 K5  O vs!m<"X h#cAɈtGP}Cl48m1qd XWV:"ٺGr p@=bͅbiOiP-DW@UxVF1O҃'*{"ߩ[ ƌpu6@z65ΉF du[(dަV<[+ihhqid3Sw>}FLڨXznt޿ږXl)ܕsǕSO,=Lm;5(Fo2y0 Y\6*Iԣu(Uɵ˶AˠٰF%>$@$@4<^}rb",x` iۇn5.-|{)&^?~RezO|0bexNHpŶ2M֥&7}Val3Tq% 2/?'$~uj n,]Y=rL$@$PԪNaYHHHHHHH*H g 􂲷jXܓ hoսvZsަs&[RnL.umXK• Tq8,&atJ!Є2L$\n{q'6e M-m&Ņ1{-~"=lj=WxztS;cK5.PJČg{IZ tt",? `gźWGy[x\uPݺLܑilo9@Nf8Rz6H36~$DKrqFɣĕ  %)Fza*mR7 Qn2cErIJd[L}p'2}Ѥy$6ԥ(c LRʽtG[`u{k!=#CJJ-ꕛ# T3ꙥ$      r \>3S~hQn"}l o|"VtP<E~9gC^:@xb/A{a/ 3!zl:[oE Hz}oNKFp\>3HnF% 1: z ]RSX67%4% @M%@RMyHHHHHHfP7.J᳿[5˻5 w?V0_y3uMhm:Y$uo6DaأGviڳ)WvjmV0k K=#<,f+I.> Ҋ|vymI<\zdh~G54ʑa$@$@՜ KռY<      GP,Ӗ,?˳C) 2 }LͧzWet!XWzrrW97+(t>%#%3]<1C4ؚb'@b*#NQ69zvQlf}'P)Tx sJnέ zM)Tu|G> nnipU.g^_nWxlڬX)$@$@5@'Xr,8 %||u`0)^@Ňk棣nߠ#$~ڸ1;.,\"/9|1Rtͨdb+MJ%(SrrQg12~ \!޴1ᚆWڌJƽ}]wٶh{7ef0*u$歟d3jGٖOoMrzMz|>Ml-Ua=UG7hTՉG  G `֬/w?[9R-J _؏ܺgIW؏$ L*b_N!\1'C[44zMPe8%as< -ʆa$Ph~DJsK);OBE 16Ba>Nfe!;<._KY&s![]sNl\Ƚ%%0IMfZȚA^vJLB$%~n㥄IuR^eBKf`9OS @M'{>tx{%3 M:ItՅ@mQC# ! Cch84I[]##A)c8HHj*.WSk&            а!0F'           Jn)/WwoP?"a q2&qoVen0$:ģQHMwd[#Q4@ݍU?cBKN"8髎h ROgWNB<=r%"EUzT`W%WHT+܃O„:aX8)[UvgaUMIHJ092  *BlǧŪ!xd]oS㘏axTܞi5 @Ai7D,Nbo{E,ڵZ 08k0mm@Y<HM.!K+-M_޵#9jk_LHmm[;:"jsy)\݅mhdl fnw[bJa*,ү~ 5]4>뒠\uпfrkH;޻C/Y:!6 3]L;ױ&_? 4kWQӘhz]jUzUhJć9U[4{fYmяHH@"`|c#  P'Y+/k0tRYٗL$M~}{Q h^?s6X~܈ ;ӀB`5;?^g]CIɿ$T\+Xf'f.\`utaL(F+>J0~nvm8{992OHD}Xi4rTU:R^yZ],[ՙRlب7*IMdU_#*zS6Pװk^}WbS;Tl(B^垹e+^ڪ2aqIxo`3 .X4B+czuR87 ہl,F'a }]#ߩ}%<,'\sޫDNŴ]̀rb̓X?!%nH}pi,ZKKǯ-NW r@<>Z[ Ə. y}*15Hr#>Um*W >僵m;ͳsߧ鉾+njZ*Tx둧{$؁I+tFfIlH30M*TJTU}O02$J^A*VLN$@Ո Kը2Y jC]W EL`F.9ya$oڎ+86LJ"F0]WSS9)d=4p?ci))G{;8]'=lOS"Ė9a'f990< X;J$K ?}/cРT;RyuF<}]Ge[0T<~mS@13omϴ\&}-Z] |b'Axxw[6;aC9zHh~Z L:4yo*X  )bAuT^gc?~W&qbof0_ه|kZ`կx.y}IE\ zֽ3/v*>͐ *Z;O*֢I:fqƛ" V3ss+XZ*F4W ٰ䤸L#&ڄ"2iYYGF'Gϱ :gF̥0,9HE vG̏`Zxm}F,|#-[D,rgκTE])Ja)D(g?>` w-KgnZǏYWNZ)Z? jg-o2"e#}Mwqt~eЕTiUK]N%J4;Ch&u6T/a7?k`HS\)k~VC.{)Ko% b9~N@ܦ|'}CGy'%Gf?s^çkIɴ<' L˯:e# \e׿t.&OI(8ۣ.qƞ3*?nɪo?ư}iSh~!{|٨服VP/j>1;.)Fw*7  l(%,ިd^A` QI\fuWu4cjh*!}A&zlT 40sYYo75th2+$qĹc,mo$/>gMߗ&+A!UѨd+֩IhzyDH6/Z4^uNᶘU9[u32xV+~F%t<7T+Vly>8GkqRI fMdT2<rkq\KYΉ#Oq ew)1((tyG>K^dTRKs֟R/x$ GC}5Y` bƒ rZk'T٨YQ=)#^ 0;9/>tCZ#7Lt][[ *Y07/C v{%C=}/q}*˧%vKm%TT0e]O/70h{D+"9yS;/_(fuƂqmt?Ĉ%hSQꎗjV019I{Q,q0 \]֕xڨsG_ օb^$fW@/eN>Yډȍ0G$1nd%6G;K,EkqPS63rJSی#qyuOC=m8a>2;)w7$bR}_C[NȊYϦV!oBk& B3ұ6eY.՚GmL> 1"q|FܻH²cMf~epT.w|]~wx#u-@<(Er< A1co,%7vqû޻je}w(ڏW{A$3§J?B< ޵ %[egakZKDĦ%çl"oj_'fV^2ը< @ Pc KeL_Ԉe!}EW$)j,myopeCLayZ!vbt/OHFxR;/-ZvXG"\<}Ka0W^f"bZ1GcӸq!t }ҽw{]1iM%NC?3u PTL Dt: zɁ\1w=y\-(*h.Mq Bq*C| 3ޟ|¤ġE하f`[w Ru}@R=Hx}D04UH\GԮmRٚ˨/>yW,m)d}HWb"c6HW=գ{mGUkp׃qIb^6}w-^?tR|jJ^N% AVFmq᪝XFTB0f&ʹt5sl^i0 \5].]b2PmY"Rhbί6ڲp^&[WgyJ0{>+0rΖ.?DƵ'#ۈA Txz} XI mgA$f\mݹ%K_?2lGd\Ӯ 5n9A k~*RG{8,'{swC+s~kAJQ$nwm[0,mSۺ7A@pKg$[m;}AݻZ Iz90~!K°.ɲ/4%T{V{ +Yh1@ڲ(;k}Ar6j9b]0w!.o{=z( ٗ % e/j}ڗ=3Ī@2JM%|?؇9*QJdap*^w~$PԩiX bqBv N-"!жrO DaXJW=$/k"fIYa[,m,d _o[ %ӊrRчXO_],Jt;[\ lT6m[3߄-g\Avq"̯bVj7niZ"ڦG z:/Y$o < )Qw1_˜&A(i.Hj_ KIe.^i!^_KZk nڞrt@]dy1Xr5_!53[y^U:?rCm/ ]gh"YfP%]+_/f^Ri&7ce^0.)mZ 7qy ĶR6a\b8Nfh}5^Pw2t kIP-wXRGqؓUkIK="~v7 jMsE{Pó"Yz0m0J\3aY0ûx׷5[* @'`|kڡE9#*B@?), ` A@$4o^b!xHiHy& T6`Rx%(r/)uzOZSCb0VY CVԣOrqb4KF9,$W/)d)ieC.9lk K3YKvݸ޸Ej6l]JOVEp-1s",CU84GxZy sAd)nƲ @PMkIۑRYc-$u뉹)-x{ѼcVwm4a0ac=BC6-zMCklT( |Rه}څ1tU€wEJ4w_˵xy&mRO4l't0eQ2psc~pM8dB˿H' h+ fӐ&(( $/Z]X],(Au3bG)Mq~Y |E? )3L硖Ƚ~2JVRm'T*q"8*Ye$v}ًqѮ=y'<_(k6.J]9ûHv(>5v;3bJ+Q<ͺQtuH+Q&Iw]~0ilgj$W:>+IzZ\CgAeǒ\W2 A('I 5(ܠCy}nϧRy1)jw&xmrTgtO@4 HMg+xѨo+"63d=`Hd,~<2J\}:1~lu"v2ĈI9ܓQhln>v{Š_YMpLie0xYFW!8Tv6`S_voU\UT:sIFz6ǩXofz$@$P K`IH|@@|%,-ެ G$PU Do 5p೥b)\Z CGXҳJѶu.?O,s 8/ _bzeVA吵g>ӖdEw|BC0t\x;v;f0,:gSu;t1F|P_Q}D7DhUw3olhWh D0C3 z-ቮ'0uhxq4Lq>u>gNO+.o NU=r\iG0/2[EX喝*Gq_ #WCwl㚤 l[W d<Ԣ{Ƚ~\2[H|Y~0Ȏ@]Âh-KsiR$ jFB~еR'>xIw :S< yOh@yB$@WOlE2 YGs[s7㱍U6 r3 ҵ\F0izY6`=~Foиٛ'tR| ; [ Qvm`<ʺq|i!/Xh93tIlީɵK+2\N -~9%OĞ4*_r1k;$u&AVKp@=w/\6y`ZLQzײY=kUq%}4nL?u9} sk:h:5'Km21ްkp}BLkȜJ+Z/ ΝA0˾YVDXSxfTWz8/}ޮxf|u<;.B2x߫.YYFIah׻`}`%&Fg li{?\C$@MO4ԎHHH@`xAG7s ?:qmPKrp6Hƙҹ:hޫVaRF ?,*u/ءҗGAbm"EOAபSޚ iV>R۔?GKR&}:L9"))ƎcI 73}S{c5\O)<=s[0}SGKط(j"ʎ-q}Jf s}U֗t K0مJ 1I H0R=z: }%:/{LRF=貸7o;!4u#Ean^|7mcɠe|x? E$8grpL]JA=VywsWo|w%~ə= ǹO~ͰF!;:ْT3[RY.Dfm%NMK9wR1 ~-Vb&!Z, YcruC1NbB~.>,Ys|=A`p:SLEX ގW-Mt+{5xSkZ =ctԩ:w^K8nu/H)$@$%cX[{K`LV3 zgZ&bQarMyI5zt|#Zw˪h6Ѝ:djܧAv|G*jK$AOWGDlFˎ㶻ZK66HzWKuTr2)ܚ'вc+4V*?-aIe|Yxqhu+r2^\^b̲%? 7HZ6 V̸Pʮu{fwˍݐq|j7ĭBQg\c@9^ Z&JKO5S7T %ŴkԾN˔BTij,}Q+r$ϔW8s7KEWT6g,o.Y*]eyӬ; >u>-3+]HLe'?ߎt.gA,[mCq%<),oJds&2W,Y7f>P-Ryi ;Q;t, lEkо?/Q?2rNo+l4YS~&!z|Iy׏ޔHRlgmʅ:+'8=b^9MIy - Bjj3m?<5^[lqq/{cl2l5 l}o=w~nK>s{Ws^ks}>z^^'\}8oطU;IHz1KUjdHH2Bd5!Ӟ0UrIU@z_ Dx3Wi?t-3 ʡ@bx͸58۴kO<_MJCqjr<8P/`gZhܝ,p 1ӅNC^7oiF%[0`t:wN:>Wum[IIǃ) qp_Lig\GRb`q>}{7Ƌ0wc/|3b㸖YNqBCY%2}&u@N1(g9W6Xfb}z⫧vL}˃d9ϬAă߆EnÃmҸUFǕvkS1LMnc@ސKkd_-|>#=m't@ۻXG9G2ZR \][?B`5MRҵ+*/%46(*3du`C.6x}r9WWkW̔sO_w'Eypt..׭ /n0LWB Cebߢ" P/TR^"nX1zصt;^y CU=K8jXR_żD$@X,uQGyX?cF܂;[|K(n ח:jʕsbXkQhcڗcQr]2.+BQ`EtxJ2EnEW  KBi0GS^)~G g@^YS["P,# ؏Wݺ`S 93]}]l?b<  ?bL  w5ǰ5A~]=TH >O\WSSC.|[I%@i$>?bL  w5ǰ7o~r$@$Pey⺪5vۚ'O-J# qc  R?             KQ *? G$@U'+|\󩩡ly$O4pY1& ;cX~~M+)uP9 2.Ӛe`Ū}@H)xY\ z$Dʓc5v+qT$nS{UZGbX&\71emԗphZ+Xڠ_xt:{il*/ *nro~j&"exy$Pr1HHHHHHH-4,H<_AA2?y K߈w(LǛba1(s,H190.w܅ytD|X4 p֔D? !^#f-ƣ-J@>< 7H ]'VO2 .kBt9z>]T}Mrz,g#S&=:MZ*}v1ש+8oG>?cF"OyBqIHHHHHH'P K.DN!e[sɏH@E*ea\1hĨΚ&?9ڵ|mFOyKO/1⑯s:`[OjF^;_Yc`{%3w 7Ac DY`AM\RlmgEwlz83z(|U]f%Uwcͨ?xH&߅/R姦#v7"      pFB3cs8+/ c Ch;ogПH*@5{]74.JhQLMdϗGꝉ"g~YNOˣZ/bzŏD o;hgo<'v{>"3ɟ:RsyſCx1qCC0!_[u1zĿaĎFOʽSjͮ#O]z!|51#RA(3etZ)o9Gu9='?&)1 g8f#8:?ϟ^A[3L_`}&Yg}1eϑ#yQIHHHHHH!P!RaIըt0+O+ ,vdܱ7$>B?+eMn0et!}룱X'|R#Hw k3 hf-)+9{:XbN$Y;K1*cfMCrcya j#GFnΦ PO39c5Mdx(%@fzWb^#+&?q2``S>W"?}<'y.ɓ(XD^F_ao/£Aϴd1$Lo߇,)p1mХ{4Ҟ'ωE9"TP {nJѪsO$7`sNlu֜£^h"tΎgYܺgb#8z9"~I0nI mdv`Ga&h2[=8KW;3%    "pxϯ42$@$PJ[Jv+e_˰hn։8K` Q>@z}kX}k)Z\RZ8TؕՇؠ1{d1"ok}]-dޯZ\KuzpOJ.bOnvA$PZXgҽsQiH3ſ_EVOb4Ra<-1|3;o dAM%4nͰ—ϏKrQy,b&nox6)r񜀙SUFkқlCK.0FT,}4>~+ $ƽE'q]U9  %$@$PA{IUg9wFٗ hԸaINP:{4 KY6:M7FF.hywHFLSӃiz^;C}l1 mė1n 5MB ecYJƈkmFhL2:RY %$@U*=fT{i.{1ov){JI6q, JRc2DmJRa2]:'Q)`>2 %Ƨ631AgT({IE_C lT-4h0FsWU<ׅOutͨdO^c1jѓdrJRԃHHHHQ s& +N"<6%_DYN' du[xYd Lu8> S?>; A?uŝcqE~EHWXrRaVa=T(nB2e{(ţPfBph q#c޲~h00h41qgܝoDŽVw(ʦ3"/uO@l{o[>h@}[9/ϰa'K)ֽ2oC;*uȽ}q,xJ?"ExFSVӍ&A*" TҬԯ'qU!~|$O?F$@$@$@$@D3='r QXr@$@$<DAϋu_"K|; (Ţ 8{l'O$w_\EˁЩO?Y \. _3T+z4 o8c*O8tNĹ}9*#B\h7s.1|u:Ќ^$@># \lTdF@r"7Y2jIjTF܎Y|㇂eGZJRʖ\d&ڳ)ԼFbP 4i#'I_K<2w1U(qL$X&݇`HZfT(4e} / 2{slQۃQN O*I@~X)THHHHX/7AKGHuC0/HH߳wV?2K iY۩!A˂4 ,8EX0BwchS[6;?~n  vrrw}u;[,1[#ܹ >2։/{&^3޿Po> % ,r*ZBhh-qc.:OI@" Cn?lCy8{\)_|tpi\"6k6{٨ _d0luR-8q5/{1KFrѶo| KIau Z&u``<+5kkSwGé(Ɵ:2AATCfL}=w DrSSzן^, #5A+J"N]xKW_ì%|T !/Ǝb%.~-Hj{{ר]!    +HŐԂY "iͨ53{θNdp~" YJ|>rb_yzT8>i#0D<$~ؽΌ{d(b<k^er3- 3nl/z-.26pX$_Ϭ )uè@m1oI學 GJO1+ao8[ΗrqFɨFV7%(v0./Ml\6!n6oZ\#DIs RrPf6UC$P D4CZx~d3Q(fcl}Ff".K DK-6*vbC]™Qi،/0e@1NqhݿGuϱ=g(R  F[ȉ!v䫱4e)`0W{I(,O}1JasP]$uus7L\%4 )s#v7OR]Dx%AB>j00o&1a*r(7Ȳ`M.Ot탧y/*WiHk-C3Z 90OI=uʩX.ARG&v;Ot|g$@$@$@$@$p TȰ\ M#CO#ť# EpBRXvoϫD.!╇!.U7+(O8K[ <>KTI^gvTGB 4(#f n8gm;f{ubqsZ-Y2g TgX)dA"~S*+`;ܳtAU&07 +U8cG`n\qRJvr pNz|?}:<< =˟wx.Z]),_\>Ot%O*@~Y-THHHHhȍy U"PV7'92=|?а7>c0FL{&e,ˑؾ1Nޯi4"$z7޺yzʿOѩO;!cg("1> bID3S8Zh.mi@Ò:dT$5}g4o(6v_M/ ӟ/P.{>-cȧ B7v0obG]"^o[r߶0Wޥ1@x>g0fe@ٽ.PTu{I_A\)?;ڡn$@$@$@$@$Pi8R0 : g3F*s,xxz{鄶5s uFQ.\k3*}38 dsؐBH`A (@VXūųJĠިv=Xy$;iI:(:hWFThmqu:'zCLtte6dij{t‘Xڲ0~<%v cUoe^{y_*+lwҪEwO쌭BHHHHH]t?c'kۺT}ښ[c̓g2FᕒJLq`.\d< Px=: JmTsGǩӨm@`x4b f"]Ié`/J#t5+ڷ]ήZOosZ9zQ-?F!A!#3n\oyo߁[ [y,*QRV "mT͆4{ӊY0(KAWKD04FiJ <=L0NZEr-yV*So7ۍZw ~_:;K%oPǑ1jYeG2$ h,yk Da؈]\t9`P(${NP+"Bz!a0@hUi=xNA$@crBc? (J.WWi$O,UaVPWc52]7-%R6o'9'NTHHHHڙkg,HHHHHh(+I]UY'    p K.ab"     _$X_&} XN3yv:$@$@$@$@$^@g$@$@$@$@$@C ybIl7%B3Kӵ:>_ y|$@$@$@$@$j4,! tVV!uV-}G/$@$@$@$@$V^[\           bhXb @[aR. t14,ueuHHHHHHHHHHHH"΢ A|Ѩ2 tB|4(9Wn˓'yzqS}!  X򕖢$@$@$@$@$@$@$@$@$@$@$@$hX`$@$@$@$@$@$@$@$@$@$@$@$+hX򕖢$@$@$@$@$@$@$@$@$@$@$@$hX`$@$@$@$@$@$@$@$@$@$@$@$+hX򕖢$@$@$@$@$@$@$@$@$@$@$@$m?ks          6%q6K$@^$@%/¤(           JN\? ]_e ?P~_9T3nÜ/|؜7AiK|pٟ_-*ᇼꋏUxщjA=Ǥ6~n7ljԣvu&VWc.9ǛupI]|-[&P {"0\>o]l~Y~`SV6&]a򇯱3 L6ᵧ^D(%k.mX"O[$@$@$@$@$.VEVHZ `ڇ?\r3v;HV΋(^ywdX܄GA.pLjp 61y݅[~c+NJҖ x)ZZv'bLxa,9id|Y-I>VgG^绤a01I83,η+6DK| \A1v O`/~uB3FvŹ{2ً]õkW0 xDG1S jbl9 ?Vhނ@^ Xz^(7v Wso-Dž}!xgE+"(I5?'Eᣟ GIH:vF $@.K.ab" .M>pdι/?w_wVUw݃6w 5*59 ?],~Y{_ۡ>fQi}bFiV" o:$8 9kgTr:΄lJJVg-FǪw=d+KB.ٯ^ɨһxta G{e0;,?I,׽qmވm؉J7ԣS^~/a/ .@ ޹Nyv$}M$@$@$@$@B߮0HAE(W1Q VlڏQ3ހ;s} (˯ERh\>eo%)_܏/ǨSWw&Q7{U/v1,c9y}o뇘Obo\;ׅԮ^n&nIuRѭeAHb,}evilFu'#Sq鰞X=j,/rZp+~C uObXTXA8;\'ڲ~_㾹+h]6] 7 x}t &w /M qY1QA,gzzHsϽk'Q'zk-|tY4lmD$@$@*sM$# K>8$ Gz}.yXg̓M'ݎ Uf/UJ KBx1ʺ,hn3s_/QըOt;W O-uI.\,kW+Zا"8 ^?Q[y~,(BM?l*h3?(F%EqׂјTXvt&2_@]-'osj:8.$2. =JK!l@E  ?Ƿ`蝄sEw q1Db%#Z7@؈!^4b?<ݑ2CH2FXĄZqSJS8z=Actו>k+ppj;R׏Q&7F:ᤱV5X 3dAT0je^ҟ٥#    OڳH-$@$ph,;Q OcR=\&>#,.|ɨ0Oơm8\6!T}2VW"/c.odn%TG捲G]B#\mo^y\z ⸐c-K$5{qߛùe2;KuH{Ri<kW}Ӈ=VkY7cɠ?%K*>G{+aCi\9Ij%u,^s{]=IJ!O7cZ.GL GR(cĻ/_k噔V1Dx6^NEwHHH}us t 1Y* psRf>LL5Gc \# hy7 v&P 彥(Ąy@PLez?@sM5Wڵv*]$&6܉MM$-Q$-%Ko¥bB!$g]bN ){ Kj?_ 4˶,~T+Ti#1^6fu CJsg8b4=#kkFFE2S_*i‘6q\GՖ %ʙ,'ڸ=ѲRNȳX    hgv?%Ϟmv# 6"`CͰ>I"ޑ.^'3 >|H_IjڌKfF7Ϩvj%qU&:vy5V,Αn PkPW}UԮ}x܊nLs++$@mF$H.z[Լ3_~%۫>k>%X*ȋZfHqWVW%#;j;TS Z$@$@OϜ% xFKqc. .N6gZtax5ոkS0^cY~M b=oCI'_7]ԥ@^J`8 )B.M$;T<*Z Cރ7k^gϿ:K3;+LƓ %Ψ$UMKz^9S-SpXY\xf+/  b+t!(`1k3F#`xͨ$%8p\/'.!F XP} h5ڽ륙q2@\|_"zYYUO9ug&Z lj^wBK+q-A{3+i}3GcM=YZv-!bunhA;աBD$І-~PT#GɕxnQ79B2U93b̟ҥ$&CZ+x@c x0B0vQ. OQ1! t:ltB$@"뮕JlTBgaV5ƫ=w]Ĩd!Wf_:oQ ͨ4w1|R3!sۖ3v<{}J7v>d^/a J@$|||+qՅkv=,c'.Ґ @l۱d ǒWqR oZ=hmҟ %fFG2YL!$[ Y] EĎ_? Q),wdMԜ.fAb6ue;u ː"'Fj?9Ud U$`eiC$JOH<>z] _ 6>KS]Rͨ$‰!P(we%j־ >{`+Y2\ra%/i3s#Ư`04KnUy#s_ge\)?2 ]vO'yjQo    pyK>S~~1}Ҝw)=l֮_\\'֒uV?鬍CZ$(|CT  Q7آN M*-؋>] G^{w:WcvB'`Ƨ*$K?BCУG'b_="I,_{ -ұϜR4}C(z817V A}<ķwSbĒ2ު0DxyC=\4( ;ٳ3GEIHHH:1f PZVZif7}NpTHHH:e;; %aB$@$@$@$@$؃'yspQ$@$@$@$@$@$@$~JSg            '@R3g$@$@$@$@$@$@$@$@$@$@$@$hXf$@$@$@$@$@$@$@$@$@$@$@$hXj,HHHHHHHHHHH|Oj?IIk<# 's#Wy_xɓW;uP9/}%ө >CH-٭L>K>xTHHHHHHHHHHHړ KIe aɇ @{a=i,           a>;U'      !d{}~!J$Rt(ĄП we~1G!LL{p>}e(>yCEڧbP+t<t.6#[.ϱd5Gv㣵[pC=WNhQT{x褱SS` pٝ&TGaa8Yif-:.ŨhŀV;jv7H׽E}y8X  /p;p!      hG Xz"tPd٦lZ}XK>e}_o" <)0:p4Uʕ둻*cL]8wlX27[OJp0w70sŨҜUxayucx+7),sELƼ091AxR_s Jrs6^c ]d1*NhYc[ܒxbh;OB;^ð<:! (cwԕ~O%֩l9suݝȚRhfݑ| 䉲.y|16KrY0ZZ+ul8NYS]9aH:]h*?O E[Gj F>k{ GO1vA$@$M4,y&e @Gi݆!Яx=}z])ǖ݇3TE̱q: R`kqe&W &O$V|9D7O|t1gA7Q&w噘HH= аԞY toJ8Hg>Ve"ao DtAJ^\4 Ԕ,f^*/&:!_Dj֫4Zӄ}A~A1kFRDथ d>L8-VFf L?'PrL5w}5Z]FJBa&ґ&Q=sZ )MJ/S@ Lf<{Mi Ǘ+$q/IDktTynm'y׺Nd  Rk 2? tbM{קV+cƂF?ݜS]P_0rZc5K! P^pH5:j|NNKuq~k9WC>y`,HH]RF =%[uc1ѹN3 '%!.CQ2F#.1  C3!,{G噈VX ݚyIٰ$R<,cd31|046Ih ,ԝ*F^br$D3dLorYl-NO dj3Gtls{ot  hXrΆWH!PS_p!q9yEw7ک#'sB$@$@$sb[Az fHuD 5-FtHw: A -'JNX뤝Ä&akc_PH0io)Z :Ln¯QdV#ꎄDQ}RxH*\Wj%|\1fPx$3,=ԊH[7ES-j9UTgTJ93GƩ>;S)׎%]>o[EK<?XKl;,c{:rSF'FOMѶi> _~;.R6 Bȱ49-8gߩ]ĦJÒhGGq8>ZƼqFT:]LeHH ~W /Vc"1vRǩ{s0 !O]~NE)1F,> =|t. Xҳ닾c@\4' \N_[mT$cn3F%sPą 3K41s v :ForwQ: Seb=)ŭB#EzɰTuHLPebjYbrWIV6dLPvL - >~DެQɜY k˼5]>jxZ3(akGEY)volv9TI{_y:ïk &B+ց57$꒻GTZ37DY;|4۾E4Ոڟc{4 X2¢CR.߅&nу1W 5.\'AНkF)<IÒM˚`6֧D럑)܎_\c_ngg"`]>HPb:|D(ּJ6 {\}J%_>ҘTH!G:exjHƈ܏DwȈ[>Dw9Sj75Ri:"GG1/yjEڲW#EX#@=\lJnf,j -UKc-y {+ax@+ٍu/~?6WKeCa%2z[lSt4Rxcyl=HHeGgRy~s5d+/<#yPO5gx%F-c\6sNQ}n3^fgtbV\}bGwՋ3ﭾ~uz2lS<ѧ3&t8Gg͌%ƊơU.ѝap29C-ᠯT=& gd+\ѿVvZuA±rwYbJk}bԗw#4,5ʥkMM15's@!$N6uIh{%7A;ؓ|Xb11Tu5Voz⧂񧧯n׏ajP_*")H)>w  L@ȜxP;I')!wjTRI~NB3bHTs\49x)y"\F0FO%RFS ;z?U$\ PdVI9uȯiSCD\h0f< IW#ZZ>Rr,^ 1#$+QsR]X}zSEA1&`VS_7`XJDk ݧPBK~%ʱFW`MR4I^Lb-"']kq>,*ux,kd_]E 8cdhS&Yժ6Jsv} l4w1`(-ߺY>tXї͆ zm 09E1IH1)&VƪM+ȱpsb鼻^A_'K}>M KdT:V%(1x/OḖNadh哐* KDOů._֗8RW_$P'Git[fQ=JU 81< u8z\́ӕQQC,ۄ׊mBuEyVcPx/Mqī&wXݨt`QԊ7XtmeXIԚ=/1emlI'Iͬ̚yhBőb9aM)^0jƫ]djNFTUI$@$${+W#sroUwaSm<BQF˿7PQ#~`(fKw_ вYw}M p*jψ.K7 a/ }K%V\ =c101ƎDZݟZNEiEBD]GbF_uz{ҀwB_@SqE,PZ\mGE&"-_٦a(,.| sp N]cZd omNH#qAsӧ nm! 偣ZN~** 3o 1r] /FKOeL5w[r=BٿƘD{N2>!ݬxf*sF9f)o"D}4£~PkJib'~IƯ95*eEL_w7,6u:ѥ<| P?7BnK":@P\%c Ӧمq%UG߫Qqk'eӳld\^ NHW>ZrWti(؊W}h7)R6w#NlH?GugLMKXN0R5H$vdQŸtXxK<1" C䰣ۉhS!VQB=p3^]XaŃ iAȁI0c={Ka)E7ak(O_/iXrj&fN ocL4c狍tFʞ4cڀ5xbc9+!ah/RxB}l*lO/-D)<< qnVZAjnDKYSWR1̇st[F qlɈ54"gksb~ ^Iq&7CCE1䊥EdejN_o11@$@$:v0u#8닿UW 귔-|XȒ825!(>߼Tǭ¨]zɻ|Ψ$7 JL^KV"X$:DD`Obø2Q`YPD+RD= b),ɻ"XxlҧQ3}p d60F>Pɳ.I໮%JLĈ<&$$:NbB!(^r1 sŞQa]kU!fRen/f;Y:7[U0ȇۭebbC9<<ӯPFp}c*9(qT^l})K1*zdgp8`7 [Ud8f4ꏋ])K,18= Pֿ[ߖa-cqtӪUa؀§_EE}= fH:ƌ:!NW ExވJ֗'!uTzF.^]9!.IceZ"d`5htn(~ĕ%۾AxͫC.Jl`GShÈ)K`Z]L`Q0,$*iKc̛++'^{AZN-f%.LWylxߥ[gݱɘ O0b򼛑!-A!6{%Kɷ0M,c8̸?F;i)xfYGʤ)mktF1bOn؄ 2{uϴ9X   p'°d*WCzeo8*vq(NF!~ANˢT5cvBRPڈYO=('ʩ'ꖐT~˔~w&&\ /*iEc_=1/?F /)8҃>,9<~L$Of|iK>헟E* yy'|"T~zZh2Jixt0Х̟u(ɫJbbxƧ<nS,G]Z9@:D~o*Q/FD=/O'DljqyzYBX[֘qx $C I5PomCSY̞T[]U/E+1x$FW%''^M̮׽M`1{2iJ0@F wdf/LՖ \fC"*C_yWYoNkGȌ 7G iV:7wB,%s xIFZGq5R՚ԫ/fk7zN=IH|I7blC35~h:,{\>L8[Rك͊ uc% dF) W]uX /&Ol&9 @ x2IN"ѠvOv*30tL դ6|t$XM1g߳3&#NT~AF1Ƣx&9IEv#Jw}2nBLvvC4>JCඉRY>-0V6_-AweoObb$clH̬dŐu@,҂5I8;<.O'НʎuHǪLU֣$8})lf`7 Jy:Ha0:^PMQ)v ̘`7G^$o"dZ]Q'~<hT;M)N=ZJut˧{^x5d-Y-wX?ZfQmhV7zY" @' `Qk1 g^dc5X­X\f~:wq$5%bLZݲsAFFP͕)3nZCB:Z퓍mC$@$@$6\oh؅EyVl;4 - Pz 阽fͳஃҘ LesL!~B8y _9 FDh},]V(DtxM^:Y1,kہ/~]W_PQ{,G1`LmX8"mTz|ԪB1cn}Z\lŨ4W4C[!I֕zT]ӿkW{~!I믃Zgôcli! / qi铪O'X8 SjtV]QWʣ0VL.@C }ZYF3g8K^`LAƓ }!{4/sp\qlߺ8c72 OOl~ꅏamþFXvcTlS8«;1^l8\$J9ypz K[d3D$@$@$ZotSEie\7CduY1gxћ+RusU_/g6^߫s06eYHJ)pmFz0ljԏ#~QJ'QtXHxiia1 uЁ2nq(*:ӍbY'gşbRHˌ֖gCm<mhؓ:lu)jpr9*t [hҝhF7݋ii=-(,ASݳgopv+L:3L$@$й MOAjkdeŞbjhoy(Z N•K_˰+.ss)}te ==Ya+֕/ٰ;\沐2s G#MUH=îg:   hncR0ԝ%[(u&Nm KJYҘ /tC3}(%trFyȥO&6I؍[1LO듔eya1glmC9tXl>= BB'@^B_@X yZQ01EVE !)ª_(FQĖ^(ϝoY%O-=nVQ2I{$kj:5}g,/<<ۻiF>ƌ tݕuB[wg[Vq=-w\V?mhp#kz M 504X^=zh%|:v=fj'$@$@$ H¥°u ;fs.*K?Γx[>?C42~{dK}{x!0c;VU~[D07 nizT+]s &Euo1Ӊw,RX.wkvG wA x;_BPEJ?sO+EL`)-R΢ fQ_8+ň~[Eԗ`y%kUzQ;beŖu]2-kl6lӵx Cqt ֬0*u \|t= !~~1i\۩^#P'wĸ8. pTܐq'{ra܄xU%HH$ &MHFuv1/Ծsu a9٦7 _\1 #]a?`+ v#G1EWfIS*WZ4V?a^$Vbit١#cLSXߚ>+[sV,¡`Ub+ŸdEݶټ_=aXSfʷ|ֳ8=u0[TK^ܬd44}r)LVLU!/DrTיDQA?l.;JgOG we~eBX/ır_Qox7GǗy4(UPXc~9iZ]Uz1[E 0`CI T/4`!=`@Ѿ68 wgգIEEiF4ӥϋś0(ZLҕ JCqխ1O(OyyA!M8X/U;M#s $cؙJx: Ccx?OW@N9JC‘ƙxѤ!iazX@;Bւ0\sx?BlX6VJ:}.F<̓]jS1F2,1b饔3yHHp>0F3Jj :%8{Ba]i## KtN,.HQZŨ$ydIƆJza)Y7getOu:~d5UCW%Д$Q:-O\[L Q=vZK:Hnneb4c}}tBľ9 19Uay%j]&_d?N[!e ml,Kg7 >wNs6>xœF6!R؄r+E 1Vih+? impƼ°HrE9r*v. T9Jpռ1{efPߓ|cA9jgCL9O4τ$=Z.9 EX@,2owMM HTrH i{A58sіu7͓!,e`MJIG0ҳnǣ%Tnt8YXUe(rwnؤ{cƄAf3Z 1u,W0L$@$[whH7./fxiw$ޝ%fdoJ*#CueGm^Dx 헄H]Q)*Y|f9[^{0Ԯp')8X? iNX*9a3Vև̜o5bFϜC[J/w3GZ&jX~Ii˳B1Slߣ,˴-OSt?c'5+{ UbȒJQA=c)f##{{V*Z]}u8(=Qpq

Lw23$$w;s99sH!r"Ra x& zq:_a3ңO]\,NeD;rVІ.I m8tuÑ1ʬDOP ) 2$"_LG,4ރx K5_-9m 0ׇؐ6#>o:Py ?Oo /s~ '{ěj^?峂f4Ri !VdyO߸U qYNoUx1-͓ =7{+dÿRDc#͝:]#,;SRmyen.\2\_O8:9uxZs\ܓGbڈ d1 _B4AC OQa~ݝ.<$TXy>]ynل]vYkg7UKpgQ+pabTJO|M̗%}HӉoL A%3 Gr:rK3'# 2/]6?"wHFM˳#d c  "ۉ>S YqxeN+Q ӤH(RW)i9᭜ 7cƛ)(?:v6_Y>jD!\UD;||Ш|X'1D(CF #苍qu4s2ڿ PsO#&}b8zI5 0vgr&Y]y $,Eqd2zeus3ee zHHHHHHH`rxZJA֣ry73b׏O禍8t{Cϑu[mvPWi{CyFN,{{+sOn>›oxyl|E91~cY> EWSY Pws@$@$@$@$@$@$݋72R18y/}ٜ\߹|}fz(7Ϙ/7RDg,7{J#d!+x/.A7b=q== #@3)֠ I`HM;5>"2)dKFNfnNO\,9q$8 8K SL~F3jzܳX%%~zZq[ {(]ԋ `n).Qsd2;=JZIlR)t. @7m N FO‡8J%65~;d-341?w_ KK4<#MEUDXRlV91^a?ikw {}b!Q "w? O`ڣ~Ϟ#{T[SO͘$@$@$@$@$@$@$@$@$@$@$@$@1G`ij!Y*- %08v̩[[֌HHHHHHHHHHHNRJhe~$@$@$@$@$@$@$@$@$@$@$@$0E P4E"           hb)D LQXdHHHHH`J8bJ+R3 k      I @I"IHHHH gChi^Sn2a:     LXL,HHHH`z[8S3OiӦ!-- yyy^OpIO&|G"$@$@$@$@$@BXi A$@$@$@$@c `a ETP.R *ƃ*$    &p8o(rT;իAcZyDkK NGsS#:;:6!j0 n݁dfe#'w:32 FMwhHHH*Ts2$@$@$@$@+U*YʥX&UކVFnFV-[$9_Ӡn֡jAݵ3PPT4ċu% MS(= iN@J* SZX2bE&XTKj;Ii* ٳ*Tn >̘Q.MTl7։HHH%@0v~\hlĬ?7-I \a5 6>"   @Sݲgo_%bKs3Ri噋TRUlގԴ48EH: @"HHHH P4LRӏNq0EŘK0HHHHN)_CFb:t)eqtJ5dº.8~ 9iT*+ZQTta}fv6K|HHHH`HI3~⧩7Mt#+iXMڦRGw/tHC;:]}HuR4I)%   x:tރ>ҳ08GTn~ X[?zڊe GT,eTc?FU}س pԁy6\==hl8Z2鲜J7čga;۠m{-1 B VU*2(f6[Ǝa.NsQU2$RIJRI밨$3&A$I$@$@$@$ w3~v'v%eH&MU-Tr+a!ԑ[{6v/i|6z(um@$@$@$@F@NY%pFhj}ݝر*P؍Ά3f ,=rQs #02.*RIJ^YP8fĩGGvnKm=ƞJD!ԮjqT_/=ĺ*n\ |F$@$@$@$`siyۯU{vգSKSܨ\yzp +?Tb}?V}~4JDLR|_Cd'0a?zł[TDjŎMu4TRx⵪6)WEd(eW' |_9J]47;߹=G1b =.+{m  O8zyY<vӂaC}x/p1gETu+mϋbi E܇_وm9 L;F(~7)fnx/l~a% WD."NmVŭ˳XW-{f#g.N.!M7^xPbz|z\r*y~} _T}X+믾7/],DvQ)ZfFBNYJe(/@Q ڤSi̧˅-F\M3~LU:P %U&Qna'wȉv#jAf*C$@$@$@$0&3mXqٸv+@2>]pڎc8ڃ.Y]oOyt۫+ʒq<$Q*nl ( lÖ-NBB2/*tG*^@}bڄ2rY9X27wҰv/hHT ;+ .NEߙr9;13SI͛+?wݺ7w&nۉTYtt,tiCp%I);J^u}T"Or@teL[Yo|xEA}HӍBaEnokPS(aԸ_Sg=`xW}J9eo{$髯oaߥʙ~{zzq(Ά+EsǶM;nSK|UC yZ*R 3E^άld;{!rivj<ۻd/%uywbýZ"e8{B?#ׯEy5xLXn&J~C$@$@$@$0 (P 8s ?{p^5K6A "qo^c5ՆTb0VJv<3lk.312O]F>{|tk58ػn 9^z <Á$kf[* 7~_GZ}LqGvlY|xD1JhzGe 뜞^GJ;n(ppV}6R&Zۇ\}pQQٳp8L{JJqOp al9=HHNa\2*"7 +VY:uW.6̻2<{(lVҺ$`eKùG_՞ظk;k6\z5Px%a+Rwz=tҏ5NBf4/"31~"v3BɂexYfĂ <8+ _Fc|vx湘neۮ>% E7᧯Q\ \rQ`8_O~ŒRIJ ͳ7VhJxpVOHyk/%:RTҐ`7۩z*IբIfh/J7D.>HHH" ^\KY;e.^:M6Y3.ZO>ĝ.>\KY2LrM|aչ( n*u,݅T*N6ȃ~+v>f: qt> ^ U\d/^ JrqqOV|n<(rtW-̓hS<ISGe6ܡ՞vX.x*qDZk*  m(IᨴA~I-ώT].êcNqƤ;͉n+;Qvo@]>ya=e~7B#~ϫc7uoC_oU*Y&Nd.k'U$])j?ٰI[e?]s%~giqO|_sl }{v܏+o4cG&Щ>7|ӬUި' ې9U.Ẏ$@$@Sy\KjCA4b){P-ϊ&nL,|މ>PaD;tƮm3>1V+zfD'V-")q@ɾ}ش+tU*YXvR|ӰiQ07tV#pp-= s "'ˊPԀ7[ d;L Bb  #*cdX0$rWd%J,/¿{=q؍ΰNIJšWՋ0F>+I?؅Yx&>o {Ia`E%٘1=čRd}X.ih7,h3,fCXfܘ1IDATuf$   &J%TjØN.d trW\:׺G'\K\*ғpUNUǛ(BB@ZGJfe(_Q2`~gXuYt1͛J2~3}jwm7\}qRKWV>rnˍ_Ya3q[ {3NN["!>RQߤ'MO3mIk>v.8Œ𫒦QU }#\•38~5wⰌյ MljHgʑ$BYq8Iэ$ @lgыMƕRYu=͚O⪂xxf/e~yOJ]>2oc=XUXP1iǸ~@証K$@$0 8T#PW8wBRxv]Է@gWek-oX7"#58_wN / W.=gξ6݈xJseGȠ`yrsi>t{eX:HeQ#zu'^ނ k㺧Ė=n}fP$2 z.~KGJR EeŒ*(K|}-{:y'ҳt TtRc>دd2RrZ#/\__ʝ|5QO{J&x>W _)m8ʗ#O)JRI>{ri}de`0j1sneᒢT !n&\h p%!Qnn4VYہVPK%r^fwuvm2>U^r+-btLgd^iPex|| _]x=/Xv \~܁CƳ3 !(kꋌg;*}P9q#G8kV n=K۵wT\?NZ-o\`9fx`}ʪ~ln)ũ@p6JǿriTR]=dLZRE,%}`3p9 uh=YEJ|ܸ< ރ_܌)}3EURP ݙr+OA׻ЩI;߶ƋYEǽ[XKg8.uq%V@]20_9TҠƘFZA-jt/ L` nsD٣UB2g)UN@Ĵ*ThONknR.0,&JncU5%3g[מI9ZPЁ621?8to@Pm@:w xxvUك 3.4! BCq8]%3Bu8=8ϟ߉ j>メ/y/(wR 5"8F]Mt1ImSƬ\CcF]i%3sNQϗHr[ZF#֝V Qx .z{ݢȶgY/"'/xȰXa]P_/',9z  g&.?p $@$@Yֱ/Hw_tJ`m?fYK05$/(bV\8n;3T? ošJXݽͫ,Kg,li{Raen8~g;C(6|CG?FY?yr.^ǿQ~g(e&~3GQ} }rYnLgZfF/qF*ڻ]X8+yh{܃7>Ŗ,{JbG71hN!<9͘`hrXj Ju":#h]V٪K *"KHnjo>{d>U׼ڟh愠8ApJON~,R,~Dȧu _5+?k>s|V[Yn1x9pnك;יD[˻lrb,m bvUYdD|}s$m:($c sPFp[]=GeCaeS CIр'QGz|Ű e*u]Zi^4#]>g^hZ'Z; c#`9="|#=TNGmTG!׶Ӡ9q9C,,\,hB )ҬSGfF(% pDxv!\7wo8M f8N|@?V?z)ŖG:{C]r9cAZ 5acM/m1#bX|~RjjW  q{au5d1/BJ<鉾g։K9/_rM[Q9,4Q2oX98 ulj(yYee,oԹO@XOUFC}a%Xth&7ҋ_$Ǘ׫"ŋXY&JnQX֚ K#SG{vكsVZچo8[*vyF;:!GZ:QUۄ^ =-i\Zbڻm5TNœC6WDp2㛄s<뾮;|͸+"ŋ/ӉniXb%+}8e3 "wX_wzn\cH/? (_ ܒ?{?єOQˤq= TJ.Oo:%j$կMŒUg8p WiD;!W#;X#_- E|6g|Xx62}iNn_Z=ʵU!οD%}1ϛFh$d20JzQ뜁UWϭvT,p*P>w=_!7)g}OJ`VxߨWI(Ζnz2<3/ ;ui<0MҵHz|\B'*:kL\PV yc6l@Q_x:q;Dra2E_H3Ցzȑr]=_t.BI7{k,k4װPsu.#'Ir5U\'vا::tH@GzQy,Y8 /+pVh%$gnLSX gU,˨};wݻ]`Y$?6! 5v:'I'xeW+d1&U,޿-I%s'F,Gиq6հĎy㬂$B{v?V.+ARG>kD4)!gf3tM``{Pn`wvRIՉ"FNDd `WrVq?M⩋‹SP7.f:p}Tq~eVHNJ%UuG   6iH]Lx WvߋW:Y2>͍#M⌾/9Uau{qR=3i?..'t_qN-vcep_w Xҏ;`xk0_Xfb[qg fӡ>f:zBb? ug IM뱳 +e1ZUN2珑\a5,-`;Z9x%ZEߒ.(Rg׮ '珜spIѺN)L]>D̕qM:|L-2V A\'*Yw(Q4K ;"o(9jHc'K}>3#?={nY\05i1^ yHH زq<* ]g+^.ZlCZUݸ! sb xIEf?Mg2ث {qf|˟ͬcߑeLDž brL_*ً^gaq}HǙ8)zaQX>WT~}BF8&oNQ/<>K{s>MX spRA #rg㌔9u+/mXbʺ#   !`Z*IVKZb6$hȈӉv}mZ0i`R%׼UőT.K.?+.Ob"ʺ$Pv`5ן߼d`||Kѷu|syaKkV,m^b\ Ҽ *?m|J%y^>;C,Lh ~^|G˭k.ÁON%n}k rUQ^:_ff lAjWvEy#k# wfrz"8~ȥuuU"奉bI-D.TQA(&T7×TR^ۀHH$*;Eڣ o4z_7‡@3_[~>.Uù3tnW۱xX4&oXzF񈋻YonYJ oq.#-erVC-l˃=]VDOć lHY,Uw^sKł$\5|"nXg>4-|TSƄ I);oS|9]5˘I3mEeK`:,=2u=aڣ~Ϟ#{ i<%J}qTQ}rԠΞS`( C,$IGX2]c;gn扛>u,{0 x즺.vNC%wfl$qS7xQ7F'\cpzO]iZ*^4rS2cxgrSԥ~45!9-iU-˚4y&uY}`v( H2v{Ȥ #C8"L7d[_Tx|2LqQi>xI!<<˗/OӍ>7K-xł RMܲ:NWF,sEzEUQv 81j   /R 3-dx o/Dz+-3#Ġ#Yr?0ap,tK1/2Jrr:$DO0#+N;=ƷѴ*XQX gCr˥XVX(^3DqsfᖁZ.MT,- aL̞v03 /Z(,>=;i)15Q^ۀHHt%KWguMRd<9e#,=ϨK[* $ 7[_7Q./ { p /2@-R,]aKJ]Q.ّ K 5b15S qRƱ㚏wbi!H2L~J$;(+݇IWŚ+›    ncc,i-o-x=hb|CgRI&pRXNkoyn( OXL/(FEV #U*FZZSؽoo.K |ƘIH$؏5❩XX>H4p:SQb.$>KNR>S9 c?/(%\;d3  B O|=e;Nmn&N,Pn&T)SؤP _pZ{ TW6lڂwEۂ(*GJrWNDCwtvU>d+*1+{m ,HHHƏ uSHHHH jdϗ9'jy2# q! }23E4XIzYN:t Ӹ~d'#X%%{Ќl̞9󐞑a,N& LIKMvfHHHH`_OY^~1L,\_rab){㑘\tuur'cX-nZv8$|Dl3~-(! L m\`ҸLIHHHH`|Хp\Ov>;*#Α nŲ&kdX[k|-gZ1Yǖ;S )8M0. %Pz;S~ad8?     X!xi1IHHHH 4A𡣝6w rbqz~W%_HHHHH BF 9mq'9a̸f$@$@$@$@c'Ue6v1---ML1*` ߔo1     lN7䓱)tM]Ysf$@$@$@$@$@N@-zǽ*`*m\     O@u=TS,v\.LhHHHHH *2Tę7sT崮ӺYy    :hPOOd(z{HMB/E5#           q%`W7xѠhb)55AfJ/<8 G. D4OCt=TS,֌t^KHHHHHHHHHHHH`<$N󠣵8y'! ǬLtuu-{-ڑw7vv% qpu:G⑛kކ68,IHHHHHHHHHHHb@\lEHHHHHHHHHHHHbK"HHHHHHHHHHHbK10HHHHHHHHHHHbK"HHHHHHHHHHHbK10HHHHHHHHHHHbK"HHHHHHHHHHHbK10HHHHHHHHHHHbK"HHHHHHHHHHHbK10HHHHHHHHHHHbK"HHHHHHHHHHHbK10HHHHHHHHHHHbK"HHHHHHHHHHHbK10HHHHHHHHHHHbK"HHHHHHHHHHHbK10HHHHHHHHHHHbK"HHHHHHHHHHHbK10HHHHHHHHHHHbH"IENDB`Mail-DMARC-1.20240314/lib000755000765000024 014574361234 13630 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail000755000765000024 014574361234 14512 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC.pm000444000765000024 4421014574361234 16054 0ustar00mattstaff000000000000package Mail::DMARC; use strict; use warnings; our $VERSION = '1.20240314'; use Carp; our $psl_loads = 0; use parent 'Mail::DMARC::Base'; require Mail::DMARC::Policy; require Mail::DMARC::Report; require Mail::DMARC::Result; require Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF; require Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM; sub new { my ( $class, @args ) = @_; croak "invalid args" if scalar @args % 2; my %args = @args; my $self = bless { config_file => 'mail-dmarc.ini', }, $class; my @keys = sort { $a eq 'config_file' ? -1 : $b eq 'config_file' ? 1 : ($a cmp $b) } keys %args; foreach my $key ( @keys ) { if ($self->can($key)) { $self->$key( $args{$key} ); } else { $self->{$key} = $args{$key}; } } return $self; } sub source_ip { return $_[0]->{source_ip} if 1 == scalar @_; croak "invalid source_ip" if !$_[0]->is_valid_ip( $_[1] ); return $_[0]->{source_ip} = $_[1]; } sub envelope_to { return $_[0]->{envelope_to} if 1 == scalar @_; croak "invalid envelope_to" if !$_[0]->is_valid_domain( $_[1] ); return $_[0]->{envelope_to} = $_[1]; } sub envelope_from { return $_[0]->{envelope_from} if 1 == scalar @_; croak "invalid envelope_from" if !$_[0]->is_valid_domain( $_[1] ); return $_[0]->{envelope_from} = $_[1]; } sub header_from { return $_[0]->{header_from} if 1 == scalar @_; croak "invalid header_from" if !$_[0]->is_valid_domain( $_[1] ); return $_[0]->{header_from} = lc $_[1]; } sub header_from_raw { return $_[0]->{header_from_raw} if 1 == scalar @_; #croak "invalid header_from_raw: $_[1]" if 'from:' ne lc substr($_[1], 0, 5); return $_[0]->{header_from_raw} = lc $_[1]; } sub local_policy { return $_[0]->{local_policy} if 1 == scalar @_; # TODO: document this, when and why it would be used return $_[0]->{local_policy} = $_[1]; } sub dkim { my ($self, @args) = @_; if (0 == scalar @args) { $self->_unwrap('dkim'); return $self->{dkim}; } # one shot if (1 == scalar @args) { # warn "one argument\n"; if (ref $args[0] eq 'CODE') { return $self->{dkim} = $args[0]; } if ( ref $args[0] eq 'ARRAY') { foreach my $d ( @{ $args[0] }) { push @{ $self->{dkim}}, Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM->new($d); } return $self->{dkim}; } if ( ref $args[0] eq 'Mail::DKIM::Verifier' ) { $self->_from_mail_dkim($args[0]); return $self->{dkim}; } }; #warn "iterative\n"; push @{ $self->{dkim}}, Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM->new(@args); return $self->{dkim}; } sub _from_mail_dkim { my ( $self, $dkim ) = @_; my $signatures = 0; # A DKIM verifier will have result and signature methods. foreach my $s ( $dkim->signatures ) { next if ref $s eq 'Mail::DKIM::DkSignature'; $signatures++; my $result = $s->result; if ($result eq 'invalid') { # See GH Issue #21 $result = 'temperror'; } push @{ $self->{dkim}}, Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM->new( domain => $s->domain, selector => $s->selector, result => $result, human_result => $s->result_detail, ); } if ($signatures < 1) { push @{ $self->{dkim}}, Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM->new( domain => '', result => 'none', ); } return; } sub _unwrap { my ( $self, $key ) = @_; if ($self->{$key} and ref $self->{$key} eq 'CODE') { my $code = delete $self->{$key}; $self->$key( $self->$code ); } return; } sub spf { my ($self, @args) = @_; if (0 == scalar @args) { $self->_unwrap('spf'); return $self->{spf}; } if (1 == scalar @args && ref $args[0] eq 'CODE') { return $self->{spf} = $args[0]; } if (1 == scalar @args && ref $args[0] eq 'ARRAY') { # warn "SPF one shot"; foreach my $d ( @{ $args[0] }) { push @{ $self->{spf} }, Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF->new($d); } return $self->{spf}; } #warn "SPF iterative"; push @{ $self->{spf} }, Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF->new(@args); return $self->{spf}; } sub policy { my ( $self, @args ) = @_; return $self->{policy} if ref $self->{policy} && 0 == scalar @args; return $self->{policy} = Mail::DMARC::Policy->new(@args); } sub report { my $self = shift; return $self->{report} if ref $self->{report}; return $self->{report} = Mail::DMARC::Report->new(); } sub result { my $self = shift; return $self->{result} if ref $self->{result}; return $self->{result} = Mail::DMARC::Result->new(); } sub is_subdomain { return $_[0]->{is_subdomain} if 1 == scalar @_; croak "invalid boolean" if 0 == grep {/^$_[1]$/ix} qw/ 0 1/; return $_[0]->{is_subdomain} = $_[1]; } sub get_report_window { my ( $self, $interval, $now ) = @_; my $min_interval = $self->config->{'report_sending'}{'min_interval'}; my $max_interval = $self->config->{'report_sending'}{'max_interval'}; $interval = 86400 if ! $interval; # Default to 1 day if ( $min_interval ) { $interval = $min_interval if $interval < $min_interval; } if ( $max_interval ) { $interval = $max_interval if $interval > $max_interval; } if ( ( 86400 % $interval ) != 0 ) { # Interval does not fit into a day nicely, # So don't work out a window, just run with it. return ( $now, $now + $interval - 1); } my $begin = $self->get_start_of_zulu_day( $now ); my $end = $begin + $interval - 1; while ( $end < $now ) { $begin = $begin + $interval; $end = $begin + $interval - 1; } return ( $begin, $end ); } sub get_start_of_zulu_day { my ( $self, $t ) = @_; my $start_of_zulu_day = $t - ( $t % 86400 ); return $start_of_zulu_day; } sub save_aggregate { my ($self) = @_; my $agg = $self->report->aggregate; # put config information in report metadata foreach my $f ( qw/ org_name email extra_contact_info report_id / ) { $agg->metadata->$f( $self->config->{organization}{$f} ); }; my ( $begin, $end ) = $self->get_report_window( $self->result->published->ri, $self->time ); $agg->metadata->begin( $begin ); $agg->metadata->end( $end ); $agg->policy_published( $self->result->published ); my $rec = Mail::DMARC::Report::Aggregate::Record->new(); $rec->row->source_ip( $self->source_ip ); $rec->identifiers( envelope_to => $self->envelope_to, envelope_from => $self->envelope_from, header_from => $self->header_from, ); $rec->auth_results->dkim($self->dkim); $rec->auth_results->spf($self->spf); $rec->row->policy_evaluated( disposition => $self->result->disposition, dkim => $self->result->dkim, spf => $self->result->spf, reason => $self->result->reason, ); $agg->record($rec); return $self->report->save_aggregate; } sub init { # used for testing my $self = shift; map { delete $self->{$_} } qw/ spf spf_ar dkim dkim_ar /; return; } 1; __END__ =pod =head1 Status Badges =for markdown [![Build Status](https://github.com/msimerson/mail-dmarc/actions/workflows/ci.yml/badge.svg)](https://github.com/msimerson/mail-dmarc/actions/workflows/ci.yml) =for markdown [![Coverage Status](https://coveralls.io/repos/msimerson/mail-dmarc/badge.svg)](https://coveralls.io/r/msimerson/mail-dmarc) =head1 NAME Mail::DMARC - Perl implementation of DMARC =head1 VERSION version 1.20240314 =head1 SYNOPSIS DMARC: Domain-based Message Authentication, Reporting and Conformance my $dmarc = Mail::DMARC::PurePerl->new( ... # see the documentation for the "new" method for required args ); my $result = $dmarc->validate(); if ( $result->result eq 'pass' ) { ...continue normal processing... return; }; # any result that did not pass is a fail. Now for disposition if ( $result->evalated->disposition eq 'reject' ) { ...treat the sender to a 550 ... }; if ( $result->evalated->disposition eq 'quarantine' ) { ...assign a bunch of spam points... }; if ( $result->evalated->disposition eq 'none' ) { ...continue normal processing... }; =head1 DESCRIPTION This module is a suite of tools for implementing DMARC. It adheres to the 2013 DMARC draft, intending to implement every MUST and every SHOULD. This module can be used by... =over 4 =item * MTAs and filtering tools like SpamAssassin to validate that incoming messages are aligned with the purported sender's policy. =item * email senders, to receive DMARC reports from other mail servers and display them via CLI and web interfaces. =item * MTA operators to send DMARC reports to DMARC author domains. =back When a message arrives via SMTP, the MTA or filtering application can pass in a small amount of metadata about the connection (envelope details, SPF and DKIM results) to Mail::DMARC. When the B method is called, Mail::DMARC will determine if: a. the header_from domain exists b. the header_from domain publishes a DMARC policy c. if a policy is published... d. does the message conform to the published policy? e. did the policy request reporting? If so, save details. The validation results are returned as a L object. If the author domain requested a report, it was saved to the L. The Store class includes a SQL implementation that is tested with SQLite, MySQL and PostgreSQL. There is more information available in the $result object. See L for complete details. Reports are viewed with the L program or with a web browser and the L program. Aggregate reports are sent to their requestors with the L program. For aggregate reports that you have been sent, the L program will parse the email messages (from IMAP, Mbox, or files) and save the report results into the L. The report store can use the same database to store reports you have received as well as reports you will send. There are several ways to identify the difference, including: =over 4 =item * received reports will have a null value for report_policy_published.rua =item * outgoing reports will have null values for report.uuid and report_record.count =back =head1 CLASSES L - the perl interface for DMARC L - a DMARC policy L - Pure Perl implementation of DMARC L - the results of applying policy L - Reporting: the R in DMARC =over 2 L - send reports via SMTP & HTTP L - receive and store reports from email, HTTP L - a persistent data store for aggregate reports L - CLI and CGI methods for viewing reports =back L - an XS implementation using libopendmarc =head1 METHODS =head2 new Create a DMARC object. my $dmarc = Mail::DMARC::PurePerl->new; Populate it. $dmarc->source_ip('192.0.1.1'); $dmarc->envelope_to('recipient.example.com'); $dmarc->envelope_from('sender.example.com'); $dmarc->header_from('sender.example.com'); $dmarc->dkim( $dkim_verifier ); $dmarc->spf([ { domain => 'example.com', scope => 'mfrom', result => 'pass', }, { scope => 'helo', domain => 'mta.example.com', result => 'fail', }, ]); Run the request: my $result = $dmarc->validate(); Alternatively, pass in all the required parameters in one shot: my $dmarc = Mail::DMARC::PurePerl->new( source_ip => '192.0.1.1', envelope_to => 'example.com', envelope_from => 'cars4you.info', header_from => 'yahoo.com', dkim => $dkim_results, # same format spf => $spf_results, # as previous example ); my $result = $dmarc->validate(); =head2 source_ip The remote IP that attempted sending the message. DMARC only uses this data for reporting to domains that request DMARC reports. =head2 envelope_to The domain portion of the RFC5321.RcptTo, (aka, the envelope recipient), and the bold portion in the following example: =over 8 RCPT TO:<user@B> =back =head2 envelope_from The domain portion of the RFC5321.MailFrom, (aka, the envelope sender). That is the the bold portion in the following example: =over 8 MAIL FROM:<user@B> =back =head2 header_from The domain portion of the RFC5322.From, aka, the From message header. =over 8 From: Ultimate Vacation <sweepstakes@B> =back You can instead pass in the entire From: header with header_from_raw. =head2 header_from_raw Retrieve the header_from domain by parsing it from a raw From field/header. The domain portion is extracted by L, which is fast, generally effective, but also rather crude. It has limits, so read the description. =head2 dkim If Mail::DKIM::Verifier was used to validate the message, just pass in the Mail::DKIM::Verifier object that processed the message: $dmarc->dkim( $dkim_verifier ); Otherwise, pass in an array reference. Each member of the DKIM array results represents a DKIM signature in the message and consists of the 4 keys shown in this example: $dmarc->dkim( [ { domain => 'example.com', selector => 'apr2013', result => 'fail', human_result=> 'fail (body has been altered)', }, { # 2nd signature, if present }, ] ); The dkim results can also be build iteratively by passing in key value pairs or hash references for each signature in the message: $dmarc->dkim( domain => 'sig1.com', result => 'fail' ); $dmarc->dkim( domain => 'sig2.com', result => 'pass' ); $dmarc->dkim( { domain => 'example.com', result => 'neutral' } ); Each hash or hashref is appended to the dkim array. Finally, you can pass a coderef which won't be called until the dkim method is used to read the dkim results. It must return an array reference as described above. The dkim result is an array reference. =head3 domain The d= parameter in the DKIM signature =head3 selector The s= parameter in the DKIM signature =head3 result The validation results of this signature. One of: none, pass, fail, policy, neutral, temperror, or permerror =head3 human result Additional information about the DKIM result. This is comparable to Mail::DKIM::Verifier->result_detail. =head2 spf The spf method works exactly the same as dkim. It accepts named arguments, a hashref, an arrayref, or a coderef: $dmarc->spf( domain => 'example.com', scope => 'mfrom', result => 'pass', ); The SPF domain and result are required for DMARC validation and the scope is used for reporting. =head3 domain The SPF checked domain =head3 scope The scope of the checked domain: mfrom, helo =head3 result The SPF result code: none, neutral, pass, fail, softfail, temperror, or permerror. =head1 DESIGN & GOALS =head2 Correct The DMARC spec is lengthy and evolving, making correctness a moving target. In cases where correctness is ambiguous, options are generally provided. =head2 Easy to use Providing an implementation of DMARC that SMTP utilities can utilize will aid DMARC adoption. The list of dependencies appears long because of reporting. If this module is used without reporting, the number of dependencies not included with perl is about 5. =head2 Maintainable Since DMARC is evolving, this implementation aims to be straight forward and easy to alter and extend. The programming style is primarily OO, which carries a small performance penalty but dividends in maintainability. When multiple options are available, such as when sending reports via SMTP or HTTP, calls should be made to the parent Send class to broker the request. When storing reports, calls are made to the Store class which dispatches to the SQL class. The idea is that if someone desired a data store other than those provided by perl's DBI class, they could easily implement their own. If you do, please fork it on GitHub and share. =head2 Fast If you deploy this in an environment where performance is insufficient, please profile the app and submit a report and preferably, patches. =head1 SEE ALSO L 2015-03 L DMARC L =head1 HISTORY The daddy of this perl module was a L. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 CONTRIBUTORS =for stopwords Benny Pedersen Jean Paul Galea Marisa Clardy Priyadi Iman Nurcahyo Ricardo Signes =over 4 =item * Benny Pedersen =item * Jean Paul Galea =item * Marisa Clardy =item * Priyadi Iman Nurcahyo =item * Ricardo Signes =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC000755000765000024 014574361234 15340 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Base.pm000444000765000024 2550714574361234 16736 0ustar00mattstaff000000000000package Mail::DMARC::Base; our $VERSION = '1.20240314'; use strict; use warnings; use 5.10.0; use Carp; use Config::Tiny; use File::ShareDir; use HTTP::Tiny; use IO::File; use Net::DNS::Resolver; use Net::IDN::Encode qw/domain_to_unicode/; use Net::IP; use Regexp::Common 2013031301 qw /net/; use Socket; use Socket6 qw//; # don't export symbols sub new { my ( $class, @args ) = @_; croak "invalid args" if scalar @args % 2 != 0; return bless { config_file => 'mail-dmarc.ini', @args, # this may override config_file }, $class; } my $_fake_time; sub time { ## no critic # Ability to return a fake time for testing my ( $self ) = @_; my $time = defined $Mail::DMARC::Base::_fake_time ? $Mail::DMARC::Base::_fake_time : time; return $time; } sub set_fake_time { my ( $self, $time ) = @_; $Mail::DMARC::Base::_fake_time = $time; return; } sub config { my ( $self, $file, @too_many ) = @_; croak "invalid args" if scalar @too_many; return $self->{config} if ref $self->{config} && !$file; return $self->{config} = $self->get_config($file); } sub get_prefix { my ($self, $subdir) = @_; return map { $_ . ($subdir ? $subdir : '') } qw[ /usr/local/ /opt/local/ / ./ ]; } sub get_sharefile { my ($self, $file) = @_; my $match = File::ShareDir::dist_file( 'Mail-DMARC', $file ); print "using $match for $file\n" if $self->verbose; return $match; } sub get_config { my $self = shift; my $file = shift || $ENV{MAIL_DMARC_CONFIG_FILE} || $self->{config_file} or croak; return Config::Tiny->read($file) if -r $file; # fully qualified foreach my $d ($self->get_prefix('etc')) { next if !-d $d; next if !-e "$d/$file"; croak "unreadable file: $d/$file" if !-r "$d/$file"; my $Config = Config::Tiny->new; return Config::Tiny->read("$d/$file"); } if ($file ne 'mail-dmarc.ini') { croak "unable to find requested config file $file\n"; } return Config::Tiny->read( $self->get_sharefile('mail-dmarc.ini') ); } sub any_inet_ntop { my ( $self, $ip_bin ) = @_; $ip_bin or croak "missing IP in request"; if ( length $ip_bin == 16 ) { return Socket6::inet_ntop( AF_INET6, $ip_bin ); } return Socket6::inet_ntop( AF_INET, $ip_bin ); } sub any_inet_pton { my ( $self, $ip_txt ) = @_; $ip_txt or croak "missing IP in request"; if ( $ip_txt =~ /:/ ) { return Socket6::inet_pton( AF_INET6, $ip_txt ) || croak "invalid IPv6: $ip_txt"; } return Socket6::inet_pton( AF_INET, $ip_txt ) || croak "invalid IPv4: $ip_txt"; } { my $public_suffixes; my $public_suffixes_stamp; sub get_public_suffix_list { my ( $self ) = @_; if ( $public_suffixes ) { return $public_suffixes; } no warnings 'once'; ## no critic $Mail::DMARC::psl_loads++; my $file = $self->find_psl_file(); $public_suffixes_stamp = ( stat( $file ) )[9]; open my $fh, '<:encoding(UTF-8)', $file or croak "unable to open $file for read: $!\n"; # load PSL into hash for fast lookups, esp. for long running daemons my %psl = map { $_ => 1 } grep { $_ !~ /^[\/\s]/ } # weed out comments & whitespace map { chomp($_); $_ } ## no critic, remove line endings <$fh>; close $fh; return $public_suffixes = \%psl; } sub check_public_suffix_list { my ( $self ) = @_; my $file = $self->find_psl_file(); my $new_public_suffixes_stamp = ( stat( $file ) )[9]; if ( $new_public_suffixes_stamp != $public_suffixes_stamp ) { $public_suffixes = undef; $self->get_public_suffix_list(); return 1; } return 0; } } sub is_public_suffix { my ( $self, $zone ) = @_; croak "missing zone name!" if !$zone; my $public_suffixes = $self->get_public_suffix_list(); $zone = domain_to_unicode( $zone ) if $zone =~ /xn--/; return 1 if $public_suffixes->{$zone}; my @labels = split /\./, $zone; $zone = join '.', '*', (@labels)[ 1 .. scalar(@labels) - 1 ]; return 1 if $public_suffixes->{$zone}; return 0; } sub update_psl_file { my ($self, $dryrun) = @_; my $psl_file = $self->find_psl_file(); die "No Public Suffix List file found\n" if ( ! $psl_file ); die "Public suffix list file $psl_file not found\n" if ( ! -f $psl_file ); die "Cannot write to Public Suffix List file $psl_file\n" if ( ! -w $psl_file ); my $url = 'https://publicsuffix.org/list/effective_tld_names.dat'; if ( $dryrun ) { print "Will attempt to update the Public Suffix List file at $psl_file (dryrun mode)\n"; return; } my $response = HTTP::Tiny->new->mirror( $url, $psl_file ); my $content = $response->{'content'}; if ( !$response->{'success'} ) { my $status = $response->{'status'}; die "HTTP Request for Public Suffix List file failed with error $status ($content)\n"; } else { if ( $response->{'status'} eq '304' ) { print "Public Suffix List file $psl_file not modified\n"; } else { print "Public Suffix List file $psl_file updated\n"; } } return; } sub find_psl_file { my ($self) = @_; my $file = $self->config->{dns}{public_suffix_list} || 'share/public_suffix_list'; if ( $file =~ /^\// && -f $file && -r $file ) { print "using $file for Public Suffix List\n" if $self->verbose; return $file; } my $path; foreach $path ($self->get_prefix('share/' . $file)) { ## no critic last if ( -f $path && -r $path ); } if ($path && -r $path) { print "using $path for Public Suffix List\n" if $self->verbose; return $path; }; # Fallback to included suffic list return $self->get_sharefile('public_suffix_list'); } sub has_dns_rr { my ( $self, $type, $domain ) = @_; my @matches; my $res = $self->get_resolver(); my $query = $res->query( $domain, $type ) or do { return 0 if ! wantarray; return @matches; }; for my $rr ( $query->answer ) { next if $rr->type ne $type; push @matches, $rr->type eq 'A' ? $rr->address : $rr->type eq 'PTR' ? $rr->ptrdname : $rr->type eq 'NS' ? $rr->nsdname : $rr->type eq 'TXT' ? $rr->txtdata : $rr->type eq 'SPF' ? $rr->txtdata : $rr->type eq 'AAAA' ? $rr->address : $rr->type eq 'MX' ? $rr->exchange : $rr->answer; } return scalar @matches if ! wantarray; return @matches; } sub epoch_to_iso { my ($self, $epoch) = @_; my @fields = localtime( $epoch ); my $ss = sprintf( "%02i", $fields[0] ); # seconds my $mn = sprintf( "%02i", $fields[1] ); # minutes my $hh = sprintf( "%02i", $fields[2] ); # hours (24 hour clock) my $dd = sprintf( "%02i", $fields[3] ); # day of month my $mm = sprintf( "%02i", $fields[4] + 1 ); # month my $yy = ( $fields[5] + 1900 ); # year return "$yy-$mm-$dd" .'T'."$hh:$mn:$ss"; } sub get_resolver { my $self = shift; my $timeout = shift || $self->config->{dns}{timeout} || 5; my $retrans = shift || $self->config->{dns}{retrans} || 5; return $self->{resolver} if defined $self->{resolver}; $self->{resolver} = Net::DNS::Resolver->new( dnsrch => 0 ); $self->{resolver}->tcp_timeout($timeout); $self->{resolver}->udp_timeout($timeout); $self->{resolver}->retrans($retrans); return $self->{resolver}; } sub set_resolver { my ($self,$resolver) = @_; $self->{resolver} = $resolver; return; } sub is_valid_ip { my ( $self, $ip ) = @_; # Using Regexp::Common removes perl 5.8 compat # Perl 5.008009 does not support the pattern $RE{net}{IPv6}. # You need Perl 5.01 or later if ( $ip =~ /:/ ) { return Net::IP->new( $ip, 6 ); } return Net::IP->new( $ip, 4 ); } sub is_valid_domain { my ( $self, $domain ) = @_; return 0 if $domain !~ /^$RE{net}{domain}{-rfc1101}{-nospace}$/x; my $tld = ( split /\./, lc $domain )[-1]; return 1 if $self->is_public_suffix($tld); $tld = join( '.', ( split /\./, $domain )[ -2, -1 ] ); return 1 if $self->is_public_suffix($tld); return 0; } sub is_valid_spf_scope { my ($self, $scope ) = @_; return lc $scope if grep { lc $scope eq $_ } qw/ mfrom helo /; carp "$scope is not a valid SPF scope"; return; } sub is_valid_spf_result { my ($self, $result ) = @_; return 1 if grep { lc $result eq $_ } qw/ fail neutral none pass permerror softfail temperror /; carp "$result is not a valid SPF result"; return; } sub slurp { my ( $self, $file ) = @_; open my $FH, '<', $file or croak "unable to read $file: $!"; my $contents = do { local $/; <$FH> }; ## no critic (Local) close $FH; return $contents; } sub verbose { return $_[0]->{verbose} if 1 == scalar @_; return $_[0]->{verbose} = $_[1]; } 1; __END__ =pod =head1 NAME Mail::DMARC::Base - DMARC utility functions =head1 VERSION version 1.20240314 =head1 METHODS =head2 is_public_suffix Determines if part of a domain is a Top Level Domain (TLD). Examples of TLDs are com, net, org, co.ok, am, and us. Determination is made by consulting a Public Suffix List. The included PSL is from mozilla.org. See http://publicsuffix.org/list/ for more information, and a link to download the latest PSL. =head2 update_psl_file Download a new Public Suffix List file from mozilla and update the installed file with the new copy. =head2 has_dns_rr Determine if a DNS Resource Record of the specified type exists at the DNS name provided. =head2 get_resolver Returns a (cached) Net::DNS::Resolver object =head2 set_resolver Set the Net::DNS::Resolver object to be used for lookups =head2 is_valid_ip Determines if the supplied IP address is a valid IPv4 or IPv6 address. =head2 is_valid_domain Determine if a string is a legal RFC 1034 or 1101 host name. Half the reason to test for domain validity is to shave seconds off our processing time by not having to process DNS queries for illegal host names. The other half is to raise exceptions if methods are being called incorrectly. =head1 SEE ALSO Mozilla Public Suffix List: http://publicsuffix.org/list/ =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/HTTP.pm000444000765000024 1344514574361234 16641 0ustar00mattstaff000000000000package Mail::DMARC::HTTP; our $VERSION = '1.20240314'; use strict; use warnings; use parent 'Net::Server::HTTP'; use CGI; use Data::Dumper; use File::ShareDir; use IO::Uncompress::Gunzip; use JSON -convert_blessed_universally; use URI; our $report; use Mail::DMARC::PurePerl; my %mimes = ( css => 'text/css', html => 'text/html', js => 'application/javascript', json => 'application/json', ); sub new { my $class = shift; return bless {}, $class; } sub dmarc_httpd { my $self = shift; $report = shift; my $port = $report->config->{http}{port} || 8080; my $ports = $report->config->{https}{port}; my $sslkey = $report->config->{https}{ssl_key}; my $sslcrt = $report->config->{https}{ssl_crt}; Net::Server::HTTP->run( app => sub { &dmarc_dispatch }, port => [$port, (($ports && $sslkey && $sslcrt) ? "$ports/ssl" : ()) ], ipv => '*', # IPv6 if available ($sslkey ? (SSL_key_file => $sslkey) : ()), ($sslcrt ? (SSL_cert_file => $sslcrt) : ()), log_file => 'Sys::Syslog', syslog_ident => 'mail_dmarc', syslog_facility => 'MAIL', ); return; } sub dmarc_dispatch { my $self = shift; # warn Dumper( { CGI->new->Vars } ); my $path = $self->{request_info}{request_path}; if ($path) { warn "path: $path\n"; return report_json_report() if $path eq '/dmarc/json/report'; return report_json_rr() if $path eq '/dmarc/json/row'; return serve_validator() if $path eq '/dmarc/json/validate'; return serve_file($path) if $path =~ /\.(?:js|css|html|gz)$/x; }; return serve_file('/dmarc/index.html'); } sub serve_pretty_error { my $error = shift || 'Sorry, that operation is not supported.'; return print <<"EO_ERROR" Content-Type: text/html

$error

EO_ERROR ; } sub return_json_error { my ($err) = @_; #warn $err; print JSON->new->utf8->encode( { err => $err } ); # to HTTP client print "\n"; return $err; # to caller } sub serve_validator { my $cgi = shift || CGI->new(); # passed in $cgi for testing my $resolver = shift; # passed in $resolver for testing my $json = JSON->new->utf8; print $cgi->header("application/json"); my $post = $cgi->param('POSTDATA'); if (!$post) { return return_json_error("missing POST data"); } my ($input, $dmpp, $res); eval { $input = $json->decode( $post ); }; if ($@) { return return_json_error($@); } if (!$input || !ref $input) { return return_json_error("invalid request $post"); } eval { $dmpp = Mail::DMARC::PurePerl->new( %$input ) }; if ($@) { return return_json_error($@); } $dmpp->set_resolver($resolver) if $resolver; eval { $res = $dmpp->validate(); }; if ($@) { return return_json_error($@); } my $return = $json->allow_blessed->convert_blessed->encode( $res ); print "$return\n"; return $return; } sub serve_file { my ($path) = @_; my @bits = split /\//, $path; shift @bits; return serve_pretty_error("file not found") if (!$bits[0] || 'dmarc' ne $bits[0]); shift @bits; $path = join '/', @bits; my $file = $bits[-1]; $file =~ s/[^[ -~]]//g; # strip out any non-printable chars my ($extension) = (split /\./, $file)[-1]; return serve_pretty_error("$extension not recognized") if ! $mimes{$extension}; my $dir = "share/html"; # distribution dir if ( ! -d $dir ) { $dir = File::ShareDir::dist_dir( 'Mail-DMARC' ); # installed loc. $dir .= "/html"; }; return serve_pretty_error("no such path") if ! $dir; return serve_gzip("$dir/$path.gz") if -f "$dir/$path.gz"; return serve_pretty_error("no such file") if ! -f "$dir/$path"; open my $FH, '<', "$dir/$path" or return serve_pretty_error( "unable to read $dir/$path: $!" ); print "Content-Type: $mimes{$extension}\n\n"; print <$FH>; close $FH; return 1; } sub serve_gzip { my $file = shift; open my $FH, '<', "$file" or return serve_pretty_error( "unable to read $file: $!" ); my $contents = do { local $/; <$FH> }; ## no critic (Local) close $FH; my $decomp = substr($file, 0, -3); # remove .gz suffix my ($extension) = (split /\./, $decomp)[-1]; # browser accepts gz encoding, serve compressed if ( grep {/gzip/} $ENV{HTTP_ACCEPT_ENCODING} ) { my $length = length $contents; return print <<"EO_GZ" Content-Length: $length Content-Type: $mimes{$extension} Content-Encoding: gzip $contents EO_GZ ; } # browser doesn't support gzip, decompress and serve my $out; IO::Uncompress::Gunzip::gunzip( \$contents => \$out ) or return serve_pretty_error( "unable to decompress" ); my $length = length $out; return print <<"EO_UNGZ" Content-Length: $length Content-Type: $mimes{$extension} $out EO_UNGZ ; } sub report_json_report { print "Content-type: application/json\n\n"; my $reports = $report->store->backend->get_report( CGI->new->Vars ); print encode_json $reports; return; } sub report_json_rr { print "Content-type: application/json\n\n"; my $row = $report->store->backend->get_rr( CGI->new->Vars ); print encode_json $row; # warn Dumper($row); return; } 1; __END__ =pod =head1 NAME Mail::DMARC::HTTP - view stored reports via HTTP =head1 VERSION version 1.20240314 =head1 SYNOPSIS See the POD docs / man page for L. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Policy.pm000444000765000024 3525314574361234 17322 0ustar00mattstaff000000000000package Mail::DMARC::Policy; use strict; use warnings; our $VERSION = '1.20240314'; use Carp; use Mail::DMARC::Report::URI; sub new { my ( $class, @args ) = @_; my $package = ref $class ? ref $class : $class; my $self = bless {}, $package; return $self if 0 == scalar @args; # no args, empty pol if (1 == @args) { # a string my $policy = $self->parse( $args[0] ); $self->is_valid($policy); return $policy; } croak "invalid arguments" if @args % 2 != 0; my $policy = {@args}; bless $policy, $package; croak "invalid policy" if !$self->is_valid($policy); return bless $policy, $package; } sub parse { my ( $self, $str, @junk ) = @_; croak "invalid parse request" if 0 != scalar @junk; my $cleaned = $str; $cleaned =~ s/\s//g; # remove whitespace $cleaned =~ s/\\;/;/g; # replace \; with ; $cleaned =~ s/;;/;/g; # replace ;; with ; $cleaned =~ s/;0;/;/g; # replace ;0; with ; chop $cleaned if ';' eq substr $cleaned, -1, 1; # remove a trailing ; my @tag_vals = split /;/, $cleaned; my %policy; my $warned = 0; foreach my $tv (@tag_vals) { my ($tag, $value) = split /=|:|-/, $tv, 2; if ( !defined $tag || !defined $value || $value eq '') { if (!$warned) { #warn "tv: $tv\n"; warn "invalid DMARC record, please post this message to\n" . "\thttps://github.com/msimerson/mail-dmarc/issues/39\n" . "\t$str\n"; } $warned++; next; } $policy{lc $tag} = $value; } return bless \%policy, ref $self; # inherited defaults + overrides } sub apply_defaults { my $self = shift; $self->adkim('r') if !defined $self->adkim; $self->aspf('r') if !defined $self->aspf; $self->fo(0) if !defined $self->fo; $self->ri(86400) if !defined $self->ri; $self->rf('afrf') if !defined $self->rf; # pct # default is 100%, but 100% -vs- not defined is different return 1; } sub v { return $_[0]->{v} if 1 == scalar @_; croak "unsupported DMARC version" if 'DMARC1' ne uc $_[1]; return $_[0]->{v} = $_[1]; } sub p { return $_[0]->{p} if 1 == scalar @_; croak "invalid p" if !$_[0]->is_valid_p( $_[1] ); return $_[0]->{p} = $_[1]; } sub sp { return $_[0]->{sp} if 1 == scalar @_; croak "invalid sp ($_[1])" if !$_[0]->is_valid_p( $_[1] ); return $_[0]->{sp} = $_[1]; } sub adkim { return $_[0]->{adkim} if 1 == scalar @_; croak "invalid adkim" if 0 == grep {/^\Q$_[1]\E$/ix} qw/ r s /; return $_[0]->{adkim} = $_[1]; } sub aspf { return $_[0]->{aspf} if 1 == scalar @_; croak "invalid aspf" if 0 == grep {/^\Q$_[1]\E$/ix} qw/ r s /; return $_[0]->{aspf} = $_[1]; } sub fo { return $_[0]->{fo} if 1 == scalar @_; croak "invalid fo: $_[1]" if $_[1] !~ /^[01ds](:[01ds])*$/ix; return $_[0]->{fo} = $_[1]; } sub rua { return $_[0]->{rua} if 1 == scalar @_; croak "invalid rua" if !$_[0]->is_valid_uri_list( $_[1] ); return $_[0]->{rua} = $_[1]; } sub ruf { return $_[0]->{ruf} if 1 == scalar @_; croak "invalid rua" if !$_[0]->is_valid_uri_list( $_[1] ); return $_[0]->{ruf} = $_[1]; } sub rf { return $_[0]->{rf} if 1 == scalar @_; foreach my $f ( split /,/, $_[1] ) { croak "invalid format: $f" if !$_[0]->is_valid_rf($f); } return $_[0]->{rf} = $_[1]; } sub ri { return $_[0]->{ri} if 1 == scalar @_; croak "not numeric ($_[1])!" if $_[1] =~ /\D/; croak "not an integer!" if $_[1] != int $_[1]; croak "out of range" if ( $_[1] < 0 || $_[1] > 4294967295 ); return $_[0]->{ri} = $_[1]; } sub pct { return $_[0]->{pct} if 1 == scalar @_; croak "not numeric ($_[1])!" if $_[1] =~ /\D/; croak "not an integer!" if $_[1] != int $_[1]; croak "out of range" if $_[1] < 0 || $_[1] > 100; return $_[0]->{pct} = $_[1]; } sub domain { return $_[0]->{domain} if 1 == scalar @_; return $_[0]->{domain} = $_[1]; } sub is_valid_rf { my ( $self, $f ) = @_; return ( grep {/^\Q$f\E$/i} qw/ iodef afrf / ) ? 1 : 0; } sub is_valid_p { my ( $self, $p ) = @_; croak "unspecified p" if !defined $p; return ( grep {/^\Q$p\E$/i} qw/ none reject quarantine / ) ? 1 : 0; } sub is_valid_uri_list { my ( $self, $str ) = @_; $self->{uri} ||= Mail::DMARC::Report::URI->new; my $uris = $self->{uri}->parse($str); return scalar @$uris; } sub is_valid { my ( $self, $obj ) = @_; $obj = $self if !$obj; croak "missing version specifier" if !$obj->{v}; croak "invalid version" if 'DMARC1' ne uc $obj->{v}; if ( !$obj->{p} ) { if ( $obj->{rua} && $self->is_valid_uri_list( $obj->{rua} ) ) { $obj->{p} = 'none'; } else { croak "missing policy action (p=)"; } } croak "invalid policy action" if !$self->is_valid_p( $obj->{p} ); # everything else is optional return 1; } 1; __END__ =pod =head1 NAME Mail::DMARC::Policy - a DMARC policy in object format =head1 VERSION version 1.20240314 =head1 SYNOPSIS my $pol = Mail::DMARC::Policy->new( 'v=DMARC1; p=none; rua=mailto:dmarc@example.com' ); print "not a valid DMARC version!" if $pol->v ne 'DMARC1'; print "take no action" if $pol->p eq 'none'; print "reject that unaligned message" if $pol->p eq 'reject'; print "do not send aggregate reports" if ! $pol->rua; print "do not send forensic reports" if ! $pol->ruf; =head1 EXAMPLES A DMARC record in DNS format looks like this: v=DMARC1; p=reject; adkim=s; aspf=s; rua=mailto:dmarc@example.com; pct=100; DMARC records are stored in TXT resource records in the DNS, at _dmarc.example.com. To retrieve a DMARC record for a domain: =head2 dig dig +short _dmarc.example.com TXT =head2 perlishly print $_->txtdata."\n" for Net::DNS::Resolver->new(dnsrch=>0)->send('_dmarc.example.com','TXT')->answer; =head2 dmarc_lookup dmarc_lookup example.com =head1 METHODS All methods validate their input against the 2013 DMARC specification. Attempts to set invalid values will throw exceptions. =head2 new Create a new empty policy: my $pol = Mail::DMARC::Policy->new; Create a new policy from named arguments: my $pol = Mail::DMARC::Policy->new( v => 'DMARC1', p => 'none', pct => 50, ); Create a new policy from a DMARC DNS resource record: my $pol = Mail::DMARC::Policy->new( 'v=DMARC1; p=reject; rua=mailto:dmarc@example.com; pct=50;' ); If a policy is passed in (the latter two examples), the resulting policy object will be an exact representation of the record as returned from DNS. =head2 apply_defaults Several of the DMARC tags (adkim,aspf,fo,ri,rf) have default values when not specified in the published DNS record. Calling I will apply those default values to the DMARC tags that were not specified in the DNS record. The resulting L object will be a perfect representation of the DMARC policy that is/was applied. =head2 parse Accepts a string containing a DMARC Resource Record, as it would be retrieved via DNS. my $pol = Mail::DMARC::Policy->new; $pol->parse( 'v=DMARC1; p=none; rua=mailto:dmarc@example.com' ); $pol->parse( 'v=DMARC1' ); # external reporting record =head1 Record Tags =head2 Tag Overview v=DMARC1; (version) p=none; (disposition policy : reject, quarantine, none (monitor)) sp=reject; (subdomain policy: same as p) adkim=s; (dkim alignment: s=strict, r=relaxed) aspf=r; (spf alignment: s=strict, r=relaxed) rua=mailto:dmarc-feedback@example.com; (aggregate reports) ruf=mailto:dmarc-feedback@example.com; (forensic reports) rf=afrf; (report format: afrf, iodef) ri=8400; (report interval) pct=50; (percent of messages to filter) =head2 Tags in Detail The descriptions of each DMARC record tag and its corresponding values is from the March 31, 2013 draft of the DMARC spec: https://datatracker.ietf.org/doc/draft-kucherawy-dmarc-base/?include_text=1 Each tag has a mutator that's a setter and getter. To set any of the tag values, pass in the new value. Examples: $pol->p('none'); set policy action to none print "do nothing" if $pol->p eq 'none'; get policy action =head2 v Version (plain-text; REQUIRED). Identifies the record retrieved as a DMARC record. It MUST have the value of "DMARC1". The value of this tag MUST match precisely; if it does not or it is absent, the entire retrieved record MUST be ignored. It MUST be the first tag in the list. =head2 p Requested Mail Receiver policy (plain-text; REQUIRED for policy records). Indicates the policy to be enacted by the Receiver at the request of the Domain Owner. Policy applies to the domain queried and to sub-domains unless sub-domain policy is explicitly described using the "sp" tag. This tag is mandatory for policy records only, but not for third-party reporting records (see Section 8.2). =head2 sp {R6} Requested Mail Receiver policy for subdomains (plain-text; OPTIONAL). Indicates the policy to be enacted by the Receiver at the request of the Domain Owner. It applies only to subdomains of the domain queried and not to the domain itself. Its syntax is identical to that of the "p" tag defined above. If absent, the policy specified by the "p" tag MUST be applied for subdomains. =head2 adkim (plain-text; OPTIONAL, default is "r".) Indicates whether or not strict DKIM identifier alignment is required by the Domain Owner. If and only if the value of the string is "s", strict mode is in use. See Section 4.3.1 for details. =head2 aspf (plain-text; OPTIONAL, default is "r".) Indicates whether or not strict SPF identifier alignment is required by the Domain Owner. If and only if the value of the string is "s", strict mode is in use. See Section 4.3.2 for details. =head2 fo Failure reporting options (plain-text; OPTIONAL, default "0")) Provides requested options for generation of failure reports. Report generators MAY choose to adhere to the requested options. This tag's content MUST be ignored if a "ruf" tag (below) is not also specified. The value of this tag is a colon-separated list of characters that indicate failure reporting options as follows: 0: Generate a DMARC failure report if all underlying authentication mechanisms failed to produce an aligned "pass" result. 1: Generate a DMARC failure report if any underlying authentication mechanism failed to produce an aligned "pass" result. d: Generate a DKIM failure report if the message had a signature that failed evaluation, regardless of its alignment. DKIM- specific reporting is described in [AFRF-DKIM]. s: Generate an SPF failure report if the message failed SPF evaluation, regardless of its alignment. SPF-specific reporting is described in [AFRF-SPF]. =head2 rua Addresses to which aggregate feedback is to be sent (comma- separated plain-text list of DMARC URIs; OPTIONAL). {R11} A comma or exclamation point that is part of such a DMARC URI MUST be encoded per Section 2.1 of [URI] so as to distinguish it from the list delimiter or an OPTIONAL size limit. Section 8.2 discusses considerations that apply when the domain name of a URI differs from that of the domain advertising the policy. See Section 15.6 for additional considerations. Any valid URI can be specified. A Mail Receiver MUST implement support for a "mailto:" URI, i.e. the ability to send a DMARC report via electronic mail. If not provided, Mail Receivers MUST NOT generate aggregate feedback reports. URIs not supported by Mail Receivers MUST be ignored. The aggregate feedback report format is described in Section 8.3. =head2 ruf Addresses to which message-specific failure information is to be reported (comma-separated plain-text list of DMARC URIs; OPTIONAL). {R11} If present, the Domain Owner is requesting Mail Receivers to send detailed failure reports about messages that fail the DMARC evaluation in specific ways (see the "fo" tag above). The format of the message to be generated MUST follow that specified in the "rf" tag. Section 8.2 discusses considerations that apply when the domain name of a URI differs from that of the domain advertising the policy. A Mail Receiver MUST implement support for a "mailto:" URI, i.e. the ability to send a DMARC report via electronic mail. If not provided, Mail Receivers MUST NOT generate failure reports. See Section 15.6 for additional considerations. =head2 rf Format to be used for message-specific failure reports (comma- separated plain-text list of values; OPTIONAL; default "afrf"). The value of this tag is a list of one or more report formats as requested by the Domain Owner to be used when a message fails both [SPF] and [DKIM] tests to report details of the individual failure. The values MUST be present in the registry of reporting formats defined in Section 14; a Mail Receiver observing a different value SHOULD ignore it, or MAY ignore the entire DMARC record. Initial default values are "afrf" (defined in [AFRF]) and "iodef" (defined in [IODEF]). See Section 8.4 for details. =head2 ri Interval requested between aggregate reports (plain-text, 32-bit unsigned integer; OPTIONAL; default 86400). {R14} Indicates a request to Receivers to generate aggregate reports separated by no more than the requested number of seconds. DMARC implementations MUST be able to provide daily reports and SHOULD be able to provide hourly reports when requested. However, anything other than a daily report is understood to be accommodated on a best- effort basis. =head2 pct (plain-text integer between 0 and 100, inclusive; OPTIONAL; default is 100). {R8} Percentage of messages from the DNS domain's mail stream to which the DMARC mechanism is to be applied. However, this MUST NOT be applied to the DMARC-generated reports, all of which must be sent and received unhindered. The purpose of the "pct" tag is to allow Domain Owners to enact a slow rollout enforcement of the DMARC mechanism. The prospect of "all or nothing" is recognized as preventing many organizations from experimenting with strong authentication-based mechanisms. See Section 7.1 for details. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/PurePerl.pm000444000765000024 6425614574361234 17626 0ustar00mattstaff000000000000package Mail::DMARC::PurePerl; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; use parent 'Mail::DMARC'; sub init { my $self = shift; $self->is_subdomain(0); $self->{header_from} = undef; $self->{header_from_raw} = undef; $self->{envelope_to} = undef; $self->{envelope_from} = undef; $self->{source_ip} = undef; $self->{policy} = undef; $self->{result} = undef; $self->{report} = undef; $self->{spf} = undef; $self->{dkim} = undef; return; } sub validate { my $self = shift; my $policy = shift; $self->result->result('fail'); # set a couple $self->result->disposition('none'); # defaults # 11.2.1 Extract RFC5322.From domain my $from_dom = $self->get_from_dom() or return $self->result; # 9.6. reject email if the domain appears to not exist $self->exists_in_dns() or return $self->result; $policy ||= $self->discover_policy(); # 11.2.2 Query DNS for DMARC policy $policy or return $self->result; # 3.5 Out of Scope DMARC has no "short-circuit" provision, such as # specifying that a pass from one authentication test allows one # to skip the other(s). All are required for reporting. eval { $self->is_dkim_aligned; }; # 11.2.3. DKIM signature verification checks eval { $self->is_spf_aligned; }; # 11.2.4. SPF validation checks my $aligned = $self->is_aligned(); # 11.2.5. identifier alignment checks if ($self->config->{report_store}{auto_save}) { eval { $self->save_aggregate(); }; } return $self->result if $aligned; my $effective_p = $self->is_subdomain && defined $policy->sp ? $policy->sp : $policy->p; # 11.2.6 Apply policy. Emails that fail the DMARC mechanism check are # disposed of in accordance with the discovered DMARC policy of the # Domain Owner. See Section 6.2 for details. if ( lc $effective_p eq 'none' ) { return $self->result; } return $self->result if $self->is_whitelisted; # 7.1. Policy Fallback Mechanism # If the "pct" tag is present in a policy record, application of policy # is done on a selective basis. if ( !defined $policy->pct ) { $self->result->disposition($effective_p); return $self->result; } # The stated percentage of messages that fail the DMARC test MUST be # subjected to whatever policy is selected by the "p" or "sp" tag if ( int( rand(100) ) < $policy->pct ) { $self->result->disposition($effective_p); return $self->result; } $self->result->reason( type => 'sampled_out' ); # Those that are not thus selected MUST instead be subjected to the next # policy lower in terms of severity. In decreasing order of severity, # the policies are "reject", "quarantine", and "none". $self->result->disposition( ( $effective_p eq 'reject' ) ? 'quarantine' : 'none' ); return $self->result; } sub save_aggregate { my ( $self ) = @_; my $pol; eval { $pol = $self->result->published; }; if ( $pol && $self->has_valid_reporting_uri($pol->rua) ) { my @valid_report_uris = $self->get_valid_reporting_uri($pol->rua); my $filtered_report_uris = join( ',', map { $_->{'uri'} . ( ( $_->{'max_bytes'} > 0 ) ? ( '!' . $_->{'max_bytes'} ) : q{} ) } @valid_report_uris ); $self->result->published->rua( $filtered_report_uris ); return $self->SUPER::save_aggregate(); } return; } sub discover_policy { my $self = shift; my $from_dom = shift || $self->header_from or croak; print "Header From: $from_dom\n" if $self->verbose; my $org_dom = $self->get_organizational_domain($from_dom); # 9.1 Mail Receivers MUST query the DNS for a DMARC TXT record my ($matches, $at_dom) = $self->fetch_dmarc_record( $from_dom, $org_dom ); if (0 == scalar @$matches ) { $self->result->result('none'); $self->result->reason( type => 'other', comment => 'no policy' ); return; }; # 9.5. If the remaining set contains multiple records, processing # terminates and the Mail Receiver takes no action. if ( scalar @$matches > 1 ) { $self->result->reason( type => 'other', comment => "too many policies" ); print "Too many DMARC records\n" if $self->verbose; return; } my $policy; if (!$at_dom) { $at_dom = $from_dom; } my $policy_str = "domain=$at_dom;" . $matches->[0]; # prefix with domain eval { $policy = $self->policy( $policy_str ) } or return; if ($@) { $self->result->reason( type => 'other', comment => "policy parse error: $@" ); return; }; $self->result->published($policy); # 9.6 If a retrieved policy record does not contain a valid "p" tag, or # contains an "sp" tag that is not valid, then: if ( !$policy->p || !$policy->is_valid_p( $policy->p ) || ( defined $policy->sp && !$policy->is_valid_p( $policy->sp ) ) ) { # A. if an "rua" tag is present and contains at least one # syntactically valid reporting URI, the Mail Receiver SHOULD # act as if a record containing a valid "v" tag and "p=none" # was retrieved, and continue processing; # B. otherwise, the Mail Receiver SHOULD take no action. if ( !$policy->rua || !$self->has_valid_reporting_uri( $policy->rua ) ) { $self->result->reason( type => 'other', comment => "no valid rua" ); return; } $policy->v('DMARC1'); $policy->p('none'); } return $policy; } sub is_aligned { my $self = shift; # 11.2.5 Conduct identifier alignment checks. With authentication checks # and policy discovery performed, the Mail Receiver checks if # Authenticated Identifiers fall into alignment as decribed in # Section 4. If one or more of the Authenticated Identifiers align # with the RFC5322.From domain, the message is considered to pass # the DMARC mechanism check. All other conditions (authentication # failures, identifier mismatches) are considered to be DMARC # mechanism check failures. if ( 'pass' eq $self->result->spf || 'pass' eq $self->result->dkim ) { $self->result->result('pass'); $self->result->disposition('none'); return 1; } return 0; } sub is_dkim_aligned { my $self = shift; $self->result->dkim('fail'); # our 'default' result $self->get_dkim_pass_sigs() or return; # 11.2.3 Perform DKIM signature verification checks. A single email may # contain multiple DKIM signatures. The results MUST include the # value of the "d=" tag from all DKIM signatures that validated. my $from_dom = $self->header_from or croak "header_from not set!"; my $policy = $self->policy or croak "no policy!?"; my $from_org = $self->get_organizational_domain(); # Required in report: DKIM-Domain, DKIM-Identity, DKIM-Selector foreach my $dkim_ref ( $self->get_dkim_pass_sigs() ) { my $dkim_dom = lc $dkim_ref->{domain}; my $dkmeta = { domain => $dkim_ref->{domain}, selector => $dkim_ref->{selector}, identity => '', # TODO, what is this? }; if ( $dkim_dom eq $from_dom ) { # strict alignment requires exact match $self->result->dkim('pass'); $self->result->dkim_align('strict'); $self->result->dkim_meta($dkmeta); last; } # don't try relaxed if policy specifies strict next if $policy->adkim && 's' eq lc $policy->adkim; # don't try relaxed if we already got a strict match next if 'pass' eq $self->result->dkim; # relaxed policy (default): Org. Dom must match a DKIM sig my $dkim_org = $self->get_organizational_domain($dkim_dom); if ( $dkim_org eq $from_org ) { $self->result->dkim('pass'); $self->result->dkim_align('relaxed'); $self->result->dkim_meta($dkmeta); } } return 1 if 'pass' eq lc $self->result->dkim; return; } sub is_spf_aligned { my $self = shift; my $spf_dom = shift; if ( !$spf_dom && !$self->spf ) { croak "missing SPF!"; } if ( !$spf_dom ) { my @passes = grep { $_->{result} && $_->{result} =~ /pass/i } @{ $self->spf }; if (scalar @passes == 0) { $self->result->spf('fail'); return 0; }; my ($ref) = grep { $_->{scope} && $_->{scope} eq 'mfrom' } @passes; if (!$ref) { ($ref) = grep { $_->{scope} && $_->{scope} eq 'helo' } @passes; } if (!$ref) { ($ref) = $passes[0]; }; $spf_dom = $ref->{domain}; }; # 11.2.4 Perform SPF validation checks. The results of this step # MUST include the domain name from the RFC5321.MailFrom if SPF # evaluation returned a "pass" result. $self->result->spf('fail'); return 0 if !$spf_dom; my $from_dom = $self->header_from or croak "header_from not set!"; if ( $spf_dom eq $from_dom ) { $self->result->spf('pass'); $self->result->spf_align('strict'); return 1; } # don't try relaxed match if strict policy requested if ( $self->policy->aspf && 's' eq lc $self->policy->aspf ) { return 0; } if ( $self->get_organizational_domain($spf_dom) eq $self->get_organizational_domain($from_dom) ) { $self->result->spf('pass'); $self->result->spf_align('relaxed'); return 1; } return 0; } sub is_whitelisted { my $self = shift; my $s_ip = shift || $self->source_ip; return if ! defined $s_ip; if ( ! $self->{_whitelist} ) { my $white_file = $self->config->{smtp}{whitelist} or return; return if ! -f $white_file || ! -r $white_file; foreach my $line ( split /\n/, $self->slurp($white_file) ) { next if $line =~ /^#/; # ignore comments my ($lip,$reason) = split /\s+/, $line, 2; next if not defined $lip; $self->{_whitelist}{$lip} = $reason; }; }; return if ! $self->{_whitelist}{$s_ip}; my ($type, $comment) = split /\s+/, $self->{_whitelist}{$s_ip}, 2; $self->result->disposition('none'); $self->result->reason( type => $type, ($comment && $comment =~ /\S/ ? ('comment' => $comment) : () ), ); return $type; } sub has_valid_reporting_uri { my ( $self, $rua ) = @_; my @valid_reporting_uris = $self->get_valid_reporting_uri( $rua ); return scalar @valid_reporting_uris; } sub get_valid_reporting_uri { my ( $self, $rua ) = @_; return unless $rua; my $recips_ref = $self->report->uri->parse($rua); my @has_permission; foreach my $uri_ref (@$recips_ref) { if ( !$self->external_report( $uri_ref->{uri} ) ) { push @has_permission, $uri_ref; next; } my $ext = $self->verify_external_reporting($uri_ref); push @has_permission, $uri_ref if $ext; } return @has_permission; } sub get_dkim_pass_sigs { my $self = shift; my $dkim_sigs = $self->dkim or return (); # message not signed if ( 'ARRAY' ne ref $dkim_sigs ) { croak "dkim needs to be an array reference!"; } return grep { 'pass' eq lc $_->{result} } @$dkim_sigs; } sub get_organizational_domain { my $self = shift; my $from_dom = shift || $self->header_from or croak "missing header_from!"; # 4.1 Acquire a "public suffix" list, i.e., a list of DNS domain # names reserved for registrations. http://publicsuffix.org/list/ # 4.2 Break the subject DNS domain name into a set of "n" ordered # labels. Number these labels from right-to-left; e.g. for # "example.com", "com" would be label 1 and "example" would be # label 2.; my @labels = reverse split /\./, lc $from_dom; # 4.3 Search the public suffix list for the name that matches the # largest number of labels found in the subject DNS domain. Let # that number be "x". my $greatest = 0; for ( my $i = 0; $i <= scalar @labels; $i++ ) { next if !$labels[$i]; my $tld = join '.', reverse( (@labels)[ 0 .. $i ] ); if ( $self->is_public_suffix($tld) ) { $greatest = $i + 1; } } if ( $greatest == scalar @labels ) { # same return $from_dom; } # 4.4 Construct a new DNS domain name using the name that matched # from the public suffix list and prefixing to it the "x+1"th # label from the subject domain. This new name is the # Organizational Domain. my $org_dom = join '.', reverse( (@labels)[ 0 .. $greatest ] ); print "Organizational Domain: $org_dom\n" if $self->verbose; return $org_dom; } sub exists_in_dns { my $self = shift; my $from_dom = shift || $self->header_from or croak "no header_from!"; # rfc7489 6.6.3 # If the set produced by the mechanism above contains no DMARC policy # record (i.e., any indication that there is no such record as opposed # to a transient DNS error), Mail Receivers SHOULD NOT apply the DMARC # mechanism to the message. my $org_dom = $self->get_organizational_domain($from_dom); my @todo = $from_dom; if ( $from_dom ne $org_dom ) { push @todo, $org_dom; $self->is_subdomain(1); } my $matched = 0; foreach (@todo) { last if $matched; $matched++ and next if $self->has_dns_rr( 'MX', $_ ); $matched++ and next if $self->has_dns_rr( 'NS', $_ ); $matched++ and next if $self->has_dns_rr( 'A', $_ ); $matched++ and next if $self->has_dns_rr( 'AAAA', $_ ); } if ( !$matched ) { $self->result->result('none'); $self->result->disposition('none'); $self->result->reason( type => 'other', comment => "$from_dom not in DNS" ); } return $matched; } sub fetch_dmarc_record { my ( $self, $zone, $org_dom ) = @_; # 1. Mail Receivers MUST query the DNS for a DMARC TXT record at the # DNS domain matching the one found in the RFC5322.From domain in # the message. A possibly empty set of records is returned. $self->is_subdomain( defined $org_dom ? 0 : 1 ); my @matches = (); my $query = $self->get_resolver->send( "_dmarc.$zone", 'TXT' ) or return (\@matches, $zone); for my $rr ( $query->answer ) { next if $rr->type ne 'TXT'; # 2. Records that do not start with a "v=" tag that identifies the # current version of DMARC are discarded. next if 'v=dmarc1' ne lc substr( $rr->txtdata, 0, 8 ); print "\n" . $rr->txtdata . "\n\n" if $self->verbose; push @matches, join( '', $rr->txtdata ); # join long records } if (scalar @matches) { return \@matches, $zone; # found one! (at least) } # 3. If the set is now empty, the Mail Receiver MUST query the DNS for # a DMARC TXT record at the DNS domain matching the Organizational # Domain in place of the RFC5322.From domain in the message (if # different). This record can contain policy to be asserted for # subdomains of the Organizational Domain. if ( defined $org_dom ) { # <- recursion break if ( $org_dom ne $zone ) { return $self->fetch_dmarc_record($org_dom); # <- recursion } } return \@matches, $zone; } sub get_from_dom { my ($self) = @_; return $self->header_from if $self->header_from; my $header = $self->header_from_raw or do { $self->result->reason( type => 'other', comment => "no header_from" ); return; }; # TODO: the From header can contain multiple addresses and should be # parsed as described in RFC 2822. If From has multiple-addresses, # then parse and use the domain in the Sender header. # This returns only the domain in the last email address. # Caller can pass in pre-parsed from_dom if this doesn't suit them. # # I care only about the domain. This is way faster than RFC2822 parsing my ($from_dom) = ( split /@/, $header )[-1]; # grab everything after the @ ($from_dom) = split /(\s+|>)/, lc $from_dom; # remove trailing cruft if ( !$from_dom ) { $self->result->reason( type => 'other', comment => "invalid header_from: ($header)" ); return; } return $self->header_from($from_dom); } sub external_report { my ( $self, $uri ) = @_; my $dmarc_dom = $self->result->published->domain or croak "published policy not tagged!"; if ( 'mailto' eq $uri->scheme ) { my $dest_email = lc $uri->path; my ($dest_host) = ( split /@/, $dest_email )[-1]; if ( $self->get_organizational_domain( $dest_host ) eq $self->get_organizational_domain( $dmarc_dom ) ) { print "$dest_host not external for $dmarc_dom\n" if $self->verbose; return 0; }; print "$dest_host is external for $dmarc_dom\n" if $self->verbose; } if ( 'http' eq $uri->scheme ) { if ($uri->host eq $dmarc_dom ) { print $uri->host ." not external for $dmarc_dom\n" if $self->verbose; return 0; }; print $uri->host ." is external for $dmarc_dom\n" if $self->verbose; } return 1; } sub verify_external_reporting { my $self = shift; my $uri_ref = shift or croak "missing URI"; # 1. Extract the host portion of the authority component of the URI. # Call this the "destination host". my $dmarc_dom = $self->result->published->domain or croak "published policy not tagged!"; my $dest_email = $uri_ref->{uri}->path or croak("invalid URI"); my ($dest_host) = ( split /@/, $dest_email )[-1]; # 2. Prepend the string "_report._dmarc". # 3. Prepend the domain name from which the policy was retrieved, # after conversion to an A-label if needed. my $dest = join '.', $dmarc_dom, '_report._dmarc', $dest_host; # 4. Query the DNS for a TXT record at the constructed name. my $query = $self->get_resolver->send( $dest, 'TXT' ) or do { print "\tquery for $dest failed\n" if $self->verbose; return; }; # 5. For each record, parse the result...same overall format: # "v=DMARC1" tag is mandatory and MUST appear first in the list. my @matches; for my $rr ( $query->answer ) { next if $rr->type ne 'TXT'; next if 'v=dmarc1' ne lc substr( $rr->txtdata, 0, 8 ); my $policy = undef; my $dmarc_str = join( '', $rr->txtdata ); # join parts eval { $policy = $self->policy->parse($dmarc_str) }; ## no critic (Eval) push @matches, $policy ? $policy : $dmarc_str; } # 6. If the result includes no TXT resource records...stop if ( !scalar @matches ) { print "\tno TXT match for $dest\n" if $self->verbose; return; }; # 7. If > 1 TXT resource record remains, external reporting authorized # 8. If a "rua" or "ruf" tag is discovered, replace the # corresponding value with the one found in this record. my @overrides = grep { ref $_ && $_->{rua} } @matches; foreach my $or (@overrides) { my $recips_ref = $self->report->uri->parse( $or->{rua} ) or next; if ( ( split /@/, $recips_ref->[0]{uri} )[-1] eq ( split /@/, $uri_ref->{uri} )[-1] ) { # the overriding URI MUST use the same destination host from the first step. print "found override RUA: $or->{rua}\n" if $self->verbose; $self->result->published->rua( $or->{rua} ); } } return @matches; } 1; __END__ =pod =head1 NAME Mail::DMARC::PurePerl - Pure Perl implementation of DMARC =head1 VERSION version 1.20240314 =head1 METHODS =head2 init Reset the Mail::DMARC object, preparing it for a fresh request. =head2 validate This method does the following: =over 4 * check if the RFC5322.From domain exists (exists_in_dns) * query DNS for a DMARC policy (discover_policy) * check DKIM alignment (is_dkim_aligned) * check SPF alignment (is_spf_aligned) * determine DMARC alignment (is_aligned) * calculate the I DMARC policy * apply the DMARC policy (see L) =back =head2 discover_policy Query the DNS to determine if a DMARC policy exists. When the domain name in the email From header (header_from) is not an Organizational Domain (ex: www.example.com), an attempt is made to determine the O.D. using the Mozilla Public Suffix List. When the O.D. differs from the header_from, a second DNS query is sent to _dmarc.[O.D.]. If a DMARC DNS record is found, it is parsed as a L object and returned. =head2 is_aligned Determine if this message is DMARC aligned. To pass this test, the message must pass at least one of the alignment test (DKIM or SPF). =head2 is_dkim_aligned Determine if a valid DKIM signature in the message is aligned with the message's From header domain. This match can be in strict (exact match) or relaxed (subdomains match) alignment. =head2 is_spf_aligned Same as DKIM, but for SPF. =head2 has_valid_reporting_uri Check for the presence of a valid reporting URI in the rua or ruf DMARC policy tags. =head2 get_organizational_domain From the 2013 DMARC spec, section 4: Organizational Domain: ..is the domain that was registered with a domain name registrar. Heuristics are used to determine this... =head2 exists_in_dns Determine if a domain exists, reliably. The DMARC draft says: 9.6 If the RFC5322.From domain does not exist in the DNS, Mail Receivers SHOULD direct the receiving SMTP server to reject the message {R9}. And in Appendix A.4: A common practice among MTA operators, and indeed one documented in [ADSP], is a test to determine domain existence prior to any more expensive processing. This is typically done by querying the DNS for MX, A or AAAA resource records for the name being evaluated, and assuming the domain is non-existent if it could be determined that no such records were published for that domain name. The original pre-standardization version of this protocol included a mandatory check of this nature. It was ultimately removed, as the method's error rate was too high without substantial manual tuning and heuristic work. There are indeed use cases this work needs to address where such a method would return a negative result about a domain for which reporting is desired, such as a registered domain name that never sends legitimate mail and thus has none of these records present in the DNS. I went back to the ADSP (which led me to the ietf-dkim email list where some 'experts' failed to agree on The Right Way to test domain validity. They pointed out: MX records aren't mandatory, and A or AAAA aren't reliable. Some experimentation proved both arguments in real world usage. This module tests for existence by searching for a MX, NS, A, or AAAA record. Since this search may be repeated for the Organizational Name, if the NS query fails, there is no delegation from the TLD. That has proven very reliable. =head2 fetch_dmarc_record Query the DNS for the presence of a DMARC record at the header from domain name and the Organizational Domain name. Returns the discovered DNS record answers. =head2 get_from_dom Returns the header_from attribute, if defined. When header_from is not defined, crudely, and very quickly parse a From header and return the domain name (aka, the header_from domain). The From header format is defined in RFC 822 and is very complex. The From header can contain multiple email addresses, each with different domains. This method returns the last one. If you want to handle this differently, parse the From header yourself and set header_from. =head2 external_report Determine if a report URL is external. If the domain name portion of the URI is not the same as the domain where the DMARC record was discovered, the report address is considered external. =head2 verify_external_reporting =head3 8.2. Verifying External Destinations It is possible to specify destinations for the different reports that are outside the domain making the request. This is enabled to allow domains that do not have mail servers to request reports and have them go someplace that is able to receive and process them. Without checks, this would allow a bad actor to publish a DMARC policy record that requests reports be sent to a victim address, and then send a large volume of mail that will fail both DKIM and SPF checks to a wide variety of destinations, which will in turn flood the victim with unwanted reports. Therefore, a verification mechanism is included. When a Mail Receiver discovers a DMARC policy in the DNS, and the domain at which that record was discovered is not identical to the host part of the authority component of a [URI] specified in the "rua" or "ruf" tag, the following verification steps SHOULD be taken: 1. Extract the host portion of the authority component of the URI. Call this the "destination host". 2. Prepend the string "_report._dmarc". 3. Prepend the domain name from which the policy was retrieved, after conversion to an A-label if needed. 4. Query the DNS for a TXT record at the constructed name. 5. For each record, parse the result...same overall format: "v=DMARC1" tag is mandatory and MUST appear first in the list. 6. If the result includes no TXT resource records...stop 7. If > 1 TXT resource record remains, external reporting authorized 8. If a "rua" or "ruf" tag is discovered, replace the corresponding value with the one found in this record. The overriding URI MUST use the same destination host from the first step. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report.pm000444000765000024 1557414574361234 17342 0ustar00mattstaff000000000000package Mail::DMARC::Report; use strict; use warnings; our $VERSION = '1.20240314'; use Carp; use IO::Compress::Gzip; use IO::Compress::Zip; use parent 'Mail::DMARC::Base'; require Mail::DMARC::Report::Aggregate; require Mail::DMARC::Report::Send; require Mail::DMARC::Report::Store; require Mail::DMARC::Report::Receive; require Mail::DMARC::Report::URI; sub compress { my ( $self, $xml_ref ) = @_; croak "xml is not a reference!" if 'SCALAR' ne ref $xml_ref; my $shrunk; my $zipper = { gz => \&IO::Compress::Gzip::gzip, # 2013 draft zip => \&IO::Compress::Zip::zip, # legacy format }; # WARNING: changes here MAY require updates in SMTP::assemble_message # my $cf = ( time > 1372662000 ) ? 'gz' : 'zip'; # gz after 7/1/13 my $cf = 'gz'; $zipper->{$cf}->( $xml_ref, \$shrunk ) or croak "unable to compress: $!"; return $shrunk; } sub init { my $self = shift; delete $self->{dmarc}; delete $self->{aggregate}; return; } sub aggregate { my $self = shift; return $self->{aggregate} if ref $self->{aggregate}; return $self->{aggregate} = Mail::DMARC::Report::Aggregate->new(); } sub dmarc { my $self = shift; return $self->{dmarc}; } sub receive { my $self = shift; return $self->{receive} if ref $self->{receive}; return $self->{receive} = Mail::DMARC::Report::Receive->new; } sub sendit { my $self = shift; return $self->{sendit} if ref $self->{sendit}; return $self->{sendit} = Mail::DMARC::Report::Send->new(); } sub store { my $self = shift; return $self->{store} if ref $self->{store}; return $self->{store} = Mail::DMARC::Report::Store->new(); } sub uri { my $self = shift; return $self->{uri} if ref $self->{uri}; return $self->{uri} = Mail::DMARC::Report::URI->new(); } sub save_aggregate { my $self = shift; return $self->store->backend->save_aggregate( $self->aggregate ); } 1; __END__ =pod =head1 NAME Mail::DMARC::Report - A DMARC report interface =head1 VERSION version 1.20240314 =head1 DESCRIPTION DMARC reports are information that a DMARC implementing Mail Transfer Agent (MTA) sends to Author Domains and also something that an Author Domain owner receives from other DMARC implementing MTAs. Mail::DMARC supports both roles, as a sender and a receiver. There are two report types, L and forensic. =head1 Aggregate Reports See L =head2 Forensic Reports TODO =head2 Report Sender See L 1. store reports 2. bundle aggregated reports 3. format report in XML 4. gzip the XML 5. deliver report to Author Domain =head2 Report Receiver See L 1. accept reports via HTTP or SMTP 2. parse the compressed XML message 3. store the report 4. present stored data =head2 Verify External Destinations 1. Extract the host portion of the authority component of the URI. Call this the "destination host". 2. Prepend the string "_report._dmarc". 3. Prepend the domain name from which the policy was retrieved. 4. Query the DNS for a TXT record at the constructed name. If the result of this request is a temporary DNS error of some kind (e.g., a timeout), the Mail Receiver MAY elect to temporarily fail the delivery so the verification test can be repeated later. 5. If the result includes no TXT resource records or multiple TXT resource records, a positive determination of the external reporting relationship cannot be made; stop. 6. Parse the result, if any, as a series of "tag=value" pairs, i.e., the same overall format as the policy record. In particular, the "v=DMARC1" tag is mandatory and MUST appear first in the list. If at least that tag is present and the record overall is syntactically valid per Section 6.3, then the external reporting arrangement was authorized by the destination ADMD. 7. If a "rua" or "ruf" tag is thus discovered, replace the corresponding value extracted from the domain's DMARC policy record with the one found in this record. This permits the report receiver to override the report destination. However, to prevent loops or indirect abuse, the overriding URI MUST use the same destination host from the first step. =head1 ERROR REPORTS 12.2.4. Error Reports When a Mail Receiver is unable to complete delivery of a report via any of the URIs listed by the Domain Owner, the Mail Receiver SHOULD generate an error message. An attempt MUST be made to send this report to all listed "mailto" URIs and MAY also be sent to any or all other listed URIs. The error report MUST be formatted per [MIME]. A text/plain part MUST be included that contains field-value pairs such as those found in Section 2 of [DSN]. The fields required, which may appear in any order, are: Report-Date: A [MAIL]-formatted date expression indicating when the transport failure occurred. Report-Domain: The domain-name about which the failed report was generated. Report-ID: The Report-ID: that the report tried to use. Report-Size: The size, in bytes, of the report that was unable to be sent. This MUST represent the number of bytes that the Mail Receiver attempted to send. Where more than one transport system was attempted, the sizes may be different; in such cases, separate error reports MUST be generated so that this value matches the actual attempt that was made. For example, a "mailto" error report would be sent to the "mailto" URIs with one size, while the "https" reports might be POSTed to those URIs with a different size, as they have different transport and encoding requirements. Submitter: The domain-name representing the Mail Receiver that generated, but was unable to submit, the report. Submitting-URI: The URI(s) to which the Mail Receiver tried, but failed, to submit the report. An additional text/plain part MAY be included that gives a human- readable explanation of the above, and MAY also include a URI that can be used to seek assistance. [NOTE: A more rigorous syntax specification, including ABNF and possible registration of a new media type, will be added here when more operational experience is acquired.] =head1 AFRF reports =head1 IODEF reports https://datatracker.ietf.org/doc/draft-kucherawy-dmarc-base/?include_text=1 Section 3.5 Out of Scope: This first version of DMARC supports only a single reporting format. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Result.pm000444000765000024 1316514574361234 17337 0ustar00mattstaff000000000000package Mail::DMARC::Result; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; require Mail::DMARC::Result::Reason; sub new { my $class = shift; return bless { dkim => '', spf => '', reason => [], }, $class; } sub published { my ( $self, $policy ) = @_; if ( !$policy ) { if ( !defined $self->{published} ) { croak "no policy discovered. Did you validate(), or at least fetch_dmarc_record() first? Or inspected results to detect a 'No Results Found' type error?"; } return $self->{published}; } $policy->{domain} or croak "tag the policy object with a domain indicating where the DMARC record was found!"; return $self->{published} = $policy; } sub disposition { return $_[0]->{disposition} if 1 == scalar @_; croak "invalid disposition ($_[1]" if 0 == grep {/^$_[1]$/ix} qw/ reject quarantine none /; return $_[0]->{disposition} = $_[1]; } sub dkim { return $_[0]->{dkim} if 1 == scalar @_; croak "invalid dkim" if 0 == grep {/^$_[1]$/ix} qw/ pass fail /; return $_[0]->{dkim} = $_[1]; } sub dkim_align { return $_[0]->{dkim_align} if 1 == scalar @_; croak "invalid dkim_align" if 0 == grep {/^$_[1]$/ix} qw/ relaxed strict /; return $_[0]->{dkim_align} = $_[1]; } sub dkim_meta { return $_[0]->{dkim_meta} if 1 == scalar @_; return $_[0]->{dkim_meta} = $_[1]; } sub spf { return $_[0]->{spf} if 1 == scalar @_; croak "invalid spf" if 0 == grep {/^$_[1]$/ix} qw/ pass fail /; return $_[0]->{spf} = $_[1]; } sub spf_align { return $_[0]->{spf_align} if 1 == scalar @_; croak "invalid spf_align" if 0 == grep {/^$_[1]$/ix} qw/ relaxed strict /; return $_[0]->{spf_align} = $_[1]; } sub result { return $_[0]->{result} if 1 == scalar @_; croak "invalid result" if 0 == grep {/^$_[1]$/ix} qw/ pass fail none /; return $_[0]->{result} = $_[1]; } sub reason { my ($self, @args) = @_; return $self->{reason} if ! scalar @args; push @{ $self->{reason}}, Mail::DMARC::Result::Reason->new(@args); return $self->{reason}; } 1; __END__ =pod =head1 NAME Mail::DMARC::Result - an aggregate report result object =head1 VERSION version 1.20240314 =head1 OVERVIEW A L object is the product of instantiating a L object, populating the variables, and running $dmarc->validate. The results object looks like this: result => 'pass', # pass, fail disposition => 'none', # reject, quarantine, none reason => [ # there can be many reasons... { type => '', # forwarded, sampled_out, trusted_forwarder, comment => '', # mailing_list, local_policy, other }, ], dkim => 'pass', # pass, fail dkim_align => 'strict', # strict, relaxed spf => 'pass', # pass, fail spf_align => 'strict', # strict, relaxed published => L, Reasons are optional and may not be present. The dkim_align and spf_align fields will only be present if the corresponding test value equals pass. They are additional info not specified by the DMARC spec. =head1 METHODS =head2 published Published is a L tagged with a domain. The domain attribute is the DNS domain name where the DMARC record was found. This may not be the same as the header_from domain (ex: bounces.amazon.com -vs- amazon.com). =head2 result Whether the message passed the DMARC test. Possible values are: pass, fail. In order to pass, at least one authentication alignment must pass. The 2013 draft defines two authentication methods: DKIM and SPF. The list is expected to grow. =head2 disposition When the DMARC result is not I, disposition is the results of applying DMARC policy to a message. Generally this is the same as the header_from domains published DMARC L. When it is not, the reason SHOULD be specified. =head2 dkim Whether the message passed or failed DKIM alignment. In order to pass the DMARC DKIM alignment test, a DKIM signature that matches the RFC5322.From domain must be present. An unsigned messsage, a message with an invalid signature, or signatures that don't match the RFC5322.From field are all considered failures. =head2 dkim_align If the message passed the DKIM alignment test, this indicates whether the alignment was strict or relaxed. =head2 spf Whether the message passed or failed SPF alignment. To pass SPF alignment, the RFC5321.MailFrom domain must match the RFC5322.From field. =head2 spf_align If the message passed the SPF alignment test, this indicates whether the alignment was strict or relaxed. =head2 reason If the applied policy differs from the sites published policy, the result policy should contain a reason and optionally a comment. A DMARC result reason has two attributes, type, and comment. reason => { type => '', comment => '', }, =head3 type The following reason types are defined and valid: forwarded sampled_out trusted_forwarder mailing_list local_policy other =head3 comment Comment is a free form text field. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report000755000765000024 014574361234 16613 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate.pm000444000765000024 2447714574361234 21232 0ustar00mattstaff000000000000package Mail::DMARC::Report::Aggregate; use strict; use warnings; our $VERSION = '1.20240314'; use Carp; use Data::Dumper; use XML::LibXML; use parent 'Mail::DMARC::Base'; use Mail::DMARC::Report::Aggregate::Metadata; sub metadata { my $self = shift; return $self->{metadata} if ref $self->{metadata}; return $self->{metadata} = Mail::DMARC::Report::Aggregate::Metadata->new; } sub policy_published { my ( $self, $policy ) = @_; return $self->{policy_published} if ! $policy; croak "not a policy object!" if 'Mail::DMARC::Policy' ne ref $policy; return $self->{policy_published} = $policy; } sub record { ## no critic (Ambiguous) my ($self, $record, @extra) = @_; if ( !$record) { return $self->{record} || []; } if (@extra) { croak "invalid args"; } if ('Mail::DMARC::Report::Aggregate::Record' ne ref $record) { croak "not a record object"; } $self->{record} ||= []; push @{ $self->{record} }, $record; return $self->{record}; }; sub dump_report { my $self = shift; carp Dumper( $self->{metadata}, $self->{policy_published}, $self->{record} ); return; } sub as_xml { my $self = shift; my $meta = $self->metadata->as_xml; my $pubp = $self->get_policy_published_as_xml; my $reco = $self->get_record_as_xml; return <<"EO_XML" \t1.0 $meta $pubp $reco EO_XML ; } sub get_record_as_xml { my $self = shift; my $rec_xml = ''; foreach my $rec ( @{ $self->{record} } ) { $rec_xml .= "\t\n"; my $ip = $rec->{row}{source_ip} or croak "no source IP!?"; my $count = $rec->{row}{count} or croak "no count!?"; $rec->{row}{policy_evaluated}{disposition} or croak "no disposition?"; $ip = XML::LibXML::Text->new( $ip )->toString(); $count = XML::LibXML::Text->new( $count )->toString(); $rec_xml .="\t\t\n" . "\t\t\t$ip\n" . "\t\t\t$count\n" . $self->get_policy_evaluated_as_xml( $rec ) . "\t\t\n" . $self->get_identifiers_as_xml($rec) . $self->get_auth_results_as_xml($rec); $rec_xml .= "\t\n"; } return $rec_xml; } sub get_identifiers_as_xml { my ( $self, $rec ) = @_; my $id = "\t\t\n"; foreach my $f (qw/ envelope_to envelope_from header_from /) { if ( $f eq 'header_from' ) { # min occurs = 1 croak "missing header_from!" if ! $rec->{identifiers}{$f}; } elsif ( $f eq 'envelope_from') { # min occurs = 1 $rec->{identifiers}{$f} = '' if ! $rec->{identifiers}{$f}; } elsif ( $f eq 'envelope_to' ) { # min occurs = 0 next if ! $rec->{identifiers}{$f}; }; my $val = XML::LibXML::Text->new( $rec->{identifiers}{$f} )->toString(); $id .= "\t\t\t<$f>$val\n"; } $id .= "\t\t\n"; return $id; } sub get_auth_results_as_xml { my ( $self, $rec ) = @_; my $ar = "\t\t\n"; foreach my $dkim_sig ( @{ $rec->{auth_results}{dkim} } ) { $ar .= "\t\t\t\n"; foreach my $g (qw/ domain selector result human_result /) { next if !defined $dkim_sig->{$g}; my $val = XML::LibXML::Text->new( $dkim_sig->{$g} )->toString(); $ar .= "\t\t\t\t<$g>$val\n"; } $ar .= "\t\t\t\n"; } foreach my $spf ( @{ $rec->{auth_results}{spf} } ) { $ar .= "\t\t\t\n"; foreach my $g (qw/ domain scope result /) { next if !defined $spf->{$g}; my $val = XML::LibXML::Text->new( $spf->{$g} )->toString(); $ar .= "\t\t\t\t<$g>$val\n"; } $ar .= "\t\t\t\n"; } $ar .= "\t\t\n"; return $ar; } sub get_policy_published_as_xml { my $self = shift; my $pp = $self->policy_published or return ''; my $xml = "\t\n\t\t$pp->{domain}\n"; foreach my $f (qw/ adkim aspf p sp pct fo /) { my $v = $pp->{$f}; # Set some default values for missing optional fields if necessary if ( $f eq 'sp' && !defined $v ) { $v = $pp->{'p'}; } if ( $f eq 'pct' && !defined $v ) { $v = '100'; } if ( $f eq 'fo' && !defined $v ) { $v = '0'; } next if !defined $v; $v = XML::LibXML::Text->new( $v )->toString(); $xml .= "\t\t<$f>$v\n"; } $xml .= "\t"; return $xml; } sub get_policy_evaluated_as_xml { my ( $self, $rec ) = @_; my $pe = "\t\t\t\n"; foreach my $f (qw/ disposition dkim spf /) { my $val = XML::LibXML::Text->new( $rec->{row}{policy_evaluated}{$f} )->toString(); $pe .= "\t\t\t\t<$f>$val\n"; } my $reasons = $rec->{row}{policy_evaluated}{reason}; if ( $reasons && scalar @$reasons ) { foreach my $reason ( @$reasons ) { my $typeval = XML::LibXML::Text->new( $reason->{type} )->toString(); my $commentval = XML::LibXML::Text->new( $reason->{comment} )->toString(); $pe .= "\t\t\t\t\n"; $pe .= "\t\t\t\t\t$typeval\n"; $pe .= "\t\t\t\t\t$commentval\n"; $pe .= "\t\t\t\t\n"; } }; $pe .= "\t\t\t\n"; return $pe; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Aggregate - aggregate report object =head1 VERSION version 1.20240314 =head1 DESCRIPTION This class is used as the canonization of an aggregate report. When reports are received, the XML is parsed into an L object, which then gets passed to the Report::Store and saved. When sending DMARC reports, data is extracted from the L as an Aggregate object, exported as XML, and sent. =head1 2013 Draft Description AGGREGATE REPORTS The report SHOULD include the following data: o Enough information for the report consumer to re-calculate DMARC disposition based on the published policy, message disposition, and SPF, DKIM, and identifier alignment results. {R12} o Data for each sender subdomain separately from mail from the sender's organizational domain, even if no subdomain policy is applied. {R13} o Sending and receiving domains {R17} o The policy requested by the Domain Owner and the policy actually applied (if different) {R18} o The number of successful authentications {R19} o The counts of messages based on all messages received even if their delivery is ultimately blocked by other filtering agents {R20} Aggregate reports are most useful when they all cover a common time period. By contrast, correlation of these reports from multiple generators when they cover incongruous time periods is difficult or impossible. Report generators SHOULD, wherever possible, adhere to hour boundaries for the reporting period they are using. For example, starting a per-day report at 00:00; starting per-hour reports at 00:00, 01:00, 02:00; et cetera. Report Generators using a 24-hour report period are strongly encouraged to begin that period at 00:00 UTC, regardless of local timezone or time of report production, in order to facilitate correlation. =head1 Report Structure This is a translation of the XML report format in the 2013 Draft, converted to perl data structures. feedback => { version => 1.0, # decimal report_metadata => { # info about DMARC reporter report_id => string org_name => 'Art Farm', email => 'no-reply@theartfarm.com', extra_contact_info => string # min 0 date_range => { begin => epoch time, end => epoch time, }, error => string, # min 0, max unbounded }, policy_published => { domain => string adkim => r, s aspf => r, s p => none, quarantine, reject sp => none, quarantine, reject pct => integer fo => string }, record => [ { row => { source_ip => # IPAddress count => # integer policy_evaluated => { # min=1 disposition => # none, quarantine, reject dkim => # pass, fail spf => # pass, fail reason => [ # min 0, max unbounded { type => # forwarded sampled_out, trusted_forwarder, mailing_list, local_policy, other comment => # string, min 0 }, ], } }, identifiers => { envelope_to min=0 envelope_from min=1 header_from min=1 }, auth_results => { spf => [ # min 1, max unbounded { domain => # min 1 scope => # min 1, helo, mfrom result => # min 1, none neutral pass fail softfail temperror permerror } ] # ( unknown -> temperror, error -> permerror ) dkim => [ # min 0, max unbounded { domain => , # min 1, the d= parameter in the signature selector => , # min 0, string result => , # none pass fail policy neutral temperror permerror human_result => # min 0, string }, ], }, ] }, }; =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Receive.pm000444000765000024 3615714574361234 20724 0ustar00mattstaff000000000000package Mail::DMARC::Report::Receive; use strict; use warnings; our $VERSION = '1.20240314'; use Carp; use Data::Dumper; use Email::MIME; use Email::Simple; use Encode; use IO::Uncompress::Unzip; use IO::Uncompress::Gunzip; use XML::LibXML; use parent 'Mail::DMARC::Base'; require Mail::DMARC::Policy; require Mail::DMARC::Report; require Mail::DMARC::Report::Aggregate::Record; sub from_imap { my $self = shift; eval "require Net::IMAP::Simple"; ## no critic (Eval) croak "Net::IMAP::Simple seems to not work, is it installed?" if $@; my $server = $self->config->{imap}{server} or croak "no imap server conf"; my $folder = $self->config->{imap}{folder} or croak "no imap folder conf"; my $a_done = $self->config->{imap}{a_done}; my $f_done = $self->config->{imap}{f_done}; my $port = $self->config->{imap}{port} // $self->get_imap_port(); no warnings qw(once); ## no critic (Warn) my $imap = Net::IMAP::Simple->new( $server, port => $port, ($port != 143) ? (use_ssl => 1) : () ) or do { ## no critic (PackageVar) my $err = $port == 143 ? $Net::IMAP::Simple::errstr : $Net::IMAP::Simple::SSL::errstr; croak "Unable to connect to IMAP: $err\n"; }; print "connected to IMAP server $server:$port\n" if $self->verbose; $imap->login( $self->config->{imap}{user}, $self->config->{imap}{pass} ) or croak "Login failed: " . $imap->errstr . "\n"; print "\tlogged in\n" if $self->verbose; my $nm = $imap->select( $self->config->{imap}{folder} ); $imap->expunge_mailbox( $self->config->{imap}{folder} ); my @mess = $imap->search( 'UNSEEN', 'DATE' ); if (! scalar @mess) { # imap server might not support SORT extension *Gmail* @mess = $imap->search( 'UNSEEN' ); } print "\tfound " . scalar @mess . " messages\n" if $self->verbose; foreach my $i (@mess) { print $imap->seen($i) ? '*' : ' '; printf "[%03d] ", $i; my $message = $imap->get($i) or do { carp "unable to get message $i\n"; next; }; my $type = $self->from_email_simple( Email::Simple->new("$message") ); next if !$type; my $done_box = $type eq 'aggregate' ? $a_done : $type eq 'forensic' ? $f_done : croak "unknown type!"; $imap->add_flags( $i, '\Seen' ); if ( $done_box ) { $imap->copy( $i, $done_box ) or do { carp $imap->errstr; next; }; $imap->add_flags( $i, '\Deleted' ); }; } $imap->quit; return 1; } sub from_file { my ( $self, $file ) = @_; croak "missing message" if !$file; croak "No such file $file: $!" if !-f $file; return $self->from_email_simple( Email::Simple->new( $self->slurp($file) ) ); } sub from_mbox { my ( $self, $file_name ) = @_; croak "missing mbox file" if !$file_name; # TODO: replace this module # commented out due to build test failures # eval "require Mail::Mbox::MessageParser"; ## no critic (Eval) # croak "is Mail::Mbox::MessageParser installed?" if $@; # my $file_handle = FileHandle->new($file_name); my $folder_reader; # = Mail::Mbox::MessageParser->new( # { 'file_name' => $file_name, # 'file_handle' => $file_handle, # 'enable_cache' => 1, # 'enable_grep' => 1, # } # ); croak $folder_reader unless ref $folder_reader; my $prologue = $folder_reader->prologue; print $prologue; while ( !$folder_reader->end_of_file() ) { $self->from_email_simple( Email::Simple->new( $folder_reader->read_next_email() ) ); } return 1; } sub from_email_simple { my ( $self, $email ) = @_; $self->report->init(); $self->{_envelope_to} = undef; $self->{_header_from} = undef; $self->get_submitter_from_subject( $email->header('Subject') ); my $unzipper = { gz => \&IO::Uncompress::Gunzip::gunzip, # 2013 draft zip => \&IO::Uncompress::Unzip::unzip, # legacy format }; my $rep_type; foreach my $part ( Email::MIME->new( $email->as_string )->parts ) { my ($c_type) = split /;/, $part->content_type || ''; next if $c_type eq 'text/plain'; if ( $c_type eq 'text/rfc822-headers' ) { warn "TODO: handle forensic reports\n"; ## no critic (Carp) $rep_type = 'forensic'; next; } if ( $c_type eq 'message/feedback-report' ) { warn "TODO: handle forensic reports\n"; ## no critic (Carp) $rep_type = 'forensic'; next; } my $bigger; my $filename = $part->{ct}{attributes}{name} || ''; if ( $c_type eq 'application/zip' || $c_type eq 'application/x-zip-compressed' ) { $self->get_submitter_from_filename( $filename ); $unzipper->{zip}->( \$part->body, \$bigger ); $self->handle_body($bigger); $rep_type = 'aggregate'; next; } if ( $c_type eq 'application/gzip' ) { $self->get_submitter_from_filename( $filename ); $unzipper->{gz}->( \$part->body, \$bigger ); $self->handle_body($bigger); $rep_type = 'aggregate'; next; } if ( $filename =~ /xml\.gz$/ ) { if ( $c_type eq 'application/octet-stream' || $c_type eq 'multipart/alternative' ) { $self->get_submitter_from_filename( $filename ); $unzipper->{gz}->( \$part->body, \$bigger ); $self->handle_body($bigger); $rep_type = 'aggregate'; next; } } warn "Unknown message part $c_type\n"; ## no critic (Carp) } return $rep_type; } sub get_imap_port { my $self = shift; eval "use IO::Socket::SSL"; ## no critic (Eval) if ( $@ ) { carp "no SSL, using insecure connection: $!\n"; return 143; }; eval "use Mozilla::CA"; ## no critic (Eval) if ( ! $@ ) { IO::Socket::SSL::set_ctx_defaults( SSL_verifycn_scheme => 'imap', SSL_verify_mode => 0x02, SSL_ca_file => Mozilla::CA::SSL_ca_file(), ); return 993; }; # no CA, disable verification IO::Socket::SSL::set_ctx_defaults( SSL_verifycn_scheme => 'imap', SSL_verify_mode => 0, ); return 993; } sub get_submitter_from_filename { my ( $self, $filename ) = @_; return if $self->{_envelope_to}; # already parsed from Subject: my ( $submitter_dom, $report_dom, $begin, $end ) = split /!/, $filename; $self->{_header_from} ||= $report_dom; return $self->{_envelope_to} = $submitter_dom; } sub get_submitter_from_subject { my ( $self, $subject ) = @_; # The 2013 DMARC spec section 12.2.1 suggests that the header SHOULD conform # to a supplied ABNF. Rather than "require" such conformance, this method is # more concerned with reliably extracting the submitter domain. Quickly. $subject = lc Encode::decode( 'MIME-Header', $subject ); print $subject . "\n"; $subject = substr( $subject, 8 ) if 'subject:' eq substr( $subject, 0, 8 ); $subject =~ s/(?:report\sdomain|submitter|report-id)//gx; # strip keywords $subject =~ s/\s+//g; # remove white space my ( undef, $report_dom, $sub_dom, $report_id ) = split /:/, $subject; my $meta = $self->report->aggregate->metadata; if ( $report_id && !$meta->uuid ) { # remove if present $report_id = substr($report_id,1) if '<' eq substr($report_id,0,1); chop $report_id if '>' eq substr($report_id,-1,1); $meta->uuid($report_id); }; $self->{_header_from} ||= $report_dom; return $self->{_envelope_to} = $sub_dom; } sub handle_body { my ( $self, $body ) = @_; print "handling decompressed body\n" if $self->{verbose}; my $dom = XML::LibXML->load_xml( string => $body ); $self->do_node_report_metadata( $dom->findnodes("/feedback/report_metadata") ); $self->do_node_policy_published( $dom->findnodes("/feedback/policy_published") ); foreach my $record ( $dom->findnodes("/feedback/record") ) { $self->do_node_record($record); } return $self->report->save_aggregate(); } sub report { my $self = shift; return $self->{report} if ref $self->{report}; return $self->{report} = Mail::DMARC::Report->new(); } sub do_node_report_metadata { my ( $self, $node ) = @_; foreach my $n (qw/ org_name email extra_contact_info /) { $self->report->aggregate->metadata->$n( $node->findnodes("./$n")->string_value ); } my $rid = $node->findnodes("./report_id")->string_value; $rid = substr($rid,1) if '<' eq substr($rid,0,1); # remove < chop $rid if '>' eq substr($rid,-1,1); # remove > $self->report->aggregate->metadata->report_id( $rid ); if ( ! $self->report->aggregate->metadata->uuid ) { $self->report->aggregate->metadata->uuid( $rid ); }; foreach my $n (qw/ begin end /) { $self->report->aggregate->metadata->$n( $node->findnodes("./date_range/$n")->string_value ); } foreach my $n ( $node->findnodes("./error") ) { $self->report->aggregate->metadata->error( $n->string_value ); } return $self->report->aggregate->metadata; } sub do_node_policy_published { my ( $self, $node ) = @_; my $pol = Mail::DMARC::Policy->new(); foreach my $n (qw/ domain adkim aspf p sp pct /) { my $val = $node->findnodes("./$n")->string_value or next; $val =~ s/\s*//g; # remove whitespace $pol->$n($val); } $self->report->aggregate->policy_published($pol); return $pol; } sub do_node_record { my ( $self, $node ) = @_; my $rec = Mail::DMARC::Report::Aggregate::Record->new; $self->do_node_record_auth(\$rec, $node); foreach my $r (qw/ source_ip count /) { $rec->row->$r( $node->findnodes("./row/$r")->string_value ); }; # policy_evaluated foreach my $pe (qw/ disposition dkim spf /) { my $ResultType = $node->findnodes("./row/policy_evaluated/$pe")->string_value; if ($pe eq 'disposition') { if ($ResultType !~ /^(none|quarantine|reject)$/) { $ResultType = 'none'; # invalid DispositionType (Facebook) } } else { if ($ResultType !~ /^(pass|fail)$/) { $ResultType = 'pass'; # invalid ResultType (also FaceBook) } } $rec->row->policy_evaluated->$pe($ResultType); } # reason $self->do_node_record_reason( \$rec, $node ); # identifiers foreach my $i (qw/ envelope_to envelope_from header_from /) { $rec->identifiers->$i( $node->findnodes("./identifiers/$i")->string_value ); } # for reports from junc.org with mis-labeled identifiers if ( !$rec->identifiers->header_from ) { $rec->identifiers->header_from( $node->findnodes("./identities/header_from")->string_value ); }; # last resort... if (!$rec->identifiers->envelope_to) { $rec->identifiers->envelope_to($self->{_envelope_to}); } if (!$rec->identifiers->header_from) { $rec->identifiers->header_from($self->{_header_from}); } print Data::Dumper::Dumper($rec) if $self->verbose; $self->report->aggregate->record($rec); return $rec; } sub do_node_record_auth { my ($self, $row, $node) = @_; my @spf = qw/ domain scope result /; foreach ( $node->findnodes("./auth_results/spf") ) { my %spf = map { $_ => $node->findnodes("./auth_results/spf/$_")->string_value } @spf; if ( $spf{scope} && ! $self->is_valid_spf_scope( $spf{scope} ) ) { carp "invalid scope: $spf{scope}, ignoring"; delete $spf{scope}; }; # this is for reports from ivenue.com with result=unknown if ( $spf{result} && ! $self->is_valid_spf_result( $spf{result} ) ) { carp "invalid SPF result: $spf{result}, setting to temperror"; $spf{result} = 'temperror'; }; $$row->auth_results->spf(\%spf); }; my @dkim = qw/ domain selector result human_result /; foreach ( $node->findnodes("./auth_results/dkim") ) { my %dkim = map { $_ => $node->findnodes("./auth_results/dkim/$_")->string_value } @dkim; $$row->auth_results->dkim(\%dkim); }; return; } sub do_node_record_reason { my ($self, $row, $node) = @_; # my @types = qw/ forwarded sampled_out trusted_forwarder mailing_list # local_policy other /; foreach my $r ( $node->findnodes("./row/policy_evaluated/reason") ) { my $type = $r->findnodes('./type')->string_value or next; my $comment = $r->findnodes('./comment')->string_value; $$row->row->policy_evaluated->reason( { type => $type, comment => $comment } ); } return; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Receive - process incoming DMARC reports =head1 VERSION version 1.20240314 =head1 DESCRIPTION Receive DMARC reports and save them to the report store/database. =head1 METHODS =head2 from_imap, from_file, from_mbox These methods are called by L program, which has its own documentation and usage instructions. The methods accept a message (or list of messages) and create an Email::Simple object from each, passing that object to from_email_simple. =head2 from_email_simple Accepts an Email::Simple message object. Returns the type of DMARC report detected or undef if no DMARC report was detected. When forensic reports are detected, no further processing is done. When an aggregate report is detected, the report details are extracted from the message body as well as the Subject field/header and attachment metadata. Parsing of the Subject and MIME metadata is necessary because the 2013 draft DMARC specification does not REQUIRE the envelope_to domain name to be included in the XML report. For example, the only way to B that the email which generated this particular report was sent to hotmail.com is to extract the envelope_to domain from the message metadata (Org Name=Microsoft, hotmail.com is not in the XML). So far, every messsage I have seen has had the envelope_to domain in one location or the other. To extract messages from the message body, the MIME attachments are decompressed and passed to L. =head2 handle_body Accepts a XML message, parsing it with XML::LibXML and XPath expressions. The parsed data is stored in a L object. When the parsing is complete, the report object is saved to the report store. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Send.pm000444000765000024 446314574361234 20206 0ustar00mattstaff000000000000package Mail::DMARC::Report::Send; use strict; use warnings; our $VERSION = '1.20240314'; use parent 'Mail::DMARC::Base'; use Mail::DMARC::Report::Send::SMTP; use Mail::DMARC::Report::Send::HTTP; sub http { my $self = shift; return $self->{http} if ref $self->{http}; return $self->{http} = Mail::DMARC::Report::Send::HTTP->new(); } sub smtp { my $self = shift; return $self->{smtp} if ref $self->{smtp}; return $self->{smtp} = Mail::DMARC::Report::Send::SMTP->new(); } sub too_big_report { my ( $self, $arg_ref ) = @_; my $OrgName = $self->config->{organization}{org_name}; my $Domain = $self->config->{organization}{domain}; my $ver = $Mail::DMARC::Base::VERSION || ''; # undef in author environ my $uri = $arg_ref->{uri}; my $bytes = $arg_ref->{report_bytes}; my $report_id = $arg_ref->{report_id}; my $rep_domain= $arg_ref->{report_domain}; my $date = $self->smtp->get_timestamp_rfc2822; return <<"EO_TOO_BIG" This is a 'too big' DMARC notice. The aggregate report was NOT delivered. Report-Date: $date Report-Domain: $rep_domain Report-ID: $report_id Report-Size: $bytes Submitter: $Domain Submitting-URI: $uri Submitted by $OrgName Generated with Mail::DMARC $ver EO_TOO_BIG ; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Send - report sending dispatch class =head1 VERSION version 1.20240314 =head1 DESCRIPTION Send DMARC reports, via SMTP or HTTP. =head2 Report Sender A report sender needs to: 1. store reports 2. bundle aggregated reports 3. format report in XML 4. gzip the XML 5. deliver report to Author Domain This class and subclasses provide methods used by L. =head1 12.2.1 Email L =head1 12.2.2. HTTP L =head1 12.2.3. Other Methods Other registered URI schemes may be explicitly supported in later versions. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Sender.pm000444000765000024 3756514574361234 20566 0ustar00mattstaff000000000000package Mail::DMARC::Report::Sender; use strict; use warnings; use Data::Dumper; use Carp; use Encode; use Getopt::Long; use Sys::Syslog qw(:standard :macros); use Mail::DMARC::Report; use Email::Sender; use Email::Sender::Simple qw{ sendmail }; use Email::Sender::Transport::SMTP; use Email::Sender::Transport::SMTP::Persistent; use Module::Load; sub new { my $class = shift; my $self = { send_delay => 5, batch_size => 1, alarm_at => 120, syslog => 0, smarthost => undef, transports_method => undef, transports_object => undef, dkim_key => undef, verbose => 0, }; return bless $self, $class; }; sub set_transports_object { my ( $self,$transports_object ) = @_; $self->{transports_object} = $transports_object; return; } sub set_transports_method { my ( $self,$transports_method ) = @_; $self->{transports_method} = $transports_method; return; # Transports method is a sub which returns # a list of transports for the given args. } # Return a list of transports to try in order. sub get_transports_for { my ( $self, $args ) = @_; # Have we passed a custom transports generation class? if ( $self->{transports_method} ) { my @transports = &{$self->{transports_method}}( $args ); return @transports; } if ( $self->{transports_object} ) { my @transports = $self->{transports_object}->get_transports_for( $args ); return @transports; } my $report = $args->{report}; my $transport_can_maybetls = $Email::Sender::VERSION > 2.0; # Do we have a smart host? if ( $report->config->{smtp}{smarthost} ) { return ($self->{smarthost}) if $self->{smarthost}; my $transport_data = { host => $report->config->{smtp}->{smarthost}, ssl => 'starttls', port => 587, helo => $report->sendit->smtp->get_helo_hostname, timeout => 32, }; $transport_data->{sasl_username} = $report->config->{smtp}->{smartuser} if $report->config->{smtp}->{smartuser}; $transport_data->{sasl_password} = $report->config->{smtp}->{smartpass} if $report->config->{smtp}->{smartpass}; my $transport = Email::Sender::Transport::SMTP::Persistent->new($transport_data); $self->{smarthost} = $transport; return ($self->{smarthost}); } my @smtp_hosts = $report->sendit->smtp->get_smtp_hosts($args->{to}); my $log_data = $args->{log_data}; my @transports; $log_data->{smtp_host} = join( ',', @smtp_hosts ); if ( Email::Sender::Transport::SMTP->can('hosts') ) { if ( $transport_can_maybetls ) { push @transports, Email::Sender::Transport::SMTP->new({ hosts => \@smtp_hosts, ssl => 'maybestarttls', port => 25, helo => $report->sendit->smtp->get_helo_hostname, timeout => 32, }); } else { push @transports, Email::Sender::Transport::SMTP->new({ hosts => \@smtp_hosts, ssl => 'starttls', port => 25, helo => $report->sendit->smtp->get_helo_hostname, timeout => 32, }); push @transports, Email::Sender::Transport::SMTP->new({ hosts => \@smtp_hosts, ssl => 0, port => 25, helo => $report->sendit->smtp->get_helo_hostname, timeout => 32, }); } } else { # We can't pass hosts to the transport, so pass a list of transports # for each possible host. if ( $transport_can_maybetls ) { foreach my $host ( @smtp_hosts ) { push @transports, Email::Sender::Transport::SMTP->new({ host => $host, ssl => 'maybestarttls', port => 25, helo => $report->sendit->smtp->get_helo_hostname, timeout => 32, }); } } else { foreach my $host ( @smtp_hosts ) { push @transports, Email::Sender::Transport::SMTP->new({ host => $host, ssl => 'starttls', port => 25, helo => $report->sendit->smtp->get_helo_hostname, timeout => 32, }); } foreach my $host ( @smtp_hosts ) { push @transports, Email::Sender::Transport::SMTP->new({ host => $host, ssl => 0, port => 25, helo => $report->sendit->smtp->get_helo_hostname, timeout => 32, }); } } } return @transports; } sub get_dkim_key { my ( $self ) = @_; my $report = $self->{report}; return $self->{dkim_key} if $self->{dkim_key}; if ( $report->config->{report_sign}->{keyfile} ) { eval { require Mail::DKIM::PrivateKey; require Mail::DKIM::Signer; require Mail::DKIM::TextWrap; }; if ( UNIVERSAL::can( 'Mail::DKIM::Signer', "new" ) ) { my $file = $report->config->{report_sign}->{keyfile}; $self->{dkim_key} = Mail::DKIM::PrivateKey->load( 'File' => $file, ); if ( ! $self->{dkim_key} ) { die "Could not load DKIM key $file"; } } else { die 'DKIM signing requested but Mail::DKIM could not be loaded. Please check that Mail::DKIM is installed.'; } $self->log_output( 'DKIM signing key loaded' ); return $self->{dkim_key}; } } sub run { my ( $self ) = @_; GetOptions ( 'verbose+' => \$self->{verbose}, 'delay=i' => \$self->{send_delay}, 'batch=i' => \$self->{batch_size}, 'timeout=i' => \$self->{alarm_at}, 'syslog+' => \$self->{syslog}, ); openlog( 'dmarc_send_reports', 'pid', LOG_MAIL ) if $self->{syslog}; $self->log_output( 'dmarc_send_reports starting up' ); $|++; my $report = Mail::DMARC::Report->new(); $self->{report} = $report; $report->verbose($self->{verbose}) if defined $self->{verbose}; # If we have defined a custom transports generation class then # load and instantiate it here. if ( $report->config->{smtp}->{transports} ) { load $report->config->{smtp}->{transports}; my $package = $report->config->{smtp}->{transports}; my $transports_object = $package->new(); $self->set_transports_object( $transports_object ); } local $SIG{'ALRM'} = sub{ die "timeout\n" }; my $batch_do = 1; # 1. get reports, one at a time REPORT: while ( my $aggregate = $report->store->next_todo() ) { eval { $self->send_report( $aggregate, $report ); }; if ( my $error = $@ ) { $self->log_output( 'error sending report: ' . $error ); } if ( $batch_do++ > $self->{batch_size} ) { $batch_do = 1; if ( $self->{send_delay} > 0 ) { print "sleeping ".$self->{send_delay} if $self->{verbose}; foreach ( 1 .. $self->{send_delay} ) { print '.' if $self->{verbose}; sleep 1; }; print "done.\n" if $self->{verbose}; } } } alarm(0); $self->log_output( 'dmarc_send_reports done' ); closelog() if $self->{syslog}; return; } # PODNAME: dmarc_send_reports # ABSTRACT: send aggregate reports sub send_report { my ( $self, $aggregate, $report ) = @_; alarm($self->{alarm_at}); $self->log_output({ 'id' => $aggregate->metadata->report_id, 'domain' => $aggregate->policy_published->domain, 'rua' => $aggregate->policy_published->rua, }); # Generate the list of report receivers my $report_receivers = eval{ $report->uri->parse( $aggregate->policy_published->rua ) }; if ( my $error = $@ ) { $self->log_output({ 'id' => $aggregate->metadata->report_id, 'error' => 'No valid ruas found - deleting report - ' . $error, }); $report->store->delete_report($aggregate->metadata->report_id); alarm(0); return; } # Check we have some receivers if ( scalar @$report_receivers == 0 ) { $self->log_output({ 'id' => $aggregate->metadata->report_id, 'error' => 'No valid ruas found - deleting report', }); $report->store->delete_report($aggregate->metadata->report_id); alarm(0); return; } # Generate the XML data and associated metadata my $xml = $aggregate->as_xml(); my $xml_compressed = $report->compress(\$xml); my $xml_compressed_bytes = length Encode::encode_utf8($xml_compressed); my $sent = 0; my $cc_sent = 0; my @too_big; URI: foreach my $receiver (@$report_receivers) { my $method = $receiver->{uri}; my $max = $receiver->{max_bytes}; if ( $max && $xml_compressed_bytes > $max ) { $self->log_output({ 'id' => $aggregate->metadata->report_id, 'info' => "skipping $method: report size ($xml_compressed_bytes) larger than $max", }); push @too_big, $method; next URI; } elsif ( 'mailto:' eq substr( $method, 0, 7 ) ) { my ($to) = ( split /:/, $method )[-1]; my $cc = $report->config->{smtp}{cc}; if ( $cc && $cc ne 'set.this@for.a.while.example.com' && ! $cc_sent ) { $self->email({ to => $cc, compressed => $xml_compressed, aggregate => \$aggregate }); $cc_sent = 1; }; $self->email({ to => $to, compressed => $xml_compressed, aggregate => \$aggregate }) and $sent++; } # http(s) sending not yet enabled in module, skip this send and # increment sent to avoid looping elsif ( 'http:' eq substr( $method, 0, 5 ) ) { #$report->sendit->http->post( $method, \$aggregate, $shrunk ); $sent++; } elsif ( 'https:' eq substr( $method, 0, 6 ) ) { #$report->sendit->http->post( $method, \$aggregate, $shrunk ); $sent++; } } if ( $sent ) { $report->store->delete_report($aggregate->metadata->report_id); } else { my $send_errors = $report->config->{smtp}->{send_errors} // 1; $self->send_too_big_email(\@too_big, $xml_compressed_bytes, $aggregate) if $send_errors; $report->store->delete_report($aggregate->metadata->report_id); }; alarm(0); return; } sub send_too_big_email { my ($self, $too_big, $bytes, $aggregate) = @_; my $report = $self->{report}; BIGURI: foreach my $uri (@$too_big) { next BIGURI if 'mailto:' ne substr( $uri, 0, 7 ); my ($to) = ( split /:/, $uri )[-1]; my $body = $report->sendit->too_big_report( { uri => $uri, report_bytes => $bytes, report_id => $aggregate->metadata->report_id, report_domain=> $aggregate->policy_published->domain, } ); my $mime_object = $report->sendit->smtp->assemble_too_big_message_object($to, $body); $self->email({ to => $to, mime => $mime_object }); }; return; }; sub email { my ($self, $args) = @_; my $to = $args->{to}; if ( !$to ) { $self->log_output({ 'error' => 'No recipient for email' }); croak 'No recipient for email'; } my $mime = $args->{mime} // undef; my $compressed = $args->{compressed} // undef; my $agg_ref = $args->{aggregate} // undef; my $report = $self->{report}; my $rid; $rid = $$agg_ref->metadata->report_id if $agg_ref; my $log_data = { deliver_to => $to, }; my $body; if ( $rid ) { my $mime_object = $report->sendit->smtp->assemble_message_object($agg_ref, $to, $compressed); $body = $mime_object->as_string; $log_data->{id} = $rid; $log_data->{to_domain} = $$agg_ref->policy_published->domain; } elsif ( $mime ) { $body = $mime->as_string; } else { croak 'No email content'; } my $dkim_key = $self->get_dkim_key(); if ( $dkim_key ) { my $dkim_algorithm = $report->config->{report_sign}{algorithm}; my $dkim_method = $report->config->{report_sign}{method}; my $dkim_domain = $report->config->{report_sign}{domain}; my $dkim_selector = $report->config->{report_sign}{selector}; eval { my $dkim = Mail::DKIM::Signer->new( Algorithm => $dkim_algorithm, Method => $dkim_method, Domain => $dkim_domain, Selector => $dkim_selector, Key => $dkim_key, ); $body =~ s/\015?\012/\015\012/g; $dkim->PRINT( $body ); $dkim->CLOSE; my $signature = $dkim->signature; $body = $signature->as_string . "\015\012" . $body; $log_data->{dkim} = 1; }; if ( my $error = $@ ) { print "DKIM Signing error\n\t$error\n" if $self->{verbose}; $log_data->{error} = 'DKIM Signing error'; $log_data->{error_detail} = $error; $self->log_output($log_data); return; } } my @transports = $self->get_transports_for({ report => $report, log_data => $log_data, to => $to, }); my $success; while ( my $transport = shift @transports ) { my $done = 0; eval { $success = sendmail( $body, { from => $report->config->{organization}{email}, to => $to, transport => $transport, } ); if ( $success ) { $log_data->{success} = $success->{message}; $done = 1; } }; if ( my $error = $@ ) { next if scalar @transports; my $code; my $message; if (ref $error eq 'Email::Sender::Failure') { $code = $error->code; $message = $error->message; } else { $code = 'error'; $message = $error; chomp $message; } $code = join( ', ', $log_data->{send_error_code}, $code ) if exists $log_data->{send_error_code}; $message = join( ', ', $log_data->{send_error}, $message ) if exists $log_data->{send_error}; $log_data->{send_error} = $message; $log_data->{send_error_code} = $code; if ( $error->code && $error->code =~ /^5/ ) { # Perma error $log_data->{deleted} = 1; $report->store->delete_report($rid); $success = 0; last; } $report->store->error($rid, $error->message); } last if $done; } $self->log_output( $log_data ); if ( $success ) { return 1; } return 0; } sub log_output { my ( $self, $args ) = @_; my $log_level = LOG_INFO; my $log_entry = ''; if ( ref $args eq 'HASH' ) { if ( $args->{'log_level'} ) { $log_level = $args->{'log_level'}; delete $args->{'log_level'}; } my @parts; foreach my $key ( sort keys %$args ) { my $value = $args->{ $key } // ''; $value =~ s/,/#044/g; # Encode commas push @parts, join( '=', $key, $value ); } $log_entry = join( ', ', @parts ); } else { $log_entry = $args; } syslog( $log_level, $log_entry ) if $self->{syslog}; print "$log_entry\n" if $self->{verbose}; return; } 1; Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Store.pm000444000765000024 376514574361234 20415 0ustar00mattstaff000000000000package Mail::DMARC::Report::Store; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; use parent 'Mail::DMARC::Base'; sub delete_report { my $self = shift; return $self->backend->delete_report(@_); } sub error { my $self = shift; return $self->backend->insert_error(@_); } sub retrieve { my $self = shift; return $self->backend->retrieve(@_); } sub next_todo { my $self = shift; return $self->backend->next_todo(@_); } sub retrieve_todo { my $self = shift; return $self->backend->retrieve_todo(@_); } sub backend { my $self = shift; my $backend = $self->config->{report_store}{backend}; croak "no backend defined?!" if !$backend; return $self->{$backend} if ref $self->{$backend}; my $module = "Mail::DMARC::Report::Store::$backend"; eval "use $module"; ## no critic (Eval) if ($@) { croak "Unable to load backend $backend: $@\n"; } return $self->{$backend} = $module->new; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Store - persistent storage broker for reports =head1 VERSION version 1.20240314 =head1 SYNOPSIS =head1 DESCRIPTION At present, the only storage module is L. I experimented with perl's AnyDBM storage backend, but chose to deploy with SQL because a single SQL implementation supports many DBD drivers, including SQLite, MySQL, and DBD (same as AnyDBM). This Store class provides a layer of indirection, allowing one to write a new Mail::DMARC::Report::Store::MyGreatDB module, update their config file, and not alter the innards of Mail::DMARC. Much. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/URI.pm000444000765000024 1103014574361234 17760 0ustar00mattstaff000000000000package Mail::DMARC::Report::URI; use strict; use warnings; our $VERSION = '1.20240314'; use Carp; use URI; sub new { my $class = shift; return bless {}, $class; } sub parse { my $self = shift; my $str = shift or croak "URI string is required!"; my @valids = (); foreach my $raw ( split /,/, $str ) { # warn "raw: $raw\n"; my ( $u, $size_f ) = split /!/, $raw; my $bytes = $self->get_size_limit($size_f); my $uri = URI->new($u) or do { carp "can't parse URI from $u"; next; }; my $scheme = $uri->scheme or next; if ( $scheme eq 'mailto' && lc substr( $u, 0, 7 ) eq 'mailto:' ) { push @valids, { max_bytes => $bytes, uri => $uri }; next; } if ( $scheme =~ /^http(s)?/x && lc substr( $u, 0, 4 ) eq 'http' ) { push @valids, { max_bytes => $bytes, uri => $uri }; next; } # print "invalid URI scheme: $scheme in $u\n"; # 12.1 Discovery - URI schemes found in "rua" tag that are not implemented by # a Mail Receiver MUST be ignored. } return \@valids; } sub get_size_limit { my ( $self, $size ) = @_; return 0 if !defined $size; # no limit return $size if $size =~ /^\d+$/; # no units, raw byte count # 6.3 Formal Definition # units are considered to be powers of two; a kilobyte is 2^10, a megabyte is 2^20, my $unit = lc chop $size; return $size * ( 2**10 ) if 'k' eq $unit; return $size * ( 2**20 ) if 'm' eq $unit; return $size * ( 2**30 ) if 'g' eq $unit; return $size * ( 2**40 ) if 't' eq $unit; croak "unrecognized unit ($unit) in size ($size)"; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::URI - a DMARC report URI =head1 VERSION version 1.20240314 =head1 SYNOPSIS use Mail::DMARC::URI; my $duri = Mail::DMARC::URI->new; my $uri_ref = $duri->parse('mailto:rua@example.com,mailto:rua@external.otherdomain.com'); foreach my $u ( @$uri_ref ) { my $method = $u->{uri}; my $max = $u->{max_bytes}; ... do some URI stuff ... }; =head1 DESCRIPTION defines a generic syntax for identifying a resource. The DMARC mechanism uses this as the format by which a Domain Owner specifies the destination for the two report types that are supported. The place such URIs are specified (see Section 6.2) allows a list of these to be provided. A report is to be sent to each listed URI. Mail Receivers MAY impose a limit on the number of URIs that receive reports, but MUST support at least two. The list of URIs is separated by commas (ASCII 0x2C). Each URI can have associated with it a maximum report size that may be sent to it. This is accomplished by appending an exclamation point (ASCII 0x21), followed by a maximum size indication, before a separating comma or terminating semi-colon. Thus, a DMARC URI is a URI within which any commas or exclamation points are percent-encoded per [URI], followed by an OPTIONAL exclamation point and a maximum size specification, and, if there are additional reporting URIs in the list, a comma and the next URI. For example, the URI "mailto:reports@example.com!50m" would request a report be sent via email to "reports@example.com" so long as the report payload does not exceed 50 megabytes. A formal definition is provided in Section 6.3. =head1 ABNF dmarc-uri = URI [ "!" 1*DIGIT [ "k" / "m" / "g" / "t" ] ] ; "URI" is imported from [URI]; commas (ASCII 0x2c) ; and exclamation points (ASCII 0x21) MUST be encoded URI is imported from RFC 3986: https://www.ietf.org/rfc/rfc3986.txt Only mailto, http, and https URIs are currently supported, examples: https://www.ietf.org/rfc/rfc3986.txt mailto:John.Doe@example.com With an optional size limit (see SIZE LIMIT). =head1 SIZE LIMIT A size limitation in a dmarc-uri, if provided, is interpreted as a count of units followed by an OPTIONAL unit size ("k" for kilobytes, "m" for megabytes, "g" for gigabytes, "t" for terabytes). Without a unit, the number is presumed to be a basic byte count. Note that the units are considered to be powers of two; a kilobyte is 2^10, a megabyte is 2^20, etc. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate000755000765000024 014574361234 20501 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Metadata.pm000444000765000024 505214574361234 22716 0ustar00mattstaff000000000000package Mail::DMARC::Report::Aggregate::Metadata; use strict; use warnings; our $VERSION = '1.20240314'; use XML::LibXML; use parent 'Mail::DMARC::Base'; sub org_name { return $_[0]->{org_name} if 1 == scalar @_; return $_[0]->{org_name} = $_[1]; } sub email { return $_[0]->{email} if 1 == scalar @_; return $_[0]->{email} = $_[1]; } sub extra_contact_info { return $_[0]->{extra_contact_info} if 1 == scalar @_; return $_[0]->{extra_contact_info} = $_[1]; } sub report_id { return $_[0]->{report_id} if 1 == scalar @_; return $_[0]->{report_id} = $_[1]; } sub date_range { return $_[0]->{date_range} if 1 == scalar @_; # croak "invalid date_range" if ('HASH' ne ref $_->[1]); return $_[0]->{date_range} = $_[1]; } sub begin { return $_[0]->{date_range}{begin} if 1 == scalar @_; return $_[0]->{date_range}{begin} = $_[1]; } sub end { return $_[0]->{date_range}{end} if 1 == scalar @_; return $_[0]->{date_range}{end} = $_[1]; } sub error { return $_[0]->{error} if 1 == scalar @_; return push @{ $_[0]->{error} }, $_[1]; } sub uuid { return $_[0]->{uuid} if 1 == scalar @_; return $_[0]->{uuid} = $_[1]; } sub as_xml { my $self = shift; my $meta = "\t\n"; foreach my $f (qw/ org_name email extra_contact_info report_id /) { my $val = $self->$f or next; $val = XML::LibXML::Text->new( $val )->toString(); $meta .= "\t\t<$f>$val\n"; } my $begin = XML::LibXML::Text->new( $self->begin )->toString(); my $end = XML::LibXML::Text->new( $self->end )->toString(); $meta .= "\t\t\n\t\t\t" . $begin . "\n" . "\t\t\t" . $end . "\n\t\t\n"; my $errors = $self->error; if ( $errors && @$errors ) { foreach my $err ( @$errors ) { $err = XML::LibXML::Text->new( $err )->toString(); $meta .= "\t\t$err\n"; }; }; $meta .= "\t"; return $meta; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Aggregate::Metadata - metadata section of aggregate report =head1 VERSION version 1.20240314 =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record.pm000444000765000024 431114574361234 22411 0ustar00mattstaff000000000000package Mail::DMARC::Report::Aggregate::Record; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; use parent 'Mail::DMARC::Base'; require Mail::DMARC::Report::Aggregate::Record::Identifiers; require Mail::DMARC::Report::Aggregate::Record::Auth_Results; require Mail::DMARC::Report::Aggregate::Record::Row; sub new { my ( $class, @args ) = @_; croak "invalid arguments" if @args % 2; my $self = bless {}, $class; return $self if 0 == scalar @args; my %args = @args; foreach my $key ( keys %args ) { $self->$key( $args{$key} ); } return $self; } sub identifiers { my ($self, @args) = @_; if ( !scalar @args ) { return $self->{identifiers} if $self->{identifiers}; } if ('HASH' eq ref $args[0]) { @args = %{ $args[0] }; } return $self->{identifiers} = Mail::DMARC::Report::Aggregate::Record::Identifiers->new(@args); } sub auth_results { my ($self, @args) = @_; if ( !scalar @args ) { return $self->{auth_results} if $self->{auth_results}; } if ( 1 == scalar @args && 'HASH' eq ref $args[0] ) { @args = %{ $args[0] }; } return $self->{auth_results} = Mail::DMARC::Report::Aggregate::Record::Auth_Results->new(@args); } sub row { my ($self, @args) = @_; if ( 0 == scalar @args ) { return $self->{row} if $self->{row}; } if ( 1 == scalar @args && 'HASH' eq ref $args[0] ) { @args = %{ $args[0] }; } return $self->{row} = Mail::DMARC::Report::Aggregate::Record::Row->new(@args); } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Aggregate::Record - record section of aggregate report =head1 VERSION version 1.20240314 =head1 DESCRIPTION An aggregate report record, with object methods for identifiers, auth_results, and each row. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record000755000765000024 014574361234 21717 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results.pm000444000765000024 443514574361234 25042 0ustar00mattstaff000000000000package Mail::DMARC::Report::Aggregate::Record::Auth_Results; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; require Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF; require Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM; sub new { my ( $class, @args ) = @_; croak "invalid arguments" if @args % 2; my $self = bless { spf => [], dkim => [] }, $class; return $self if 0 == scalar @args; my %args = @args; foreach my $key ( keys %args ) { $self->$key( $args{$key} ); } return $self; } sub spf { my ($self, @args) = @_; return $self->{spf} if 0 == scalar @args; # one shot if (1 == scalar @args && ref $args[0] eq 'ARRAY') { #warn "SPF one shot"; my $iter = 0; foreach my $d ( @{ $args[0] }) { $self->{spf}->[$iter] = Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF->new($d); $iter++; } return $self->{spf}; } #warn "SPF iterative"; push @{ $self->{spf} }, Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF->new(@args); return $self->{spf}; } sub dkim { my ($self, @args) = @_; return $self->{dkim} if 0 == scalar @args; if (1 == scalar @args && ref $args[0] eq 'ARRAY') { #warn "dkim one shot"; my $iter = 0; foreach my $d ( @{ $args[0] }) { $self->{dkim}->[$iter] = Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM->new($d); $iter++; } return $self->{dkim}; } #warn "dkim iterative"; push @{ $self->{dkim}}, Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM->new(@args); return $self->{dkim}; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Aggregate::Record::Auth_Results - auth_results section of a DMARC aggregate record =head1 VERSION version 1.20240314 =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record/Identifiers.pm000444000765000024 240214574361234 24655 0ustar00mattstaff000000000000package Mail::DMARC::Report::Aggregate::Record::Identifiers; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; sub new { my ( $class, @args ) = @_; croak "invalid arguments" if @args % 2; my %args = @args; my $self = bless {}, $class; foreach my $key ( keys %args ) { $self->$key( $args{$key} ); } return $self; } sub envelope_to { return $_[0]->{envelope_to} if 1 == scalar @_; return $_[0]->{envelope_to} = $_[1]; } sub envelope_from { return $_[0]->{envelope_from} if 1 == scalar @_; return $_[0]->{envelope_from} = $_[1]; } sub header_from { return $_[0]->{header_from} if 1 == scalar @_; return $_[0]->{header_from} = $_[1]; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Aggregate::Record::Identifiers - identifiers section of a DMARC aggregate record =head1 VERSION version 1.20240314 =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record/Row.pm000444000765000024 307714574361234 23170 0ustar00mattstaff000000000000package Mail::DMARC::Report::Aggregate::Record::Row; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; require Mail::DMARC::Report::Aggregate::Record::Row::Policy_Evaluated; sub new { my ( $class, @args ) = @_; croak "invalid arguments" if @args % 2; my %args = @args; my $self = bless {}, $class; foreach my $key ( keys %args ) { $self->$key( $args{$key} ); } return $self; } sub source_ip { return $_[0]->{source_ip} if 1 == scalar @_; return $_[0]->{source_ip} = $_[1]; } sub policy_evaluated { my ($self, @args) = @_; if (0 == scalar @args) { return $self->{policy_evaluated} if $self->{policy_evaluated}; } if (1 == scalar @args) { if ('HASH' eq ref $args[0]) { @args = %{ $args[0] }; } } return $self->{policy_evaluated} = Mail::DMARC::Report::Aggregate::Record::Row::Policy_Evaluated->new(@args); } sub count { return $_[0]->{count} if 1 == scalar @_; return $_[0]->{count} = $_[1]; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Aggregate::Record::Row - row section of a DMARC aggregate record =head1 VERSION version 1.20240314 =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results000755000765000024 014574361234 24341 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results/DKIM.pm000444000765000024 412214574361234 25557 0ustar00mattstaff000000000000package Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM; our $VERSION = '1.20240314'; use strict; use Carp; sub new { my ( $class, @args ) = @_; croak "missing arguments" if 0 == scalar @args; my $self = bless {}, $class; # a bare hash return $self->_from_hash(@args) if scalar @args > 1; my $dkim = shift @args; croak "dkim argument not a ref" if ! ref $dkim; return $dkim if ref $dkim eq $class; # been here before... return $self->_from_hashref($dkim) if 'HASH' eq ref $dkim; croak "invalid dkim argument"; } sub domain { return $_[0]->{domain} if 1 == scalar @_; return $_[0]->{domain} = $_[1]; } sub selector { return $_[0]->{selector} if 1 == scalar @_; return $_[0]->{selector} = $_[1]; } sub result { return $_[0]->{result} if 1 == scalar @_; croak "invalid DKIM result" if ! grep { $_ eq $_[1] } qw/ pass fail neutral none permerror policy temperror /; return $_[0]->{result} = $_[1]; } sub human_result { return $_[0]->{human_result} if 1 == scalar @_; return $_[0]->{human_result} = $_[1]; } sub _from_hash { my ($self, %args) = @_; foreach my $key ( keys %args ) { $self->$key( $args{$key} ); } $self->is_valid; return $self; } sub _from_hashref { return $_[0]->_from_hash(%{ $_[1] }); } sub is_valid { my $self = shift; foreach my $f (qw/ domain result /) { if ( ! defined $self->{$f} ) { croak "DKIM value $f is required!"; } } return; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Aggregate::Record::Auth_Results::DKIM - auth_results/dkim section of a DMARC aggregate record =head1 VERSION version 1.20240314 =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record/Auth_Results/SPF.pm000444000765000024 441414574361234 25467 0ustar00mattstaff000000000000package Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF; our $VERSION = '1.20240314'; use strict; use Carp; use parent 'Mail::DMARC::Base'; sub new { my ( $class, @args ) = @_; my $self = bless {}, $class; if (0 == scalar @args) { return $self; } # a bare hash return $self->_from_hash(@args) if scalar @args > 1; my $spf = shift @args; return $spf if ref $spf eq $class; return $self->_from_hashref($spf) if 'HASH' eq ref $spf; croak "invalid spf argument"; } sub domain { return $_[0]->{domain} if 1 == scalar @_; return $_[0]->{domain} = lc $_[1]; } sub result { return $_[0]->{result} if 1 == scalar @_; croak if !$_[0]->is_valid_spf_result( $_[1] ); return $_[0]->{result} = $_[1]; } sub scope { return $_[0]->{scope} if 1 == scalar @_; croak if ! $_[0]->is_valid_spf_scope( $_[1] ); return $_[0]->{scope} = $_[1]; } sub _from_hash { my ($self, %args) = @_; foreach my $key ( keys %args ) { # scope is frequently absent on received reports next if ($key eq 'scope' && !$args{$key}); $self->$key( $args{$key} ); } $self->is_valid; return $self; } sub _from_hashref { return $_[0]->_from_hash(%{ $_[1] }); } sub is_valid { my $self = shift; foreach my $f (qw/ domain result scope /) { next if $self->{$f}; if ($f ne 'scope') { # quite a few DMARC reporters don't include scope warn "SPF $f is required but missing!\n"; } return 0; } if ( $self->{result} =~ /^pass$/i && !$self->{domain} ) { warn "SPF pass MUST include the RFC5321.MailFrom domain!\n"; return 0; } return 1; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Aggregate::Record::Auth_Results::SPF - auth_results/spf section of a DMARC aggregate record =head1 VERSION version 1.20240314 =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record/Row000755000765000024 014574361234 22466 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Aggregate/Record/Row/Policy_Evaluated.pm000444000765000024 312714574361234 26415 0ustar00mattstaff000000000000package Mail::DMARC::Report::Aggregate::Record::Row::Policy_Evaluated; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; sub new { my ( $class, @args ) = @_; croak "invalid arguments" if @args % 2; my %args = @args; my $self = bless { reason => [] }, $class; foreach my $key ( keys %args ) { $self->$key( $args{$key} ); } return $self; } sub disposition { return $_[0]->{disposition} if 1 == scalar @_; croak "invalid disposition ($_[1]" if 0 == grep {/^$_[1]$/ix} qw/ reject quarantine none /; return $_[0]->{disposition} = $_[1]; } sub dkim { return $_[0]->{dkim} if 1 == scalar @_; return $_[0]->{dkim} = $_[1]; } sub spf { return $_[0]->{spf} if 1 == scalar @_; return $_[0]->{spf} = $_[1]; } sub reason { return $_[0]->{reason} if 1 == scalar @_; if ('ARRAY' eq ref $_[1]) { # one shot argument $_[0]->{reason} = $_[1]; } else { push @{ $_[0]->{reason} }, $_[1]; } return $_[0]->{reason}; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Aggregate::Record::Row::Policy_Evaluated - row/policy_evaluated section of a DMARC aggregate record =head1 VERSION version 1.20240314 =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Send000755000765000024 014574361234 17504 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Send/HTTP.pm000444000765000024 377214574361234 20767 0ustar00mattstaff000000000000package Mail::DMARC::Report::Send::HTTP; use strict; use warnings; our $VERSION = '1.20240314'; use Carp; #use Data::Dumper; #use HTTP::Tiny; # a possibility #use Net::HTTP; # lazy loaded in 'post' use parent 'Mail::DMARC::Base'; sub post { my ( $self, $uri, $report, $gz ) = @_; carp "http send feature not complete!"; return; ## no critic (Unreachable,Eval) # TODO: test against real HTTP server, validate HTTP response eval "require Net::HTTP" or return; my $ver = $Mail::DMARC::Base::VERSION; my $s = Net::HTTP->new( Host => $uri->host ) or croak $@; $s->write_request( POST => $uri->path, 'User-Agent' => "Mail::DMARC/$ver" ); my ( $code, $mess, %h ) = $s->read_response_headers; while (1) { my $buf; my $n = $s->read_entity_body( $buf, 1024 ); croak "read failed: $!" unless defined $n; last unless $n; print $buf; return 1; } return 0; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Send::HTTP - utility methods to send reports by HTTP =head1 VERSION version 1.20240314 =head1 12.2.2. HTTP Where an "http" or "https" method is requested in a Domain Owner's URI list, the Mail Receiver MAY encode the data using the "application/gzip" media type ([GZIP]) or MAY send the Appendix C data uncompressed or unencoded. The header portion of the POST or PUT request SHOULD contain a Subject field as described in Section 12.2.1. HTTP permits the use of Content-Transfer-Encoding to upload gzip content using the POST or PUT instruction after translating the content to 7-bit ASCII. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Send/SMTP.pm000444000765000024 2047314574361234 21010 0ustar00mattstaff000000000000package Mail::DMARC::Report::Send::SMTP; use strict; use warnings; our $VERSION = '1.20240314'; use Carp; use English '-no_match_vars'; use Email::MIME; #use Mail::Sender; # something to consider use Sys::Hostname; use POSIX; use parent 'Mail::DMARC::Base'; sub get_domain_mx { my ( $self, $domain ) = @_; print "getting MX for $domain\n"; my $query; eval { $query = $self->get_resolver->send( $domain, 'MX' ) or return []; } or print $@; if ( ! $query ) { print "\terror:\n\t$@"; return []; }; my @mx; for my $rr ( $query->answer ) { next if $rr->type ne 'MX'; push @mx, { pref => $rr->preference, addr => $rr->exchange }; print $rr->exchange if $self->verbose; } if ( $self->verbose ) { print "found " . scalar @mx . "MX exchanges\n"; }; return \@mx; } sub get_smtp_hosts { my $self = shift; my $email = shift or croak "missing email!"; my ($domain) = ( split /@/, $email )[-1]; my @mx = map { $_->{addr} } sort { $a->{pref} <=> $b->{pref} } @{ $self->get_domain_mx($domain) }; push @mx, $domain; print "\tfound " . scalar @mx . " MX for $email\n" if $self->verbose; return @mx; } sub get_subject { my ( $self, $agg_ref ) = @_; my $rid = $$agg_ref->metadata->report_id || $self->time; my $id = POSIX::strftime( "%Y.%m.%d.", localtime $self->time ) . $rid; my $us = $self->config->{organization}{domain}; if ($us eq 'example.com') { die "Please update mail-dmarc.ini"; } my $pol_dom = $$agg_ref->policy_published->domain; return "Report Domain: $pol_dom Submitter: $us Report-ID:$id"; } sub human_summary { my ( $self, $agg_ref ) = @_; my $records = scalar @{ $$agg_ref->{record} }; my $OrgName = $self->config->{organization}{org_name}; my $pass = grep { 'pass' eq $_->{row}{policy_evaluated}{dkim} || 'pass' eq $_->{row}{policy_evaluated}{spf} } @{ $$agg_ref->{record} }; my $fail = grep { 'pass' ne $_->{row}{policy_evaluated}{dkim} && 'pass' ne $_->{row}{policy_evaluated}{spf} } @{ $$agg_ref->{record} }; my $ver = $Mail::DMARC::Base::VERSION || ''; # undef in author environ my $from = $$agg_ref->{policy_published}{domain} or croak; return <<"EO_REPORT" This is a DMARC aggregate report for $from $records records. $pass passed. $fail failed. Submitted by $OrgName Generated with Mail::DMARC $ver EO_REPORT ; } sub get_filename { my ( $self, $agg_ref ) = @_; # 2013 DMARC Draft, 12.2.1 Email # # filename = receiver "!" policy-domain "!" begin-timestamp "!" # end-timestamp [ "!" unique-id ] "." extension # filename="mail.receiver.example!example.com!1013662812!1013749130.gz" return join( '!', $self->config->{organization}{domain}, $$agg_ref->policy_published->domain, $$agg_ref->metadata->begin, $$agg_ref->metadata->end, $$agg_ref->metadata->report_id || $self->time, ) . '.xml'; } sub assemble_too_big_message_object { my ( $self, $to, $body ) = @_; my @parts = Email::MIME->create( attributes => { content_type => "text/plain", disposition => "inline", charset => "US-ASCII", }, body => $body, ) or croak "unable to add body!"; my $email = Email::MIME->create( header_str => [ From => $self->config->{organization}{email}, To => $to, Date => $self->get_timestamp_rfc2822, Subject => 'DMARC too big report', ], parts => [@parts], ) or croak "unable to assemble message\n"; return $email; } sub assemble_message_object { my ( $self, $agg_ref, $to, $shrunk ) = @_; my $filename = $self->get_filename($agg_ref); # WARNING: changes made here MAY affect Send::compress. Check it! # my $cf = ( time > 1372662000 ) ? 'gzip' : 'zip'; # gz after 7/1/13 my $cf = 'gzip'; $filename .= $cf eq 'gzip' ? '.gz' : '.zip'; my @parts = Email::MIME->create( attributes => { content_type => "text/plain", disposition => "inline", charset => "US-ASCII", }, body => $self->human_summary( $agg_ref ), ) or croak "unable to add body!"; push @parts, Email::MIME->create( attributes => { filename => $filename, content_type => "application/$cf", encoding => "base64", name => $filename, }, body => $shrunk, ) or croak "unable to add report!"; my $email = Email::MIME->create( header_str => [ From => $self->config->{organization}{email}, To => $to, Date => $self->get_timestamp_rfc2822, Subject => $self->get_subject( $agg_ref ), ], parts => [@parts], ) or croak "unable to assemble message\n"; return $email; } sub get_timestamp_rfc2822 { my ($self, @args) = @_; my @ts = scalar @args ? @args : localtime $self->time; my $locale = setlocale(LC_CTYPE); setlocale(LC_ALL, 'C'); my $timestamp = POSIX::strftime( '%a, %d %b %Y %H:%M:%S %z', @ts ); setlocale(LC_ALL, $locale); return $timestamp; } sub get_helo_hostname { my $self = shift; my $host = $self->config->{smtp}{hostname}; return $host if $host && $host ne 'mail.example.com'; return Sys::Hostname::hostname; }; 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Send::SMTP - utility methods for sending reports via SMTP =head1 VERSION version 1.20240314 =head2 SUBJECT FIELD The RFC5322.Subject field for individual report submissions SHOULD conform to the following ABNF: dmarc-subject = %x52.65.70.6f.72.74 1*FWS ; "Report" %x44.6f.6d.61.69.6e.3a 1*FWS ; "Domain:" domain-name 1*FWS ; from RFC6376 %x53.75.62.6d.69.74.74.65.72.3a ; "Submitter:" 1*FWS domain-name 1*FWS %x52.65.70.6f.72.74.2d.49.44.3a ; "Report-ID:" msg-id ; from RFC5322 The first domain-name indicates the DNS domain name about which the report was generated. The second domain-name indicates the DNS domain name representing the Mail Receiver generating the report. The purpose of the Report-ID: portion of the field is to enable the Domain Owner to identify and ignore duplicate reports that might be sent by a Mail Receiver. =head1 12.2.1 Email In the case of a "mailto" URI, the Mail Receiver SHOULD communicate reports using the method described in [STARTTLS]. The message generated by the Mail Receiver must be a [MIME] formatted [MAIL] message. The aggregate report itself MUST be included in one of the parts of the message. A human-readable portion MAY be included as a MIME part (such as a text/plain part). The aggregate data MUST be an XML file subjected to GZIP compression. The aggregate data MUST be present using the media type "application/ gzip", and the filenames SHOULD be constructed using the following ABNF: filename = receiver "!" policy-domain "!" begin-timestamp "!" end-timestamp [ "!" unique-id ] "." extension unique-id = token ; "token" is imported from [MIME] receiver = domain ; imported from [MAIL] policy-domain = domain begin-timestamp = 1*DIGIT ; seconds since 00:00:00 UTC January 1, 1970 ; indicating start of the time range contained ; in the report end-timestamp = 1*DIGIT ; seconds since 00:00:00 UTC January 1, 1970 ; indicating end of the time range contained ; in the report extension = "xml" / "gzip" For the GZIP file itself, the extension MUST be "gz"; for the XML report, the extension MUST be "xml". =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Store000755000765000024 014574361234 17707 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Store/SQL.pm000444000765000024 5231414574361234 21066 0ustar00mattstaff000000000000package Mail::DMARC::Report::Store::SQL; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; use Data::Dumper; use DBIx::Simple; use File::ShareDir; use Mail::DMARC::Report::Store::SQL::Grammars::MySQL; use Mail::DMARC::Report::Store::SQL::Grammars::SQLite; use Mail::DMARC::Report::Store::SQL::Grammars::PostgreSQL; use parent 'Mail::DMARC::Base'; use Mail::DMARC::Report::Aggregate; sub save_aggregate { my ( $self, $agg ) = @_; $self->db_connect(); croak "policy_published must be a Mail::DMARC::Policy object" if 'Mail::DMARC::Policy' ne ref $agg->policy_published; #warn Dumper($meta); ## no critic (Carp) foreach my $f ( qw/ org_name email begin end / ) { croak "meta field $f required" if ! $agg->metadata->$f; } my $rid = $self->get_report_id( $agg ) or croak "failed to create report!"; # on 6/8/2013, Microsoft spat out a bunch of reports with zero records. if ( ! $agg->record ) { warn "\ta report with ZERO records! Illegal.\n"; ## no critic (Carp) return $rid; }; foreach my $rec ( @{ $agg->record } ) { $self->insert_agg_record($rid, $rec); }; return $rid; } sub retrieve { my ( $self, %args ) = @_; my $query = $self->grammar->select_report_query; my @params; if ( $args{rid} ) { $query .= $self->grammar->and_arg('r.id'); push @params, $args{rid}; }; if ( $args{begin} ) { $query .= $self->grammar->and_arg('r.begin', '>='); push @params, $args{begin}; }; if ( $args{end} ) { $query .= $self->grammar->and_arg('r.end', '<='); push @params, $args{end}; }; if ( $args{author} ) { $query .= $self->grammar->and_arg('a.org_name'); push @params, $args{author}; }; if ( $args{from_domain} ) { $query .= $self->grammar->and_arg('fd.domain'); push @params, $args{from_domain}; }; my $reports = $self->query( $query, \@params ); foreach (@$reports ) { $_->{begin} = join(" ", split(/T/, $self->epoch_to_iso( $_->{begin} ))); $_->{end} = join(" ", split(/T/, $self->epoch_to_iso( $_->{end} ))); }; return $reports; } sub next_todo { my ( $self ) = @_; if ( ! exists $self->{ _todo_list } ) { $self->{_todo_list} = $self->query( $self->grammar->select_todo_query, [ $self->time ] ); return if ! $self->{_todo_list}; } my $next_todo = shift @{ $self->{_todo_list} }; if ( ! $next_todo ) { delete $self->{_todo_list}; return; } my $agg = Mail::DMARC::Report::Aggregate->new(); $self->populate_agg_metadata( \$agg, \$next_todo ); my $pp = $self->get_report_policy_published( $next_todo->{rid} ); $pp->{domain} = $next_todo->{from_domain}; $agg->policy_published( Mail::DMARC::Policy->new( %$pp ) ); $self->populate_agg_records( \$agg, $next_todo->{rid} ); return $agg; } sub retrieve_todo { my ( $self, @args ) = @_; # this method extracts the data from the SQL tables and populates a # list of Aggregate report objects with them. my $reports = $self->query( $self->grammar->select_todo_query, [ $self->time ] ); my @reports_todo; return \@reports_todo if ! scalar @$reports; foreach my $report ( @{ $reports } ) { my $agg = Mail::DMARC::Report::Aggregate->new(); $self->populate_agg_metadata( \$agg, \$report ); my $pp = $self->get_report_policy_published( $report->{rid} ); $pp->{domain} = $report->{from_domain}; $agg->policy_published( Mail::DMARC::Policy->new( %$pp ) ); $self->populate_agg_records( \$agg, $report->{rid} ); push @reports_todo, $agg; } return \@reports_todo; } sub delete_report { my $self = shift; my $report_id = shift or croak "missing report ID"; print "deleting report $report_id\n" if $self->verbose; # deletes with FK don't cascade in SQLite? Clean each table manually my $rows = $self->query( $self->grammar->report_record_id, [$report_id] ); my @row_ids = map { $_->{id} } @$rows; if (scalar @row_ids) { foreach my $table (qw/ report_record_spf report_record_dkim report_record_reason /) { print "deleting $table rows " . join(',', @row_ids) . "\n" if $self->verbose; eval { $self->query( $self->grammar->delete_from_where_record_in($table), \@row_ids); }; # warn $@ if $@; } } foreach my $table (qw/ report_policy_published report_record report_error /) { print "deleting $table rows for report $report_id\n" if $self->verbose; eval { $self->query( $self->grammar->delete_from_where_report($table), [$report_id] ); }; # warn $@ if $@; } # In MySQL, where FK constraints DO cascade, this is the only query needed $self->query( $self->grammar->delete_report, [$report_id] ); return 1; } sub get_domain_id { my ( $self, $domain ) = @_; croak "missing domain calling " . ( caller(0) )[3] if !$domain; my $r = $self->query( $self->grammar->select_domain_id, [$domain] ); if ( $r && scalar @$r ) { return $r->[0]{id}; } return $self->query( $self->grammar->insert_domain, [$domain]); } sub get_author_id { my ( $self, $meta ) = @_; croak "missing author name" if !$meta->org_name; my $r = $self->query( $self->grammar->select_author_id, [ $meta->org_name ] ); if ( $r && scalar @$r ) { return $r->[0]{id}; } carp "missing email" if !$meta->email; return $self->query( $self->grammar->insert_author, [ $meta->org_name, $meta->email, $meta->extra_contact_info ] ); } sub get_report_id { my ( $self, $aggr ) = @_; my $meta = $aggr->metadata; my $pol = $aggr->policy_published; # check if report exists my $author_id = $self->get_author_id( $meta ) or croak; my $from_dom_id = $self->get_domain_id( $pol->domain ) or croak; my $ids; if ( $meta->report_id ) { # reports arriving via the wire will have an author ID & report ID $ids = $self->query( $self->grammar->select_report_id, [ $meta->report_id, $author_id ] ); } else { # Reports submitted by our local MTA will not have a report ID # They aggregate on the From domain, where the DMARC policy was discovered $ids = $self->query( $self->grammar->select_id_with_end, [ $from_dom_id, $self->time, $author_id ] ); }; if ( scalar @$ids ) { # report already exists return $self->{report_id} = $ids->[0]{id}; } my $rid = $self->{report_id} = $self->query( $self->grammar->insert_report, [ $from_dom_id, $meta->begin, $meta->end, $author_id, $meta->uuid ] ) or return; $self->insert_policy_published( $rid, $pol ); return $rid; } sub get_report { my ($self,@args) = @_; croak "invalid parameters" if @args % 2; my %args = @args; my $query = $self->grammar->select_report_query; my @params; my @known = qw/ r.id a.org_name fd.domain r.begin r.end /; my %known = map { $_ => 1 } @known; # TODO: allow custom search ops? 'searchOper' => 'eq', if ( $args{searchField} && $known{ $args{searchField} } ) { $query .= $self->grammar->and_arg($args{searchField}); push @params, $args{searchString}; }; foreach my $known ( @known ) { next if ! defined $args{$known}; $query .= $self->grammar->and_arg($known); push @params, $args{$known}; }; if ( $args{sidx} && $known{$args{sidx}} ) { if ( $args{sord} ) { $query .= $self->grammar->order_by($args{sidx}, $args{sord} eq 'desc' ? ' DESC' : ' ASC'); }; }; my $total_recs = $self->dbix->query($self->grammar->count_reports)->list; my $total_pages = 0; if ( $args{rows} ) { if ( $args{page} ) { $total_pages = POSIX::ceil($total_recs / $args{rows}); my $start = ($args{rows} * $args{page}) - $args{rows}; $start = 0 if $start < 0; $query .= $self->grammar->limit_args(2); push @params, $start, $args{rows}; } else { $query .= $self->grammar->limit_args; push @params, $args{rows}; }; }; # warn "query: $query\n" . join(", ", @params) . "\n"; my $reports = $self->query($query, \@params); foreach (@$reports ) { $_->{begin} = join('
', split(/T/, $self->epoch_to_iso( $_->{begin} ))); $_->{end} = join('
', split(/T/, $self->epoch_to_iso( $_->{end} ))); }; # return in the format expected by jqGrid return { cur_page => $args{page}, total_pages => $total_pages, total_rows => $total_recs, rows => $reports, }; } sub get_report_policy_published { my ($self, $rid) = @_; my $pp = $self->query($self->grammar->select_report_policy_published, [ $rid ] )->[0]; $pp->{p} ||= 'none'; $pp = Mail::DMARC::Policy->new( v=>'DMARC1', %$pp ); return $pp; } sub get_rr { my ($self,@args) = @_; croak "invalid parameters" if @args % 2; my %args = @args; # warn Dumper(\%args); croak "missing report ID (rid)!" if ! defined $args{rid}; my $rows = $self->query( $self->grammar->select_rr_query, [ $args{rid} ] ); foreach ( @$rows ) { $_->{source_ip} = $self->any_inet_ntop( $_->{source_ip} ) if $self->grammar->language ne 'postgresql'; $_->{reasons} = $self->query($self->grammar->select_report_reason, [ $_->{id} ] ); }; return { cur_page => 1, total_pages => 1, total_rows => scalar @$rows, rows => $rows, }; } sub populate_agg_metadata { my ($self, $agg_ref, $report_ref) = @_; $$agg_ref->metadata->report_id( $$report_ref->{rid} ); foreach my $f ( qw/ org_name email extra_contact_info / ) { $$agg_ref->metadata->$f( $self->config->{organization}{$f} ); }; foreach my $f ( qw/ begin end / ) { $$agg_ref->metadata->$f( $$report_ref->{$f} ); }; my $errors = $self->query($self->grammar->select_report_error, [ $$report_ref->{rid} ] ); foreach ( @$errors ) { $$agg_ref->metadata->error( $_->{error} ); }; return 1; } sub populate_agg_records { my ($self, $agg_ref, $rid) = @_; my $recs = $self->query( $self->grammar->select_rr_query, [ $rid ] ); # aggregate the connections per IP-Disposition-DKIM-SPF uniqueness my (%ips, %uniq, %pe, %auth, %ident, %reasons, %other); foreach my $rec ( @$recs ) { my $ip = $rec->{source_ip}; $ip = $self->any_inet_ntop($rec->{source_ip}) if $self->grammar->language ne 'postgresql'; my $key = join('-', $ip, @$rec{ qw/ disposition dkim spf / }); # hash slice $uniq{ $key }++; $ips{$key} = $rec->{source_ip}; $ident{$key}{header_from} ||= $rec->{header_from}; $ident{$key}{envelope_from} ||= $rec->{envelope_from}; $ident{$key}{envelope_to} ||= $rec->{envelope_to}; $pe{$key}{disposition} ||= $rec->{disposition}; $pe{$key}{dkim} ||= $rec->{dkim}; $pe{$key}{spf} ||= $rec->{spf}; $auth{$key}{spf} ||= $self->get_row_spf($rec->{id}); $auth{$key}{dkim} ||= $self->get_row_dkim($rec->{id}); my $reasons = $self->get_row_reason( $rec->{id} ); foreach my $reason ( @$reasons ) { my $type = $reason->{type} or next; $reasons{$key}{$type} = $reason->{comment}; # flatten reasons } } foreach my $u ( keys %uniq ) { my $record = Mail::DMARC::Report::Aggregate::Record->new( identifiers => $ident{$u}, auth_results => $auth{$u}, row => { source_ip => $self->grammar->language eq 'postgresql' ? $ips{$u} : $self->any_inet_ntop( $ips{$u} ), count => $uniq{ $u }, policy_evaluated => { %{ $pe{$u} }, $reasons{$u} ? ( reason => [ map { { type => $_, comment => $reasons{$u}{$_} } } sort keys %{ $reasons{$u} } ] ) : (), }, } ); $$agg_ref->record( $record ); } return $$agg_ref->record; } sub row_exists { my ($self, $rid, $rec ) = @_; if ( ! defined $rec->{row}{count} ) { print "new record\n" if $self->verbose; return; }; my $rows = $self->query( $self->grammar->select_report_record, [ $rid, $rec->{row}{source_ip}, $rec->{row}{count}, ] ); return 1 if scalar @$rows; return; } sub insert_agg_record { my ($self, $row_id, $rec) = @_; return 1 if $self->row_exists( $row_id, $rec); $row_id = $self->insert_rr( $row_id, $rec ) or croak "failed to insert report row"; my $reasons = $rec->row->policy_evaluated->reason; if ( $reasons ) { foreach my $reason ( @$reasons ) { next if !$reason || !$reason->{type}; $self->insert_rr_reason( $row_id, $reason->{type}, $reason->{comment} ); }; } my $spf_ref = $rec->auth_results->spf; if ( $spf_ref ) { foreach my $spf (@$spf_ref) { $self->insert_rr_spf( $row_id, $spf ); } } my $dkim = $rec->auth_results->dkim; if ($dkim) { foreach my $sig (@$dkim) { next if ! $sig || ! $sig->{domain}; $self->insert_rr_dkim( $row_id, $sig ); } } return 1; } sub insert_error { my ( $self, $rid, $error ) = @_; # wait >5m before trying to deliver this report again $self->query($self->grammar->insert_error(0), [$self->time + (5*60), $rid]); return $self->query( $self->grammar->insert_error(1), [ $rid, $error ] ); } sub insert_rr_reason { my ( $self, $row_id, $type, $comment ) = @_; return $self->query( $self->grammar->insert_rr_reason, [ $row_id, $type, ($comment || '') ] ); } sub insert_rr_dkim { my ( $self, $row_id, $dkim ) = @_; my (@fields, @values); foreach ( qw/ domain selector result human_result / ) { next if ! defined $dkim->{$_}; if ( 'domain' eq $_ ) { push @fields, 'domain_id'; push @values, $self->get_domain_id( $dkim->{domain} ); next; }; push @fields, $_; push @values, $dkim->{$_}; }; my $query = $self->grammar->insert_rr_dkim(\@fields); $self->query( $query, [ $row_id, @values ] ); return 1; } sub insert_rr_spf { my ( $self, $row_id, $spf ) = @_; my (@fields, @values); for ( qw/ domain scope result / ) { next if ! defined $spf->{$_}; if ( 'domain' eq $_ ) { push @fields, 'domain_id'; push @values, $self->get_domain_id( $spf->{domain} ); next; }; push @fields, $_; push @values, $spf->{$_}; }; my $query = $self->grammar->insert_rr_spf(\@fields); $self->query( $query, [ $row_id, @values ]); return 1; } sub insert_rr { my ( $self, $report_id, $rec ) = @_; $report_id or croak "report ID required?!"; my $query = $self->grammar->insert_rr; my $ip = $rec->row->source_ip; $ip = $self->any_inet_pton( $ip ) if $self->grammar->language ne 'postgresql'; my @args = ( $report_id, $ip, $rec->{row}{count}, ); foreach my $f ( qw/ header_from envelope_to envelope_from / ) { push @args, $rec->identifiers->$f ? $self->get_domain_id( $rec->identifiers->$f ) : undef; }; push @args, map { $rec->row->policy_evaluated->$_ } qw/ disposition dkim spf /; my $rr_id = $self->query( $query, \@args ) or croak; return $self->{report_row_id} = $rr_id; } sub insert_policy_published { my ( $self, $id, $pub ) = @_; my $query = $self->grammar->insert_policy_published; $self->query( $query, [ $id, @$pub{ qw/ adkim aspf p sp pct rua /} ] ); return 1; } sub db_connect { my $self = shift; my $dsn = $self->config->{report_store}{dsn} or croak; my $user = $self->config->{report_store}{user}; my $pass = $self->config->{report_store}{pass}; # cacheing if ($self->{grammar} && $self->{dbix}) { my $cached_grammar_type = $self->{grammar}->dsn; if ( $dsn =~ /$cached_grammar_type/ ) { return $self->{dbix}; # caching } } my $needs_tables; $self->{grammar} = undef; if ($dsn =~ /sqlite/i) { my ($db) = ( split /=/, $dsn )[-1]; if ( !$db || $db eq ':memory:' || !-e $db ) { my $schema = 'mail_dmarc_schema.sqlite'; $needs_tables = $self->get_db_schema($schema) or croak "can't locate DB $db AND can't find $schema! Create $db manually.\n"; } $self->{grammar} = Mail::DMARC::Report::Store::SQL::Grammars::SQLite->new(); } elsif ($dsn =~ /mysql/i) { $self->{grammar} = Mail::DMARC::Report::Store::SQL::Grammars::MySQL->new(); } elsif ($dsn =~ /pg/i) { $self->{grammar} = Mail::DMARC::Report::Store::SQL::Grammars::PostgreSQL->new(); } else { croak "can't determine database type, so unable to load grammar.\n"; } $self->{dbix} = DBIx::Simple->connect( $dsn, $user, $pass ) or return $self->error( DBIx::Simple->error ); if ($needs_tables) { $self->apply_db_schema($needs_tables); } return $self->{dbix}; } sub db_check_err { my ( $self, $err ) = @_; ## no critic (PackageVars) return if !defined $DBI::errstr; return if !$DBI::errstr; return if $DBI::errstr eq 'DBI error: '; croak $err . $DBI::errstr; } sub dbix { return $_[0]->{dbix} if $_[0]->{dbix}; return $_[0]->db_connect(); } sub apply_db_schema { my ( $self, $file ) = @_; my $setup = $self->slurp($file); foreach ( split /;/, $setup ) { # warn "$_\n"; $self->dbix->query($_); } return; } sub get_db_schema { my ( $self, $file ) = @_; return "share/$file" if -f "share/$file"; # when testing return File::ShareDir::dist_file( 'Mail-DMARC', $file ); # when installed } sub query { my ( $self, $query, $params, @extra ) = @_; my @c = caller; my $err = sprintf( "query called by %s, %s\n", $c[0], $c[2] ) . "\t$query\n\t"; my @params; if ( defined $params ) { @params = ref $params eq 'ARRAY' ? @$params : $params; no warnings; ## no critic (NoWarnings) $err .= join( ', ', @params ); } croak "too many arguments to exec_query!" if @extra; my $dbix = $self->db_connect() or croak DBIx::Simple->error; return $self->query_insert( $query, $err, @params ) if $query =~ /^INSERT/ix; return $self->query_replace( $query, $err, @params ) if $query =~ /^(?:REPLACE|UPDATE)/ix; return $self->query_delete( $query, $err, @params ) if $query =~ /^(?:DELETE|TRUNCATE)/ix; return $self->query_any( $query, $err, @params ); } sub query_any { my ( $self, $query, $err, @params ) = @_; # warn "query: $query\n" . join(", ", @params) . "\n"; my $r; eval { $r = $self->dbix->query( $query, @params )->hashes; } or print ''; $self->db_check_err($err); die "something went wrong with: $err\n" if ! $r; ## no critic (Carp) return $r; } sub query_insert { my ( $self, $query, $err, @params ) = @_; eval { $self->dbix->query( $query, @params ) } or do { warn DBIx::Simple->error . "\n"; croak $err; }; $self->db_check_err($err); # If the table has no autoincrement field, last_insert_id is zero my ( undef, undef, $table ) = split /\s+/, $query; ($table) = split( /\(/, $table ) if $table =~ /\(/; $table =~ s/^"|"$//g; croak "unable to determine table in query: $query" if !$table; return $self->dbix->last_insert_id( undef, undef, $table, undef ); } sub query_replace { my ( $self, $query, $err, @params ) = @_; $self->dbix->query( $query, @params ) or croak $err; $self->db_check_err($err); return 1; # sorry, no indication of success } sub query_delete { my ( $self, $query, $err, @params ) = @_; my $affected = $self->dbix->query( $query, @params )->rows or croak $err; $self->db_check_err($err); return $affected; } sub get_row_spf { my ($self, $rowid) = @_; return $self->query( $self->grammar->select_row_spf, [ $rowid ] ); } sub get_row_dkim { my ($self, $rowid) = @_; return $self->query( $self->grammar->select_row_dkim, [ $rowid ] ); } sub get_row_reason { my ($self, $rowid) = @_; return $self->query( $self->grammar->select_row_reason, [ $rowid ] ); } sub grammar { my $self = shift; $self->db_connect(); return $self->{grammar}; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Store::SQL - store and retrieve reports from a SQL RDBMS =head1 VERSION version 1.20240314 =head1 DESCRIPTION Uses ANSI SQL syntax, keeping the SQL as portable as possible. DB engine specific features are to be avoided. =head1 SYPNOSIS Store and retrieve DMARC reports from SQL data store. Tested with SQLite, MySQL and PostgreSQL. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Store/SQL000755000765000024 014574361234 20346 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Store/SQL/Grammars000755000765000024 014574361234 22117 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Store/SQL/Grammars/MySQL.pm000444000765000024 1525414574361234 23606 0ustar00mattstaff000000000000package Mail::DMARC::Report::Store::SQL::Grammars::MySQL; our $VERSION = '1.20240314'; use strict; use warnings; sub new { my $class = shift; my $self = { }; bless $self, $class; return $self; } sub language { return 'mysql'; } sub dsn { return 'mysql'; } sub and_arg { my ($self, $column, $operator) = @_; $operator //= '='; return " AND $column $operator ?"; } sub report_record_id { return 'SELECT id FROM report_record WHERE report_id=?'; } sub delete_from_where_record_in { my ($self, $table) = @_; return "DELETE FROM $table WHERE report_record_id IN (??)" } sub delete_from_where_report { my ($self, $table) = @_; return "DELETE FROM $table WHERE report_id=?"; } sub delete_report { return "DELETE FROM report WHERE id=?"; } sub select_domain_id { return 'SELECT id FROM domain WHERE domain=?'; } sub insert_domain { return 'INSERT INTO domain (domain) VALUES (?)'; } sub select_author_id { return 'SELECT id FROM author WHERE org_name=?'; } sub insert_author { return 'INSERT INTO author (org_name,email,extra_contact) VALUES (?,?,?)'; } sub select_report_id { return 'SELECT id FROM report WHERE uuid=? AND author_id=?'; } sub select_id_with_end { return 'SELECT id FROM report WHERE from_domain_id=? AND end > ? AND author_id=?'; } sub insert_report { return 'INSERT INTO report (from_domain_id, begin, end, author_id, uuid) VALUES (?,?,?,?,?)'; } sub order_by { my ($self, $arg, $order) = @_; return " ORDER BY $arg $order"; } sub count_reports { return 'SELECT COUNT(*) FROM report'; } sub limit { my ($self, $number_of_entries) = @_; $number_of_entries //= 1; return " LIMIT $number_of_entries"; } sub limit_args { my ($self, $number_of_entries) = @_; my $return = ' LIMIT '; $number_of_entries //= 1; for (my $i = 1; $i <= $number_of_entries; $i++) { $return .= '?'; $return .= ',' if $i < $number_of_entries; } return $return; } sub select_report_policy_published { return 'SELECT * from report_policy_published WHERE report_id=?'; } sub select_report_reason { return 'SELECT type,comment FROM report_record_reason WHERE report_record_id=?'; } sub select_report_error { return 'SELECT error FROM report_error WHERE report_id=?'; } sub select_report_record { return 'SELECT id FROM report_record WHERE report_id=? AND source_ip=? AND count=?' } sub select_todo_query { return <<'EO_TODO_QUERY' SELECT r.id AS rid, r.begin AS begin, r.end AS end, a.org_name AS author, fd.domain AS from_domain FROM report r LEFT JOIN report_record rr ON r.id=rr.report_id LEFT JOIN author a ON r.author_id=a.id LEFT JOIN domain fd ON r.from_domain_id=fd.id WHERE rr.count IS NULL AND rr.report_id IS NOT NULL AND r.end < ? GROUP BY r.id ORDER BY r.id ASC EO_TODO_QUERY ; } sub select_row_spf { return <<"EO_SPF_ROW" SELECT d.domain AS domain, s.result AS result, s.scope AS scope FROM report_record_spf s LEFT JOIN domain d ON s.domain_id=d.id WHERE s.report_record_id=? EO_SPF_ROW ; } sub select_row_dkim { return <<"EO_DKIM_ROW" SELECT d.domain AS domain, k.selector AS selector, k.result AS result, k.human_result AS human_result FROM report_record_dkim k LEFT JOIN domain d ON k.domain_id=d.id WHERE report_record_id=? EO_DKIM_ROW ; } sub select_row_reason { return <<"EO_ROW_QUERY" SELECT type,comment FROM report_record_reason WHERE report_record_id=? EO_ROW_QUERY ; } sub select_rr_query { return <<'EO_ROW_QUERY' SELECT rr.*, etd.domain AS envelope_to, efd.domain AS envelope_from, hfd.domain AS header_from FROM report_record rr LEFT JOIN domain etd ON etd.id=rr.envelope_to_did LEFT JOIN domain efd ON efd.id=rr.envelope_from_did LEFT JOIN domain hfd ON hfd.id=rr.header_from_did WHERE report_id = ? ORDER BY id ASC EO_ROW_QUERY ; } sub select_report_query { return <<'EO_REPORTS' SELECT r.id AS rid, r.uuid, r.begin AS begin, r.end AS end, a.org_name AS author, fd.domain AS from_domain FROM report r LEFT JOIN author a ON r.author_id=a.id LEFT JOIN domain fd ON r.from_domain_id=fd.id WHERE 1=1 EO_REPORTS ; } sub select_from { my ($self, $columns, $table) = @_; my $colStr = join( ', ', @$columns ); return "SELECT $colStr FROM $table WHERE 1=1"; } sub insert_error { my ( $self, $which ) = @_; if ( $which == 0 ) { return 'UPDATE report SET end=? WHERE id=?'; } else { return 'INSERT INTO report_error (report_id, error) VALUES (?,?)'; } } sub insert_rr_reason { return 'INSERT INTO report_record_reason (report_record_id, type, comment) VALUES (?,?,?)' } sub insert_rr_dkim { my ( $self, $fields ) = @_; my $fields_str = join ', ', @$fields; return <<"EO_DKIM" INSERT INTO report_record_dkim (report_record_id, $fields_str) VALUES (??) EO_DKIM ; } sub insert_rr_spf { my ( $self, $fields ) = @_; my $fields_str = join ', ', @$fields; return "INSERT INTO report_record_spf (report_record_id, $fields_str) VALUES(??)"; } sub insert_rr { return <<'EO_ROW_INSERT' INSERT INTO report_record (report_id, source_ip, count, header_from_did, envelope_to_did, envelope_from_did, disposition, dkim, spf) VALUES (??) EO_ROW_INSERT ; } sub insert_policy_published { return <<"EO_RPP" INSERT INTO report_policy_published (report_id, adkim, aspf, p, sp, pct, rua) VALUES (??) EO_RPP ; } sub insert_into { my ($self, $table, $cols) = @_; my $columns = join ', ', @$cols; return "INSERT INTO $table ($columns) VALUES (??)"; } sub replace_into { my ($self, $table, $cols) = @_; my $columns = join ', ', @$cols; return "REPLACE INTO $table ($columns) VALUES (??)"; } sub update { my ($self, $table, $cols) = @_; my $columns = join( ' = ?, ') . ' = ?'; return "UPDATE $table SET $columns WHERE 1=1"; } sub delete_from { my ($self, $table) = @_; return "DELETE FROM $table WHERE 1=1"; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Store::SQL::Grammars::MySQL - Grammar for working with mysql databases. =head1 VERSION version 1.20240314 =head1 SYPNOSIS Allow DMARC to be able to speak to MySQL databases. =head1 DESCRIPTION Uses ANSI SQL syntax, keeping the SQL as portable as possible. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Store/SQL/Grammars/PostgreSQL.pm000444000765000024 1741314574361234 24643 0ustar00mattstaff000000000000package Mail::DMARC::Report::Store::SQL::Grammars::PostgreSQL; our $VERSION = '1.20240314'; use strict; use warnings; sub new { my $class = shift; my $self = { }; bless $self, $class; return $self; } sub language { return 'postgresql'; } sub dsn { return 'Pg'; } sub and_arg { my ($self, $column, $operator) = @_; $operator //= '='; $column =~ s/(\w+)\.(\w+)/"$1"."$2"/ if $column =~ /\./; return " AND $column $operator ?"; } sub report_record_id { return 'SELECT "id" FROM "report_record" WHERE "report_id"=?'; } sub delete_from_where_record_in { my ($self, $table, $row_ids) = @_; return "DELETE FROM \"$table\" WHERE \"report_record_id\" IN (??)" } sub delete_from_where_report { my ($self, $table) = @_; return "DELETE FROM \"$table\" WHERE \"report_id\"=?"; } sub delete_report { return "DELETE FROM \"report\" WHERE \"id\"=?"; } sub select_domain_id { return 'SELECT "id" FROM "domain" WHERE "domain"=?'; } sub select_report_id { return 'SELECT "id" FROM "report" WHERE "uuid"=? AND "author_id"=?'; } sub select_id_with_end { return 'SELECT "id" FROM "report" WHERE "from_domain_id"=? AND "end" > ? AND "author_id"=?'; } sub insert_domain { return 'INSERT INTO "domain" ("domain") VALUES (?)'; } sub select_author_id { return 'SELECT "id" FROM "author" WHERE "org_name"=?'; } sub insert_author { return 'INSERT INTO "author" ("org_name", "email", "extra_contact") VALUES (?,?,?)'; } sub insert_report { return 'INSERT INTO "report" ("from_domain_id", "begin", "end", "author_id", "uuid") VALUES (?,?,?,?,?)'; } sub order_by { my ($self, $arg, $order) = @_; return " ORDER BY \"$arg\" $order"; } sub count_reports { return 'SELECT COUNT(*) FROM "report"'; } sub limit { my ($self, $number_of_entries) = @_; $number_of_entries //= 1; return " LIMIT $number_of_entries"; } sub limit_args { my ($self, $number_of_entries) = @_; my $return = ' LIMIT ?'; $number_of_entries //= 1; if ($number_of_entries > 1) { $return = " OFFSET ? $return"; } return $return; } sub select_report_policy_published { return 'SELECT * from "report_policy_published" WHERE "report_id"=?'; } sub select_report_reason { return 'SELECT "type","comment" FROM "report_record_reason" WHERE "report_record_id"=?'; } sub select_report_error { return 'SELECT "error" FROM "report_error" WHERE "report_id"=?'; } sub select_report_record { return 'SELECT "id" FROM "report_record" WHERE "report_id"=? AND "source_ip"=? AND "count"=?' } sub select_todo_query { return <<'EO_TODO_QUERY' SELECT "r"."id" AS "rid", "r"."begin" AS "begin", "r"."end" AS "end", "a"."org_name" AS "author", "fd"."domain" AS "from_domain" FROM "report" "r" LEFT JOIN "report_record" "rr" ON "r"."id"="rr"."report_id" LEFT JOIN "author" "a" ON "r"."author_id"="a"."id" LEFT JOIN "domain" "fd" ON "r"."from_domain_id"="fd"."id" WHERE "rr"."count" IS NULL AND "rr"."report_id" IS NOT NULL AND "r"."end" < ? GROUP BY "r"."id", "r"."begin", "r"."end", "a"."org_name", "fd"."domain" ORDER BY "r"."id" ASC EO_TODO_QUERY ; } sub select_row_spf { return <<"EO_SPF_ROW" SELECT "d"."domain" AS "domain", "s"."result" AS "result", "s"."scope" AS "scope" FROM "report_record_spf" "s" LEFT JOIN "domain" "d" ON "s"."domain_id"="d"."id" WHERE "s"."report_record_id"=? ORDER BY "s"."id" ASC EO_SPF_ROW ; } sub select_row_dkim { return <<"EO_DKIM_ROW" SELECT "d"."domain" AS "domain", "k"."selector" AS "selector", "k"."result" AS "result", "k"."human_result" AS "human_result" FROM "report_record_dkim" "k" LEFT JOIN "domain" "d" ON "k"."domain_id"="d"."id" WHERE "report_record_id"=? ORDER BY "k"."id" ASC EO_DKIM_ROW ; } sub select_row_reason { return <<"EO_ROW_QUERY" SELECT "type","comment" FROM "report_record_reason" WHERE "report_record_id"=? EO_ROW_QUERY ; } sub select_rr_query { return <<'EO_ROW_QUERY' SELECT "rr".*, "etd"."domain" AS "envelope_to", "efd"."domain" AS "envelope_from", "hfd"."domain" AS "header_from" FROM "report_record" "rr" LEFT JOIN "domain" "etd" ON "etd"."id"="rr"."envelope_to_did" LEFT JOIN "domain" "efd" ON "efd"."id"="rr"."envelope_from_did" LEFT JOIN "domain" "hfd" ON "hfd"."id"="rr"."header_from_did" WHERE "report_id" = ? ORDER BY "id" ASC EO_ROW_QUERY ; } sub select_report_query { return <<'EO_REPORTS' SELECT "r"."id" AS "rid", "r"."uuid", "r"."begin" AS "begin", "r"."end" AS "end", "a"."org_name" AS "author", "fd"."domain" AS "from_domain" FROM "report" "r" LEFT JOIN "author" "a" ON "r"."author_id"="a"."id" LEFT JOIN "domain" "fd" ON "r"."from_domain_id"="fd"."id" WHERE 1=1 EO_REPORTS ; } sub insert_error { my ( $self, $which ) = @_; if ( $which == 0 ) { return 'UPDATE "report" SET "end"=? WHERE "id"=?'; } else { return 'INSERT INTO "report_error" ("report_id", "error") VALUES (?,?)'; } } sub insert_rr_reason { return 'INSERT INTO "report_record_reason" ("report_record_id", "type", "comment") VALUES (?,?,?)' } sub insert_rr_dkim { my ( $self, $fields ) = @_; my $fields_str = join '", "', @$fields; return <<"EO_DKIM" INSERT INTO "report_record_dkim" ("report_record_id", \"$fields_str\") VALUES (??) EO_DKIM ; } sub insert_rr_spf { my ( $self, $fields ) = @_; my $fields_str = join '", "', @$fields; return "INSERT INTO \"report_record_spf\" (\"report_record_id\", \"$fields_str\") VALUES(??)"; } sub insert_rr { return <<'EO_ROW_INSERT' INSERT INTO report_record (report_id, source_ip, count, header_from_did, envelope_to_did, envelope_from_did, disposition, dkim, spf) VALUES (??) EO_ROW_INSERT ; } sub insert_policy_published { return <<"EO_RPP" INSERT INTO report_policy_published (report_id, adkim, aspf, p, sp, pct, rua) VALUES (??) EO_RPP ; } sub select_from { my ($self, $columns, $table) = @_; my $colStr = '*'; if ( @{$columns}[0] ne '*' ) { my @cols; foreach my $col (@$columns) { if ( $col =~ /(\w+)(?:\s+as\s+(\w+))/i ) { $col = "$1\" AS \"$2"; } $col = "\"$col\""; push @cols, $col; } $colStr = join( ', ', @cols ); } return "SELECT $colStr FROM \"$table\" WHERE 1=1"; } sub insert_into { my ($self, $table, $cols) = @_; my $columns = '"' . join( '", "', @$cols ) . '"'; return "INSERT INTO \"$table\" ($columns) VALUES (??)"; } sub update { my ($self, $table, $cols) = @_; my $columns = '"' . join( '" = ?, "') . '" = ?'; return "UPDATE \"$table\" SET $columns WHERE 1=1"; } sub delete_from { my ($self, $table) = @_; return "DELETE FROM \"$table\" WHERE 1=1"; } sub replace_into { my ($self, $table, $cols) = @_; my $insertColumns = '"' . join( '", "', @$cols ) . '"'; my @ucols; foreach my $col (@$cols) { push @ucols, "\"$col\" = EXCLUDED.\"$col\"" } my $updateColumns = join ', ', @ucols; return "INSERT INTO \"$table\" ($insertColumns) VALUES (??) ON CONFLICT ($insertColumns) DO UPDATE SET $updateColumns"; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Store::SQL::Grammars::PostgreSQL - Grammar for working with pgsql databases. =head1 VERSION version 1.20240314 =head1 SYPNOSIS Allow DMARC to be able to speak to PostgreSQL databases. =head1 DESCRIPTION =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Report/Store/SQL/Grammars/SQLite.pm000444000765000024 1534214574361234 24000 0ustar00mattstaff000000000000package Mail::DMARC::Report::Store::SQL::Grammars::SQLite; our $VERSION = '1.20240314'; use strict; use warnings; sub new { my $class = shift; my $self = { }; bless $self, $class; return $self; } sub language { return 'sqlite'; } sub dsn { return 'SQLite'; } sub and_arg { my ($self, $column, $operator) = @_; $operator //= '='; return " AND $column $operator ?"; } sub report_record_id { return 'SELECT id FROM report_record WHERE report_id=?'; } sub delete_from_where_record_in { my ($self, $table) = @_; return "DELETE FROM $table WHERE report_record_id IN (??)" } sub delete_from_where_report { my ($self, $table) = @_; return "DELETE FROM $table WHERE report_id=?"; } sub delete_report { return "DELETE FROM report WHERE id=?"; } sub select_domain_id { return 'SELECT id FROM domain WHERE domain=?'; } sub insert_domain { return 'INSERT INTO domain (domain) VALUES (?)'; } sub select_author_id { return 'SELECT id FROM author WHERE org_name=?'; } sub insert_author { return 'INSERT INTO author (org_name,email,extra_contact) VALUES (?,?,?)'; } sub select_report_id { return 'SELECT id FROM report WHERE uuid=? AND author_id=?'; } sub select_id_with_end { return 'SELECT id FROM report WHERE from_domain_id=? AND end > ? AND author_id=?'; } sub insert_report { return 'INSERT INTO report (from_domain_id, begin, end, author_id, uuid) VALUES (?,?,?,?,?)'; } sub order_by { my ($self, $arg, $order) = @_; return " ORDER BY $arg $order"; } sub count_reports { return 'SELECT COUNT(*) FROM report'; } sub limit { my ($self, $number_of_entries) = @_; $number_of_entries //= 1; return " LIMIT $number_of_entries"; } sub limit_args { my ($self, $number_of_entries) = @_; my $return = ' LIMIT '; $number_of_entries //= 1; for (my $i = 1; $i <= $number_of_entries; $i++) { $return .= '?'; $return .= ',' if $i < $number_of_entries; } return $return; } sub select_report_policy_published { return 'SELECT * from report_policy_published WHERE report_id=?'; } sub select_report_reason { return 'SELECT type,comment FROM report_record_reason WHERE report_record_id=?'; } sub select_report_error { return 'SELECT error FROM report_error WHERE report_id=?'; } sub select_report_record { return 'SELECT id FROM report_record WHERE report_id=? AND source_ip=? AND count=?' } sub select_todo_query { return <<'EO_TODO_QUERY' SELECT r.id AS rid, r.begin AS begin, r.end AS end, a.org_name AS author, fd.domain AS from_domain FROM report r LEFT JOIN report_record rr ON r.id=rr.report_id LEFT JOIN author a ON r.author_id=a.id LEFT JOIN domain fd ON r.from_domain_id=fd.id WHERE rr.count IS NULL AND rr.report_id IS NOT NULL AND r.end < ? GROUP BY r.id ORDER BY r.id ASC EO_TODO_QUERY ; } sub select_row_spf { return <<"EO_SPF_ROW" SELECT d.domain AS domain, s.result AS result, s.scope AS scope FROM report_record_spf s LEFT JOIN domain d ON s.domain_id=d.id WHERE s.report_record_id=? EO_SPF_ROW ; } sub select_row_dkim { return <<"EO_DKIM_ROW" SELECT d.domain AS domain, k.selector AS selector, k.result AS result, k.human_result AS human_result FROM report_record_dkim k LEFT JOIN domain d ON k.domain_id=d.id WHERE report_record_id=? EO_DKIM_ROW ; } sub select_row_reason { return <<"EO_ROW_QUERY" SELECT type,comment FROM report_record_reason WHERE report_record_id=? EO_ROW_QUERY ; } sub select_rr_query { return <<'EO_ROW_QUERY' SELECT rr.*, etd.domain AS envelope_to, efd.domain AS envelope_from, hfd.domain AS header_from FROM report_record rr LEFT JOIN domain etd ON etd.id=rr.envelope_to_did LEFT JOIN domain efd ON efd.id=rr.envelope_from_did LEFT JOIN domain hfd ON hfd.id=rr.header_from_did WHERE report_id = ? ORDER BY id ASC EO_ROW_QUERY ; } sub select_report_query { return <<'EO_REPORTS' SELECT r.id AS rid, r.uuid, r.begin AS begin, r.end AS end, a.org_name AS author, fd.domain AS from_domain FROM report r LEFT JOIN author a ON r.author_id=a.id LEFT JOIN domain fd ON r.from_domain_id=fd.id WHERE 1=1 EO_REPORTS ; } sub insert_error { my ( $self, $which ) = @_; if ( $which == 0 ) { return 'UPDATE report SET end=? WHERE id=?'; } else { return 'INSERT INTO report_error (report_id, error) VALUES (?,?)'; } } sub insert_rr_reason { return 'INSERT INTO report_record_reason (report_record_id, type, comment) VALUES (?,?,?)' } sub insert_rr_dkim { my ( $self, $fields ) = @_; my $fields_str = join ', ', @$fields; return <<"EO_DKIM" INSERT INTO report_record_dkim (report_record_id, $fields_str) VALUES (??) EO_DKIM ; } sub insert_rr_spf { my ( $self, $fields ) = @_; my $fields_str = join ', ', @$fields; return "INSERT INTO report_record_spf (report_record_id, $fields_str) VALUES(??)"; } sub insert_rr { return <<'EO_ROW_INSERT' INSERT INTO report_record (report_id, source_ip, count, header_from_did, envelope_to_did, envelope_from_did, disposition, dkim, spf) VALUES (??) EO_ROW_INSERT ; } sub insert_policy_published { return <<"EO_RPP" INSERT INTO report_policy_published (report_id, adkim, aspf, p, sp, pct, rua) VALUES (??) EO_RPP ; } sub select_from { my ($self, $columns, $table) = @_; my $colStr = join( ', ', @$columns ); return "SELECT $colStr FROM $table WHERE 1=1"; } sub insert_into { my ($self, $table, $cols) = @_; my $columns = join ', ', @$cols; return "INSERT INTO $table ($columns) VALUES (??)"; } sub update { my ($self, $table, $cols) = @_; my $columns = join( ' = ?, ') . ' = ?'; return "UPDATE $table SET $columns WHERE 1=1"; } sub replace_into { my ($self, $table, $cols) = @_; my $columns = join ', ', @$cols; return "REPLACE INTO $table ($columns) VALUES (??)"; } sub delete_from { my ($self, $table) = @_; return "DELETE FROM $table WHERE 1=1"; } 1; __END__ =pod =head1 NAME Mail::DMARC::Report::Store::SQL::Grammars::SQLite - Grammar for working with sqlite databases. =head1 VERSION version 1.20240314 =head1 SYPNOSIS Allow DMARC to be able to speak to SQLite databases. =head1 DESCRIPTION Uses ANSI SQL syntax, keeping the SQL as portable as possible. DB engine specific features are to be avoided. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Result000755000765000024 014574361234 16616 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Result/Reason.pm000444000765000024 265114574361234 20544 0ustar00mattstaff000000000000package Mail::DMARC::Result::Reason; our $VERSION = '1.20240314'; use strict; use warnings; use Carp; sub new { my ( $class, @args ) = @_; croak "invalid arguments" if @args % 2; my %args = @args; my $self = bless {}, $class; foreach my $key ( keys %args ) { $self->$key( $args{$key} ); } return $self; } sub type { return $_[0]->{type} if 1 == scalar @_; croak "invalid type" if 0 == grep {/^$_[1]$/ix} qw/ forwarded sampled_out trusted_forwarder mailing_list local_policy other /; return $_[0]->{type} = $_[1]; } sub comment { return $_[0]->{comment} if 1 == scalar @_; # comment is optional and requires no validation return $_[0]->{comment} = $_[1]; } 1; __END__ =pod =head1 NAME Mail::DMARC::Result::Reason - policy override reason =head1 VERSION version 1.20240314 =head1 METHODS =head2 type Type is the type of override used, and is one of a number of fixed strings. =head2 comment Comment may or may not be present, and may be anything. =head1 AUTHORS =over 4 =item * Matt Simerson =item * Davide Migliavacca =item * Marc Bradshaw =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2024 by Matt Simerson. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Mail-DMARC-1.20240314/lib/Mail/DMARC/Test000755000765000024 014574361234 16257 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/lib/Mail/DMARC/Test/Transport.pm000444000765000024 73014574361234 20726 0ustar00mattstaff000000000000package Mail::DMARC::Test::Transport; # VERSION use strict; use warnings; use Email::Sender::Transport::Test; sub new { my $class = shift; my $self = {}; return bless $self, $class; }; { my $global_transport = Email::Sender::Transport::Test->new; sub get_test_transport { return $global_transport; } } sub get_transports_for { my ( $self,$args ) = @_; my @transports; push @transports, $self->get_test_transport; return @transports; } 1; Mail-DMARC-1.20240314/share000755000765000024 014574361234 14164 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/share/dmarc_whitelist000444000765000024 171614574361234 17433 0ustar00mattstaff000000000000# Format: IP *whitespace* type *whitespace* comment # # Reason types: forwarded sampled_out trusted_forwarder mailing_list local_policy other # any IP without a type specified will default to 'other' # # Comment is a free form text entry 127.0.0.3 trusted_forwarder Test Comment 127.0.0.1 local_policy 140.211.11.3 mailing_list apache.org 141.42.206.35 mailing_list charite.de 146.112.225.21 mailing_list phishtank.com 168.100.1.1 mailing_list cloud9.net 168.100.1.3 mailing_list cloud9.net 168.100.1.4 mailing_list cloud9.net 168.100.1.7 mailing_list cloud9.net 2604:8d00:0:1::3 mailing_list cloud9.net 2604:8d00:0:1::4 mailing_list cloud9.net 2604:8d00:0:1::7 mailing_list cloud9.net 198.148.79.53 mailing_list clamav.net 208.69.40.157 mailing_list blackops.org 95.128.36.21 mailing_list kolabsys.com 95.128.36.22 mailing_list kolabsys.com 95.128.36.23 mailing_list kolabsys.com 199.185.178.25 mailing_list openbsd.org 199.247.13.58 mailing_list opensmtpd.org Mail-DMARC-1.20240314/share/mail-dmarc.cron000444000765000024 45714574361234 17200 0ustar00mattstaff000000000000# /etc/cron.d/dmarc_update_public_suffix_list: crontab entries for the Mail::DMARC package SHELL=/bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # Periodically (once a week) check for updates of the public suffix list 0 4 * * 0 root dmarc_update_public_suffix_list --random Mail-DMARC-1.20240314/share/mail-dmarc.ini000444000765000024 452614574361234 17037 0ustar00mattstaff000000000000; This is YOU. DMARC reports include information about the reports. Enter it here. [organization] domain = example.com org_name = My Great Company email = noreply@example.com extra_contact_info = http://www.example.com/dmarc-policy/ ; aggregate DMARC reports need to be stored somewhere. Any database ; with a DBI module (MySQL, SQLite, DBD, etc.) should work. ; SQLite, MySQL and Postgresql are supported. ; Default is sqlite. [report_store] backend = SQL dsn = dbi:SQLite:dbname=dmarc_reports.sqlite ;dsn = dbi:mysql:database=dmarc_report;host=db;port=3306 ;dsn = dbi:Pg:database=dmarc_report;port=5432 user = pass = ; when validating DMARC messages, reports are not saved by default. This ; enables 'save by default' auto_save = 0 ; Sign outgoing report emails with DKIM ; Options match those which would be passed ; to Mail::DKIM::Signer [report_sign] algorithm = rsa-sha1 method = relaxed domain = signer.example.com selector = dkim keyfile = /path/to/private.key [report_sending] ; minimum reporting interval in seconds: default: none ; min_interval = 3600 ; ; maximum reporting interval in seconds: default: none ; max_interval = 86400 ; backend can be perl or libopendmarc [dmarc] backend = perl [dns] timeout = 5 retrans = 5 public_suffix_list = share/public_suffix_list [smtp] ; hostname is the external FQDN of this MTA hostname = mail.example.com cc = set.this@for.a.while.example.com ; list IP addresses to whitelist (bypass DMARC reject/quarantine) ; see sample whitelist in share/dmarc_whitelist whitelist = /path/to/etc/dmarc_whitelist ; By default, we attempt to email directly to the report recipient. ; Set these to relay via a SMTP smart host. smarthost = smartuser = smartpass = ; Send error report emails, if set, we will send a simple report to ; any report handler when we were unable to send an aggregate report ; This currently covers errors where the report was too large to send. send_errors = 1 [imap] server = mail.example.com port = 993 user = pass = ; the imap folder where new dmarc messages will be found folder = dmarc ; the folders to store processed reports (a=aggregate, f=forensic) f_done = dmarc.forensic a_done = dmarc.aggregate [http] port = 8080 [https] port = 8443 ssl_crt = ssl_key = Mail-DMARC-1.20240314/share/mail_dmarc_schema.mysql000444000765000024 2432714574361234 21050 0ustar00mattstaff000000000000# ************************************************************ # Sequel Pro SQL dump # Version 4096 # # http://www.sequelpro.com/ # http://code.google.com/p/sequel-pro/ # # Host: 127.0.0.2 (MySQL 5.5.30) # Database: mail_dmarc # Generation Time: 2013-05-17 07:47:45 +0000 # ************************************************************ /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; # Dump of table author # ------------------------------------------------------------ DROP TABLE IF EXISTS `author`; CREATE TABLE `author` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `org_name` varchar(253) CHARACTER SET ascii NOT NULL DEFAULT '', `email` varchar(255) CHARACTER SET ascii DEFAULT NULL, `extra_contact` varchar(255) CHARACTER SET ascii DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; # Dump of table domain # ------------------------------------------------------------ DROP TABLE IF EXISTS `domain`; CREATE TABLE `domain` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `domain` varchar(253) CHARACTER SET ascii NOT NULL DEFAULT '', PRIMARY KEY (`id`), UNIQUE KEY `domain` (`domain`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `report_error`; CREATE TABLE `report_error` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `report_id` int(11) unsigned NOT NULL, `error` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '', `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `report_id` (`report_id`), CONSTRAINT `report_error_ibfk_1` FOREIGN KEY (`report_id`) REFERENCES `report` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; # Dump of table fk_disposition # ------------------------------------------------------------ DROP TABLE IF EXISTS `fk_disposition`; CREATE TABLE `fk_disposition` ( `disposition` varchar(10) NOT NULL DEFAULT '', PRIMARY KEY (`disposition`) ) ENGINE=InnoDB DEFAULT CHARSET=ascii; LOCK TABLES `fk_disposition` WRITE; /*!40000 ALTER TABLE `fk_disposition` DISABLE KEYS */; INSERT INTO `fk_disposition` (`disposition`) VALUES ('none'), ('quarantine'), ('reject'); /*!40000 ALTER TABLE `fk_disposition` ENABLE KEYS */; UNLOCK TABLES; # Dump of table fk_disposition_reason # ------------------------------------------------------------ DROP TABLE IF EXISTS `fk_disposition_reason`; CREATE TABLE `fk_disposition_reason` ( `type` varchar(24) NOT NULL DEFAULT '', PRIMARY KEY (`type`) ) ENGINE=InnoDB DEFAULT CHARSET=ascii; LOCK TABLES `fk_disposition_reason` WRITE; /*!40000 ALTER TABLE `fk_disposition_reason` DISABLE KEYS */; INSERT INTO `fk_disposition_reason` (`type`) VALUES ('forwarded'), ('local_policy'), ('mailing_list'), ('other'), ('sampled_out'), ('trusted_forwarder'); /*!40000 ALTER TABLE `fk_disposition_reason` ENABLE KEYS */; UNLOCK TABLES; # Dump of table fk_dkim_result # ------------------------------------------------------------ DROP TABLE IF EXISTS `fk_dkim_result`; CREATE TABLE `fk_dkim_result` ( `result` varchar(9) NOT NULL DEFAULT '', PRIMARY KEY (`result`) ) ENGINE=InnoDB DEFAULT CHARSET=ascii; LOCK TABLES `fk_dkim_result` WRITE; /*!40000 ALTER TABLE `fk_dkim_result` DISABLE KEYS */; INSERT INTO `fk_dkim_result` (`result`) VALUES ('fail'), ('neutral'), ('none'), ('pass'), ('permerror'), ('policy'), ('temperror'); /*!40000 ALTER TABLE `fk_dkim_result` ENABLE KEYS */; UNLOCK TABLES; # Dump of table fk_spf_result # ------------------------------------------------------------ DROP TABLE IF EXISTS `fk_spf_result`; CREATE TABLE `fk_spf_result` ( `result` varchar(9) NOT NULL DEFAULT '', PRIMARY KEY (`result`) ) ENGINE=InnoDB DEFAULT CHARSET=ascii; LOCK TABLES `fk_spf_result` WRITE; /*!40000 ALTER TABLE `fk_spf_result` DISABLE KEYS */; INSERT INTO `fk_spf_result` (`result`) VALUES ('fail'), ('neutral'), ('none'), ('pass'), ('permerror'), ('softfail'), ('temperror'); /*!40000 ALTER TABLE `fk_spf_result` ENABLE KEYS */; UNLOCK TABLES; # Dump of table fk_spf_scope # ------------------------------------------------------------ DROP TABLE IF EXISTS `fk_spf_scope`; CREATE TABLE `fk_spf_scope` ( `scope` varchar(5) NOT NULL DEFAULT '', PRIMARY KEY (`scope`) ) ENGINE=InnoDB DEFAULT CHARSET=ascii; LOCK TABLES `fk_spf_scope` WRITE; /*!40000 ALTER TABLE `fk_spf_scope` DISABLE KEYS */; INSERT INTO `fk_spf_scope` (`scope`) VALUES ('helo'), ('mfrom'); /*!40000 ALTER TABLE `fk_spf_scope` ENABLE KEYS */; UNLOCK TABLES; # Dump of table report # ------------------------------------------------------------ DROP TABLE IF EXISTS `report`; CREATE TABLE `report` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `begin` int(11) unsigned NOT NULL, `end` int(11) unsigned NOT NULL, `author_id` int(11) unsigned NOT NULL, `rcpt_domain_id` int(11) unsigned DEFAULT NULL, `from_domain_id` int(11) unsigned NOT NULL, `uuid` varchar(253) DEFAULT NULL, PRIMARY KEY (`id`), KEY `author_id` (`author_id`), KEY `from_domain_id` (`from_domain_id`), CONSTRAINT `report_ibfk_3` FOREIGN KEY (`from_domain_id`) REFERENCES `domain` (`id`) ON UPDATE CASCADE, CONSTRAINT `report_ibfk_1` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`) ON DELETE NO ACTION ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=ascii; # Dump of table report_policy_published # ------------------------------------------------------------ DROP TABLE IF EXISTS `report_policy_published`; CREATE TABLE `report_policy_published` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `report_id` int(11) unsigned NOT NULL, `adkim` char(1) DEFAULT NULL, `aspf` char(1) DEFAULT NULL, `p` varchar(10) DEFAULT NULL, `sp` varchar(10) DEFAULT NULL, `pct` tinyint(1) unsigned DEFAULT NULL, `rua` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `report_id` (`report_id`), CONSTRAINT `report_policy_published_ibfk_1` FOREIGN KEY (`report_id`) REFERENCES `report` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=ascii; # Dump of table report_record # ------------------------------------------------------------ DROP TABLE IF EXISTS `report_record`; CREATE TABLE `report_record` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `report_id` int(11) unsigned NOT NULL, `source_ip` varbinary(16) NOT NULL DEFAULT '', `count` int(11) unsigned DEFAULT NULL, `disposition` varchar(10) NOT NULL DEFAULT '', `dkim` char(4) NOT NULL DEFAULT '', `spf` char(4) NOT NULL DEFAULT '', `envelope_to_did` int(11) unsigned DEFAULT NULL, `envelope_from_did` int(11) unsigned DEFAULT NULL, `header_from_did` int(11) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `report_id` (`report_id`), KEY `disposition` (`disposition`), CONSTRAINT `report_record_ibfk_2` FOREIGN KEY (`disposition`) REFERENCES `fk_disposition` (`disposition`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `report_record_ibfk_1` FOREIGN KEY (`report_id`) REFERENCES `report` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=ascii; # Dump of table report_record_reason # ------------------------------------------------------------ DROP TABLE IF EXISTS `report_record_reason`; CREATE TABLE `report_record_reason` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `report_record_id` int(11) unsigned NOT NULL, `type` varchar(24) NOT NULL DEFAULT '', `comment` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL, PRIMARY KEY (`id`), KEY `report_record_id` (`report_record_id`), KEY `type` (`type`), CONSTRAINT `report_record_reason_ibfk_3` FOREIGN KEY (`report_record_id`) REFERENCES `report_record` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `report_record_reason_ibfk_4` FOREIGN KEY (`type`) REFERENCES `fk_disposition_reason` (`type`) ON DELETE NO ACTION ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=ascii; # Dump of table report_record_dkim # ------------------------------------------------------------ DROP TABLE IF EXISTS `report_record_dkim`; CREATE TABLE `report_record_dkim` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `report_record_id` int(11) unsigned NOT NULL, `domain_id` int(11) unsigned NOT NULL, `selector` varchar(253) DEFAULT NULL, `result` varchar(9) NOT NULL DEFAULT '', `human_result` varchar(64) DEFAULT NULL, PRIMARY KEY (`id`), KEY `report_record_id` (`report_record_id`), KEY `result` (`result`), CONSTRAINT `report_record_dkim_ibfk_2` FOREIGN KEY (`result`) REFERENCES `fk_dkim_result` (`result`), CONSTRAINT `report_record_dkim_ibfk_1` FOREIGN KEY (`report_record_id`) REFERENCES `report_record` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=ascii; # Dump of table report_record_spf # ------------------------------------------------------------ DROP TABLE IF EXISTS `report_record_spf`; CREATE TABLE `report_record_spf` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `report_record_id` int(11) unsigned NOT NULL, `domain_id` int(11) unsigned NOT NULL, `scope` varchar(5) DEFAULT NULL, `result` varchar(9) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `report_record_id` (`report_record_id`), KEY `scope` (`scope`), KEY `result` (`result`), CONSTRAINT `report_record_spf_ibfk_1` FOREIGN KEY (`report_record_id`) REFERENCES `report_record` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `report_record_spf_ibfk_2` FOREIGN KEY (`scope`) REFERENCES `fk_spf_scope` (`scope`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `report_record_spf_ibfk_3` FOREIGN KEY (`result`) REFERENCES `fk_spf_result` (`result`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=ascii; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; Mail-DMARC-1.20240314/share/mail_dmarc_schema.pgsql000444000765000024 730314574361234 21004 0ustar00mattstaff000000000000-- $Id$ -- Dump of table author -- ------------------------------------------------------------ DROP TABLE IF EXISTS author CASCADE; CREATE TABLE author ( id serial unique, org_name varchar(253) NOT NULL DEFAULT '', email varchar(255) DEFAULT NULL, extra_contact varchar(255) DEFAULT NULL ); -- Dump of table domain -- ------------------------------------------------------------ DROP TABLE IF EXISTS domain CASCADE; CREATE TABLE domain ( id serial unique, domain varchar(253) NOT NULL DEFAULT '', UNIQUE (domain) ); -- Dump of table report -- ------------------------------------------------------------ DROP TABLE IF EXISTS report CASCADE; CREATE TABLE report ( id serial unique, "begin" int NOT NULL, "end" int NOT NULL, author_id int NOT NULL REFERENCES author (id) ON DELETE NO ACTION, rcpt_domain_id int DEFAULT NULL, from_domain_id int NOT NULL REFERENCES domain (id), uuid varchar(253) DEFAULT NULL ); DROP TABLE IF EXISTS report_error CASCADE; CREATE TABLE report_error ( id serial unique, report_id int REFERENCES report(id) ON DELETE CASCADE, error varchar(255) NOT NULL DEFAULT '', time timestamp NOT NULL DEFAULT now() ); -- Dump of table report_policy_published -- ------------------------------------------------------------ DROP TABLE IF EXISTS report_policy_published CASCADE; CREATE TABLE report_policy_published ( id serial unique, report_id int NOT NULL REFERENCES report (id) ON DELETE CASCADE, adkim varchar(1) DEFAULT NULL, aspf varchar(1) DEFAULT NULL, p varchar(10) DEFAULT NULL, sp varchar(10) DEFAULT NULL, pct int DEFAULT NULL, rua varchar(255) DEFAULT NULL ); -- Dump of table report_record -- ------------------------------------------------------------ DROP TABLE IF EXISTS report_record CASCADE; CREATE TABLE report_record ( id serial unique, report_id int NOT NULL REFERENCES report (id) ON DELETE CASCADE, source_ip INET NOT NULL, count int DEFAULT NULL, disposition varchar(10) NOT NULL, dkim varchar(4) NOT NULL DEFAULT '', spf varchar(4) NOT NULL DEFAULT '', envelope_to_did int DEFAULT NULL, envelope_from_did int DEFAULT NULL, header_from_did int NOT NULL ); DROP TABLE IF EXISTS report_record_reason CASCADE; CREATE TABLE report_record_reason ( id serial unique, report_record_id int NOT NULL REFERENCES report_record (id) ON DELETE CASCADE, type varchar(24) NOT NULL, comment varchar(255) DEFAULT NULL ); -- Dump of table report_record_dkim -- ------------------------------------------------------------ DROP TABLE IF EXISTS report_record_dkim CASCADE; CREATE TABLE report_record_dkim ( id serial unique, report_record_id int NOT NULL REFERENCES report_record (id) ON DELETE CASCADE, domain_id int NOT NULL, selector varchar(253) DEFAULT NULL, result varchar(9) NOT NULL DEFAULT '', human_result varchar(64) DEFAULT NULL ); -- Dump of table report_record_spf -- ------------------------------------------------------------ DROP TABLE IF EXISTS report_record_spf CASCADE; CREATE TABLE report_record_spf ( id serial unique, report_record_id int NOT NULL REFERENCES report_record (id) ON DELETE CASCADE, domain_id int NOT NULL, scope varchar(5) DEFAULT NULL, result varchar(9) NOT NULL ); -- Indexes -- ----------------------------------------------------------- CREATE INDEX report_record_spf_report_record_id_idx ON report_record_spf(report_record_id); CREATE INDEX report_record_dkim_report_record_id_idx ON report_record_dkim(report_record_id); CREATE INDEX report_record_report_id_idx ON report_record(report_id); CREATE INDEX report_record_reason_report_record_id_idx ON report_record_reason(report_record_id); CREATE INDEX report_policy_published_report_id_idx ON report_policy_published(report_id); Mail-DMARC-1.20240314/share/mail_dmarc_schema.sqlite000444000765000024 1175714574361234 21207 0ustar00mattstaff000000000000 DROP TABLE IF EXISTS `author`; CREATE TABLE `author` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `org_name` TEXT NOT NULL, `email` TEXT DEFAULT NULL, `extra_contact` TEXT DEFAULT NULL ); CREATE UNIQUE INDEX "org_name_idx" ON "author" ("org_name"); DROP TABLE IF EXISTS `domain`; CREATE TABLE `domain` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `domain` TEXT NOT NULL ); CREATE UNIQUE INDEX "domain_idx" ON "domain" ("domain"); DROP TABLE IF EXISTS `fk_disposition`; CREATE TABLE `fk_disposition` ( `disposition` TEXT NOT NULL, PRIMARY KEY (`disposition`) ); INSERT INTO "fk_disposition" VALUES ('none'); INSERT INTO "fk_disposition" VALUES ('quarantine'); INSERT INTO "fk_disposition" VALUES ('reject'); DROP TABLE IF EXISTS `fk_disposition_reason`; CREATE TABLE `fk_disposition_reason` ( `type` TEXT NOT NULL, PRIMARY KEY (`type`) ); INSERT INTO "fk_disposition_reason" VALUES ('forwarded'); INSERT INTO "fk_disposition_reason" VALUES ('local_policy'); INSERT INTO "fk_disposition_reason" VALUES ('mailing_list'); INSERT INTO "fk_disposition_reason" VALUES ('other'); INSERT INTO "fk_disposition_reason" VALUES ('sampled_out'); INSERT INTO "fk_disposition_reason" VALUES ('trusted_forwarder'); DROP TABLE IF EXISTS `fk_dkim_result`; CREATE TABLE `fk_dkim_result` ( `result` TEXT NOT NULL, PRIMARY KEY (`result`) ); INSERT INTO "fk_dkim_result" VALUES ('fail'); INSERT INTO "fk_dkim_result" VALUES ('neutral'); INSERT INTO "fk_dkim_result" VALUES ('none'); INSERT INTO "fk_dkim_result" VALUES ('pass'); INSERT INTO "fk_dkim_result" VALUES ('permerror'); INSERT INTO "fk_dkim_result" VALUES ('policy'); INSERT INTO "fk_dkim_result" VALUES ('temperror'); DROP TABLE IF EXISTS `fk_spf_result`; CREATE TABLE `fk_spf_result` ( `result` TEXT NOT NULL, PRIMARY KEY (`result`) ); INSERT INTO "fk_spf_result" VALUES ('fail'); INSERT INTO "fk_spf_result" VALUES ('neutral'); INSERT INTO "fk_spf_result" VALUES ('none'); INSERT INTO "fk_spf_result" VALUES ('pass'); INSERT INTO "fk_spf_result" VALUES ('permerror'); INSERT INTO "fk_spf_result" VALUES ('softfail'); INSERT INTO "fk_spf_result" VALUES ('temperror'); DROP TABLE IF EXISTS `fk_spf_scope`; CREATE TABLE `fk_spf_scope` ( `scope` TEXT NOT NULL, PRIMARY KEY (`scope`) ); INSERT INTO "fk_spf_scope" VALUES ('helo'); INSERT INTO "fk_spf_scope" VALUES ('mfrom'); DROP TABLE IF EXISTS `report`; CREATE TABLE `report` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `begin` INTEGER NOT NULL, `end` INTEGER NOT NULL, `author_id` INTEGER NOT NULL REFERENCES `author`(`id`) ON UPDATE CASCADE ON DELETE CASCADE, `rcpt_domain_id` INTEGER DEFAULT NULL, `from_domain_id` INTEGER NOT NULL REFERENCES `domain`(`id`) ON UPDATE CASCADE ON DELETE CASCADE, `uuid` TEXT DEFAULT NULL ); DROP TABLE IF EXISTS `report_error`; CREATE TABLE "report_error" ( `report_id` INTEGER NOT NULL REFERENCES "report"("id") ON UPDATE CASCADE ON DELETE CASCADE, `error` TEXT NOT NULL, `time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL ); DROP TABLE IF EXISTS `report_policy_published`; CREATE TABLE `report_policy_published` ( `report_id` INTEGER NOT NULL REFERENCES "report"("id") ON UPDATE CASCADE ON DELETE CASCADE, `adkim` TEXT DEFAULT NULL, `aspf` TEXT DEFAULT NULL, `p` TEXT DEFAULT NULL, `sp` TEXT DEFAULT NULL, `pct` INTEGER DEFAULT NULL, `rua` TEXT DEFAULT NULL ); DROP TABLE IF EXISTS `report_record`; CREATE TABLE `report_record` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT, `report_id` INTEGER NOT NULL REFERENCES "report"("id") ON UPDATE CASCADE ON DELETE CASCADE, `source_ip` varbinary(16) NOT NULL, `count` INTEGER DEFAULT NULL, `disposition` TEXT NOT NULL REFERENCES "fk_disposition"("disposition") ON UPDATE CASCADE ON DELETE NO ACTION, `dkim` TEXT DEFAULT NULL, `spf` TEXT DEFAULT NULL, `envelope_to_did` INTEGER DEFAULT NULL, `envelope_from_did` INTEGER DEFAULT NULL, `header_from_did` INTEGER NOT NULL ); DROP TABLE IF EXISTS `report_record_reason`; CREATE TABLE `report_record_reason` ( `report_record_id` INTEGER NOT NULL REFERENCES "report_record"("id") ON UPDATE CASCADE ON DELETE CASCADE, `type` TEXT NOT NULL REFERENCES "fk_disposition_reason"("type") ON UPDATE CASCADE ON DELETE CASCADE, `comment` TEXT DEFAULT NULL ); DROP TABLE IF EXISTS `report_record_dkim`; CREATE TABLE `report_record_dkim` ( `report_record_id` INTEGER NOT NULL REFERENCES "report_record"("id") ON UPDATE CASCADE ON DELETE CASCADE, `domain_id` INTEGER NOT NULL, `selector` TEXT DEFAULT NULL, `result` TEXT DEFAULT NULL REFERENCES "fk_dkim_result"("result") ON UPDATE CASCADE ON DELETE CASCADE, `human_result` TEXT DEFAULT NULL ); DROP TABLE IF EXISTS `report_record_spf`; CREATE TABLE `report_record_spf` ( `report_record_id` INTEGER NOT NULL REFERENCES "report_record"("id") ON UPDATE CASCADE ON DELETE CASCADE, `domain_id` INTEGER NOT NULL, `scope` TEXT DEFAULT NULL REFERENCES "fk_spf_scope"("scope") ON UPDATE CASCADE ON DELETE RESTRICT, `result` TEXT NOT NULL REFERENCES "fk_spf_result"("result") ON UPDATE CASCADE ON DELETE CASCADE ); Mail-DMARC-1.20240314/share/public_suffix_list000444000765000024 113250714574361234 20232 0ustar00mattstaff000000000000// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat, // rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported. // Instructions on pulling and using this list can be found at https://publicsuffix.org/list/. // ===BEGIN ICANN DOMAINS=== // ac : http://nic.ac/rules.htm ac com.ac edu.ac gov.ac net.ac mil.ac org.ac // ad : https://en.wikipedia.org/wiki/.ad ad nom.ad // ae : https://tdra.gov.ae/en/aeda/ae-policies ae co.ae net.ae org.ae sch.ae ac.ae gov.ae mil.ae // aero : see https://www.information.aero/index.php?id=66 aero accident-investigation.aero accident-prevention.aero aerobatic.aero aeroclub.aero aerodrome.aero agents.aero aircraft.aero airline.aero airport.aero air-surveillance.aero airtraffic.aero air-traffic-control.aero ambulance.aero amusement.aero association.aero author.aero ballooning.aero broker.aero caa.aero cargo.aero catering.aero certification.aero championship.aero charter.aero civilaviation.aero club.aero conference.aero consultant.aero consulting.aero control.aero council.aero crew.aero design.aero dgca.aero educator.aero emergency.aero engine.aero engineer.aero entertainment.aero equipment.aero exchange.aero express.aero federation.aero flight.aero fuel.aero gliding.aero government.aero groundhandling.aero group.aero hanggliding.aero homebuilt.aero insurance.aero journal.aero journalist.aero leasing.aero logistics.aero magazine.aero maintenance.aero media.aero microlight.aero modelling.aero navigation.aero parachuting.aero paragliding.aero passenger-association.aero pilot.aero press.aero production.aero recreation.aero repbody.aero res.aero research.aero rotorcraft.aero safety.aero scientist.aero services.aero show.aero skydiving.aero software.aero student.aero trader.aero trading.aero trainer.aero union.aero workinggroup.aero works.aero // af : http://www.nic.af/help.jsp af gov.af com.af org.af net.af edu.af // ag : http://www.nic.ag/prices.htm ag com.ag org.ag net.ag co.ag nom.ag // ai : http://nic.com.ai/ ai off.ai com.ai net.ai org.ai // al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31 al com.al edu.al gov.al mil.al net.al org.al // am : https://www.amnic.net/policy/en/Policy_EN.pdf am co.am com.am commune.am net.am org.am // ao : https://en.wikipedia.org/wiki/.ao // http://www.dns.ao/REGISTR.DOC ao ed.ao gv.ao og.ao co.ao pb.ao it.ao // aq : https://en.wikipedia.org/wiki/.aq aq // ar : https://nic.ar/es/nic-argentina/normativa ar bet.ar com.ar coop.ar edu.ar gob.ar gov.ar int.ar mil.ar musica.ar mutual.ar net.ar org.ar senasa.ar tur.ar // arpa : https://en.wikipedia.org/wiki/.arpa // Confirmed by registry 2008-06-18 arpa e164.arpa in-addr.arpa ip6.arpa iris.arpa uri.arpa urn.arpa // as : https://en.wikipedia.org/wiki/.as as gov.as // asia : https://en.wikipedia.org/wiki/.asia asia // at : https://en.wikipedia.org/wiki/.at // Confirmed by registry 2008-06-17 at ac.at co.at gv.at or.at sth.ac.at // au : https://en.wikipedia.org/wiki/.au // http://www.auda.org.au/ au // 2LDs com.au net.au org.au edu.au gov.au asn.au id.au // Historic 2LDs (closed to new registration, but sites still exist) info.au conf.au oz.au // CGDNs - http://www.cgdn.org.au/ act.au nsw.au nt.au qld.au sa.au tas.au vic.au wa.au // 3LDs act.edu.au catholic.edu.au // eq.edu.au - Removed at the request of the Queensland Department of Education nsw.edu.au nt.edu.au qld.edu.au sa.edu.au tas.edu.au vic.edu.au wa.edu.au // act.gov.au Bug 984824 - Removed at request of Greg Tankard // nsw.gov.au Bug 547985 - Removed at request of // nt.gov.au Bug 940478 - Removed at request of Greg Connors qld.gov.au sa.gov.au tas.gov.au vic.gov.au wa.gov.au // 4LDs // education.tas.edu.au - Removed at the request of the Department of Education Tasmania schools.nsw.edu.au // aw : https://en.wikipedia.org/wiki/.aw aw com.aw // ax : https://en.wikipedia.org/wiki/.ax ax // az : https://en.wikipedia.org/wiki/.az az com.az net.az int.az gov.az org.az edu.az info.az pp.az mil.az name.az pro.az biz.az // ba : http://nic.ba/users_data/files/pravilnik_o_registraciji.pdf ba com.ba edu.ba gov.ba mil.ba net.ba org.ba // bb : https://en.wikipedia.org/wiki/.bb bb biz.bb co.bb com.bb edu.bb gov.bb info.bb net.bb org.bb store.bb tv.bb // bd : https://en.wikipedia.org/wiki/.bd *.bd // be : https://en.wikipedia.org/wiki/.be // Confirmed by registry 2008-06-08 be ac.be // bf : https://en.wikipedia.org/wiki/.bf bf gov.bf // bg : https://en.wikipedia.org/wiki/.bg // https://www.register.bg/user/static/rules/en/index.html bg a.bg b.bg c.bg d.bg e.bg f.bg g.bg h.bg i.bg j.bg k.bg l.bg m.bg n.bg o.bg p.bg q.bg r.bg s.bg t.bg u.bg v.bg w.bg x.bg y.bg z.bg 0.bg 1.bg 2.bg 3.bg 4.bg 5.bg 6.bg 7.bg 8.bg 9.bg // bh : https://en.wikipedia.org/wiki/.bh bh com.bh edu.bh net.bh org.bh gov.bh // bi : https://en.wikipedia.org/wiki/.bi // http://whois.nic.bi/ bi co.bi com.bi edu.bi or.bi org.bi // biz : https://en.wikipedia.org/wiki/.biz biz // bj : https://nic.bj/bj-suffixes.txt // submitted by registry bj africa.bj agro.bj architectes.bj assur.bj avocats.bj co.bj com.bj eco.bj econo.bj edu.bj info.bj loisirs.bj money.bj net.bj org.bj ote.bj resto.bj restaurant.bj tourism.bj univ.bj // bm : http://www.bermudanic.bm/dnr-text.txt bm com.bm edu.bm gov.bm net.bm org.bm // bn : http://www.bnnic.bn/faqs bn com.bn edu.bn gov.bn net.bn org.bn // bo : https://nic.bo/delegacion2015.php#h-1.10 bo com.bo edu.bo gob.bo int.bo org.bo net.bo mil.bo tv.bo web.bo // Social Domains academia.bo agro.bo arte.bo blog.bo bolivia.bo ciencia.bo cooperativa.bo democracia.bo deporte.bo ecologia.bo economia.bo empresa.bo indigena.bo industria.bo info.bo medicina.bo movimiento.bo musica.bo natural.bo nombre.bo noticias.bo patria.bo politica.bo profesional.bo plurinacional.bo pueblo.bo revista.bo salud.bo tecnologia.bo tksat.bo transporte.bo wiki.bo // br : http://registro.br/dominio/categoria.html // Submitted by registry br 9guacu.br abc.br adm.br adv.br agr.br aju.br am.br anani.br aparecida.br app.br arq.br art.br ato.br b.br barueri.br belem.br bhz.br bib.br bio.br blog.br bmd.br boavista.br bsb.br campinagrande.br campinas.br caxias.br cim.br cng.br cnt.br com.br contagem.br coop.br coz.br cri.br cuiaba.br curitiba.br def.br des.br det.br dev.br ecn.br eco.br edu.br emp.br enf.br eng.br esp.br etc.br eti.br far.br feira.br flog.br floripa.br fm.br fnd.br fortal.br fot.br foz.br fst.br g12.br geo.br ggf.br goiania.br gov.br // gov.br 26 states + df https://en.wikipedia.org/wiki/States_of_Brazil ac.gov.br al.gov.br am.gov.br ap.gov.br ba.gov.br ce.gov.br df.gov.br es.gov.br go.gov.br ma.gov.br mg.gov.br ms.gov.br mt.gov.br pa.gov.br pb.gov.br pe.gov.br pi.gov.br pr.gov.br rj.gov.br rn.gov.br ro.gov.br rr.gov.br rs.gov.br sc.gov.br se.gov.br sp.gov.br to.gov.br gru.br imb.br ind.br inf.br jab.br jampa.br jdf.br joinville.br jor.br jus.br leg.br lel.br log.br londrina.br macapa.br maceio.br manaus.br maringa.br mat.br med.br mil.br morena.br mp.br mus.br natal.br net.br niteroi.br *.nom.br not.br ntr.br odo.br ong.br org.br osasco.br palmas.br poa.br ppg.br pro.br psc.br psi.br pvh.br qsl.br radio.br rec.br recife.br rep.br ribeirao.br rio.br riobranco.br riopreto.br salvador.br sampa.br santamaria.br santoandre.br saobernardo.br saogonca.br seg.br sjc.br slg.br slz.br sorocaba.br srv.br taxi.br tc.br tec.br teo.br the.br tmp.br trd.br tur.br tv.br udi.br vet.br vix.br vlog.br wiki.br zlg.br // bs : http://www.nic.bs/rules.html bs com.bs net.bs org.bs edu.bs gov.bs // bt : https://en.wikipedia.org/wiki/.bt bt com.bt edu.bt gov.bt net.bt org.bt // bv : No registrations at this time. // Submitted by registry bv // bw : https://en.wikipedia.org/wiki/.bw // http://www.gobin.info/domainname/bw.doc // list of other 2nd level tlds ? bw co.bw org.bw // by : https://en.wikipedia.org/wiki/.by // http://tld.by/rules_2006_en.html // list of other 2nd level tlds ? by gov.by mil.by // Official information does not indicate that com.by is a reserved // second-level domain, but it's being used as one (see www.google.com.by and // www.yahoo.com.by, for example), so we list it here for safety's sake. com.by // http://hoster.by/ of.by // bz : https://en.wikipedia.org/wiki/.bz // http://www.belizenic.bz/ bz com.bz net.bz org.bz edu.bz gov.bz // ca : https://en.wikipedia.org/wiki/.ca ca // ca geographical names ab.ca bc.ca mb.ca nb.ca nf.ca nl.ca ns.ca nt.ca nu.ca on.ca pe.ca qc.ca sk.ca yk.ca // gc.ca: https://en.wikipedia.org/wiki/.gc.ca // see also: http://registry.gc.ca/en/SubdomainFAQ gc.ca // cat : https://en.wikipedia.org/wiki/.cat cat // cc : https://en.wikipedia.org/wiki/.cc cc // cd : https://en.wikipedia.org/wiki/.cd // see also: https://www.nic.cd/domain/insertDomain_2.jsp?act=1 cd gov.cd // cf : https://en.wikipedia.org/wiki/.cf cf // cg : https://en.wikipedia.org/wiki/.cg cg // ch : https://en.wikipedia.org/wiki/.ch ch // ci : https://en.wikipedia.org/wiki/.ci // http://www.nic.ci/index.php?page=charte ci org.ci or.ci com.ci co.ci edu.ci ed.ci ac.ci net.ci go.ci asso.ci aéroport.ci int.ci presse.ci md.ci gouv.ci // ck : https://en.wikipedia.org/wiki/.ck *.ck !www.ck // cl : https://www.nic.cl // Confirmed by .CL registry cl co.cl gob.cl gov.cl mil.cl // cm : https://en.wikipedia.org/wiki/.cm plus bug 981927 cm co.cm com.cm gov.cm net.cm // cn : https://en.wikipedia.org/wiki/.cn // Submitted by registry cn ac.cn com.cn edu.cn gov.cn net.cn org.cn mil.cn 公司.cn 网络.cn 網絡.cn // cn geographic names ah.cn bj.cn cq.cn fj.cn gd.cn gs.cn gz.cn gx.cn ha.cn hb.cn he.cn hi.cn hl.cn hn.cn jl.cn js.cn jx.cn ln.cn nm.cn nx.cn qh.cn sc.cn sd.cn sh.cn sn.cn sx.cn tj.cn xj.cn xz.cn yn.cn zj.cn hk.cn mo.cn tw.cn // co : https://en.wikipedia.org/wiki/.co // Submitted by registry co arts.co com.co edu.co firm.co gov.co info.co int.co mil.co net.co nom.co org.co rec.co web.co // com : https://en.wikipedia.org/wiki/.com com // coop : https://en.wikipedia.org/wiki/.coop coop // cr : http://www.nic.cr/niccr_publico/showRegistroDominiosScreen.do cr ac.cr co.cr ed.cr fi.cr go.cr or.cr sa.cr // cu : https://en.wikipedia.org/wiki/.cu cu com.cu edu.cu org.cu net.cu gov.cu inf.cu // cv : https://en.wikipedia.org/wiki/.cv // cv : http://www.dns.cv/tldcv_portal/do?com=DS;5446457100;111;+PAGE(4000018)+K-CAT-CODIGO(RDOM)+RCNT(100); <- registration rules cv com.cv edu.cv int.cv nome.cv org.cv // cw : http://www.una.cw/cw_registry/ // Confirmed by registry 2013-03-26 cw com.cw edu.cw net.cw org.cw // cx : https://en.wikipedia.org/wiki/.cx // list of other 2nd level tlds ? cx gov.cx // cy : http://www.nic.cy/ // Submitted by registry Panayiotou Fotia // namespace policies URL https://www.nic.cy/portal//sites/default/files/symfonia_gia_eggrafi.pdf cy ac.cy biz.cy com.cy ekloges.cy gov.cy ltd.cy mil.cy net.cy org.cy press.cy pro.cy tm.cy // cz : https://en.wikipedia.org/wiki/.cz cz // de : https://en.wikipedia.org/wiki/.de // Confirmed by registry (with technical // reservations) 2008-07-01 de // dj : https://en.wikipedia.org/wiki/.dj dj // dk : https://en.wikipedia.org/wiki/.dk // Confirmed by registry 2008-06-17 dk // dm : https://en.wikipedia.org/wiki/.dm dm com.dm net.dm org.dm edu.dm gov.dm // do : https://en.wikipedia.org/wiki/.do do art.do com.do edu.do gob.do gov.do mil.do net.do org.do sld.do web.do // dz : http://www.nic.dz/images/pdf_nic/charte.pdf dz art.dz asso.dz com.dz edu.dz gov.dz org.dz net.dz pol.dz soc.dz tm.dz // ec : http://www.nic.ec/reg/paso1.asp // Submitted by registry ec com.ec info.ec net.ec fin.ec k12.ec med.ec pro.ec org.ec edu.ec gov.ec gob.ec mil.ec // edu : https://en.wikipedia.org/wiki/.edu edu // ee : http://www.eenet.ee/EENet/dom_reeglid.html#lisa_B ee edu.ee gov.ee riik.ee lib.ee med.ee com.ee pri.ee aip.ee org.ee fie.ee // eg : https://en.wikipedia.org/wiki/.eg eg com.eg edu.eg eun.eg gov.eg mil.eg name.eg net.eg org.eg sci.eg // er : https://en.wikipedia.org/wiki/.er *.er // es : https://www.nic.es/site_ingles/ingles/dominios/index.html es com.es nom.es org.es gob.es edu.es // et : https://en.wikipedia.org/wiki/.et et com.et gov.et org.et edu.et biz.et name.et info.et net.et // eu : https://en.wikipedia.org/wiki/.eu eu // fi : https://en.wikipedia.org/wiki/.fi fi // aland.fi : https://en.wikipedia.org/wiki/.ax // This domain is being phased out in favor of .ax. As there are still many // domains under aland.fi, we still keep it on the list until aland.fi is // completely removed. // TODO: Check for updates (expected to be phased out around Q1/2009) aland.fi // fj : http://domains.fj/ // Submitted by registry 2020-02-11 fj ac.fj biz.fj com.fj gov.fj info.fj mil.fj name.fj net.fj org.fj pro.fj // fk : https://en.wikipedia.org/wiki/.fk *.fk // fm : https://en.wikipedia.org/wiki/.fm com.fm edu.fm net.fm org.fm fm // fo : https://en.wikipedia.org/wiki/.fo fo // fr : https://www.afnic.fr/ https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf fr asso.fr com.fr gouv.fr nom.fr prd.fr tm.fr // Other SLDs now selfmanaged out of AFNIC range. Former "domaines sectoriels", still registration suffixes avoues.fr cci.fr greta.fr huissier-justice.fr // ga : https://en.wikipedia.org/wiki/.ga ga // gb : This registry is effectively dormant // Submitted by registry gb // gd : https://en.wikipedia.org/wiki/.gd edu.gd gov.gd gd // ge : http://www.nic.net.ge/policy_en.pdf ge com.ge edu.ge gov.ge org.ge mil.ge net.ge pvt.ge // gf : https://en.wikipedia.org/wiki/.gf gf // gg : http://www.channelisles.net/register-domains/ // Confirmed by registry 2013-11-28 gg co.gg net.gg org.gg // gh : https://en.wikipedia.org/wiki/.gh // see also: http://www.nic.gh/reg_now.php // Although domains directly at second level are not possible at the moment, // they have been possible for some time and may come back. gh com.gh edu.gh gov.gh org.gh mil.gh // gi : http://www.nic.gi/rules.html gi com.gi ltd.gi gov.gi mod.gi edu.gi org.gi // gl : https://en.wikipedia.org/wiki/.gl // http://nic.gl gl co.gl com.gl edu.gl net.gl org.gl // gm : http://www.nic.gm/htmlpages%5Cgm-policy.htm gm // gn : http://psg.com/dns/gn/gn.txt // Submitted by registry gn ac.gn com.gn edu.gn gov.gn org.gn net.gn // gov : https://en.wikipedia.org/wiki/.gov gov // gp : http://www.nic.gp/index.php?lang=en gp com.gp net.gp mobi.gp edu.gp org.gp asso.gp // gq : https://en.wikipedia.org/wiki/.gq gq // gr : https://grweb.ics.forth.gr/english/1617-B-2005.html // Submitted by registry gr com.gr edu.gr net.gr org.gr gov.gr // gs : https://en.wikipedia.org/wiki/.gs gs // gt : https://www.gt/sitio/registration_policy.php?lang=en gt com.gt edu.gt gob.gt ind.gt mil.gt net.gt org.gt // gu : http://gadao.gov.gu/register.html // University of Guam : https://www.uog.edu // Submitted by uognoc@triton.uog.edu gu com.gu edu.gu gov.gu guam.gu info.gu net.gu org.gu web.gu // gw : https://en.wikipedia.org/wiki/.gw // gw : https://nic.gw/regras/ gw // gy : https://en.wikipedia.org/wiki/.gy // http://registry.gy/ gy co.gy com.gy edu.gy gov.gy net.gy org.gy // hk : https://www.hkirc.hk // Submitted by registry hk com.hk edu.hk gov.hk idv.hk net.hk org.hk 公司.hk 教育.hk 敎育.hk 政府.hk 個人.hk 个人.hk 箇人.hk 網络.hk 网络.hk 组織.hk 網絡.hk 网絡.hk 组织.hk 組織.hk 組织.hk // hm : https://en.wikipedia.org/wiki/.hm hm // hn : http://www.nic.hn/politicas/ps02,,05.html hn com.hn edu.hn org.hn net.hn mil.hn gob.hn // hr : http://www.dns.hr/documents/pdf/HRTLD-regulations.pdf hr iz.hr from.hr name.hr com.hr // ht : http://www.nic.ht/info/charte.cfm ht com.ht shop.ht firm.ht info.ht adult.ht net.ht pro.ht org.ht med.ht art.ht coop.ht pol.ht asso.ht edu.ht rel.ht gouv.ht perso.ht // hu : http://www.domain.hu/domain/English/sld.html // Confirmed by registry 2008-06-12 hu co.hu info.hu org.hu priv.hu sport.hu tm.hu 2000.hu agrar.hu bolt.hu casino.hu city.hu erotica.hu erotika.hu film.hu forum.hu games.hu hotel.hu ingatlan.hu jogasz.hu konyvelo.hu lakas.hu media.hu news.hu reklam.hu sex.hu shop.hu suli.hu szex.hu tozsde.hu utazas.hu video.hu // id : https://pandi.id/en/domain/registration-requirements/ id ac.id biz.id co.id desa.id go.id mil.id my.id net.id or.id ponpes.id sch.id web.id // ie : https://en.wikipedia.org/wiki/.ie ie gov.ie // il : http://www.isoc.org.il/domains/ // see also: https://en.isoc.org.il/il-cctld/registration-rules // ISOC-IL (operated by .il Registry) il ac.il co.il gov.il idf.il k12.il muni.il net.il org.il // xn--4dbrk0ce ("Israel", Hebrew) : IL ישראל // xn--4dbgdty6c.xn--4dbrk0ce. אקדמיה.ישראל // xn--5dbhl8d.xn--4dbrk0ce. ישוב.ישראל // xn--8dbq2a.xn--4dbrk0ce. צהל.ישראל // xn--hebda8b.xn--4dbrk0ce. ממשל.ישראל // im : https://www.nic.im/ // Submitted by registry im ac.im co.im com.im ltd.co.im net.im org.im plc.co.im tt.im tv.im // in : https://en.wikipedia.org/wiki/.in // see also: https://registry.in/policies // Please note, that nic.in is not an official eTLD, but used by most // government institutions. in 5g.in 6g.in ac.in ai.in am.in bihar.in biz.in business.in ca.in cn.in co.in com.in coop.in cs.in delhi.in dr.in edu.in er.in firm.in gen.in gov.in gujarat.in ind.in info.in int.in internet.in io.in me.in mil.in net.in nic.in org.in pg.in post.in pro.in res.in travel.in tv.in uk.in up.in us.in // info : https://en.wikipedia.org/wiki/.info info // int : https://en.wikipedia.org/wiki/.int // Confirmed by registry 2008-06-18 int eu.int // io : http://www.nic.io/rules.htm // list of other 2nd level tlds ? io com.io // iq : http://www.cmc.iq/english/iq/iqregister1.htm iq gov.iq edu.iq mil.iq com.iq org.iq net.iq // ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules // Also see http://www.nic.ir/Internationalized_Domain_Names // Two .ir entries added at request of , 2010-04-16 ir ac.ir co.ir gov.ir id.ir net.ir org.ir sch.ir // xn--mgba3a4f16a.ir (.ir, Persian YEH) ایران.ir // xn--mgba3a4fra.ir (.ir, Arabic YEH) ايران.ir // is : http://www.isnic.is/domain/rules.php // Confirmed by registry 2008-12-06 is net.is com.is edu.is gov.is org.is int.is // it : https://en.wikipedia.org/wiki/.it it gov.it edu.it // Reserved geo-names (regions and provinces): // https://www.nic.it/sites/default/files/archivio/docs/Regulation_assignation_v7.1.pdf // Regions abr.it abruzzo.it aosta-valley.it aostavalley.it bas.it basilicata.it cal.it calabria.it cam.it campania.it emilia-romagna.it emiliaromagna.it emr.it friuli-v-giulia.it friuli-ve-giulia.it friuli-vegiulia.it friuli-venezia-giulia.it friuli-veneziagiulia.it friuli-vgiulia.it friuliv-giulia.it friulive-giulia.it friulivegiulia.it friulivenezia-giulia.it friuliveneziagiulia.it friulivgiulia.it fvg.it laz.it lazio.it lig.it liguria.it lom.it lombardia.it lombardy.it lucania.it mar.it marche.it mol.it molise.it piedmont.it piemonte.it pmn.it pug.it puglia.it sar.it sardegna.it sardinia.it sic.it sicilia.it sicily.it taa.it tos.it toscana.it trentin-sud-tirol.it trentin-süd-tirol.it trentin-sudtirol.it trentin-südtirol.it trentin-sued-tirol.it trentin-suedtirol.it trentino-a-adige.it trentino-aadige.it trentino-alto-adige.it trentino-altoadige.it trentino-s-tirol.it trentino-stirol.it trentino-sud-tirol.it trentino-süd-tirol.it trentino-sudtirol.it trentino-südtirol.it trentino-sued-tirol.it trentino-suedtirol.it trentino.it trentinoa-adige.it trentinoaadige.it trentinoalto-adige.it trentinoaltoadige.it trentinos-tirol.it trentinostirol.it trentinosud-tirol.it trentinosüd-tirol.it trentinosudtirol.it trentinosüdtirol.it trentinosued-tirol.it trentinosuedtirol.it trentinsud-tirol.it trentinsüd-tirol.it trentinsudtirol.it trentinsüdtirol.it trentinsued-tirol.it trentinsuedtirol.it tuscany.it umb.it umbria.it val-d-aosta.it val-daosta.it vald-aosta.it valdaosta.it valle-aosta.it valle-d-aosta.it valle-daosta.it valleaosta.it valled-aosta.it valledaosta.it vallee-aoste.it vallée-aoste.it vallee-d-aoste.it vallée-d-aoste.it valleeaoste.it valléeaoste.it valleedaoste.it valléedaoste.it vao.it vda.it ven.it veneto.it // Provinces ag.it agrigento.it al.it alessandria.it alto-adige.it altoadige.it an.it ancona.it andria-barletta-trani.it andria-trani-barletta.it andriabarlettatrani.it andriatranibarletta.it ao.it aosta.it aoste.it ap.it aq.it aquila.it ar.it arezzo.it ascoli-piceno.it ascolipiceno.it asti.it at.it av.it avellino.it ba.it balsan-sudtirol.it balsan-südtirol.it balsan-suedtirol.it balsan.it bari.it barletta-trani-andria.it barlettatraniandria.it belluno.it benevento.it bergamo.it bg.it bi.it biella.it bl.it bn.it bo.it bologna.it bolzano-altoadige.it bolzano.it bozen-sudtirol.it bozen-südtirol.it bozen-suedtirol.it bozen.it br.it brescia.it brindisi.it bs.it bt.it bulsan-sudtirol.it bulsan-südtirol.it bulsan-suedtirol.it bulsan.it bz.it ca.it cagliari.it caltanissetta.it campidano-medio.it campidanomedio.it campobasso.it carbonia-iglesias.it carboniaiglesias.it carrara-massa.it carraramassa.it caserta.it catania.it catanzaro.it cb.it ce.it cesena-forli.it cesena-forlì.it cesenaforli.it cesenaforlì.it ch.it chieti.it ci.it cl.it cn.it co.it como.it cosenza.it cr.it cremona.it crotone.it cs.it ct.it cuneo.it cz.it dell-ogliastra.it dellogliastra.it en.it enna.it fc.it fe.it fermo.it ferrara.it fg.it fi.it firenze.it florence.it fm.it foggia.it forli-cesena.it forlì-cesena.it forlicesena.it forlìcesena.it fr.it frosinone.it ge.it genoa.it genova.it go.it gorizia.it gr.it grosseto.it iglesias-carbonia.it iglesiascarbonia.it im.it imperia.it is.it isernia.it kr.it la-spezia.it laquila.it laspezia.it latina.it lc.it le.it lecce.it lecco.it li.it livorno.it lo.it lodi.it lt.it lu.it lucca.it macerata.it mantova.it massa-carrara.it massacarrara.it matera.it mb.it mc.it me.it medio-campidano.it mediocampidano.it messina.it mi.it milan.it milano.it mn.it mo.it modena.it monza-brianza.it monza-e-della-brianza.it monza.it monzabrianza.it monzaebrianza.it monzaedellabrianza.it ms.it mt.it na.it naples.it napoli.it no.it novara.it nu.it nuoro.it og.it ogliastra.it olbia-tempio.it olbiatempio.it or.it oristano.it ot.it pa.it padova.it padua.it palermo.it parma.it pavia.it pc.it pd.it pe.it perugia.it pesaro-urbino.it pesarourbino.it pescara.it pg.it pi.it piacenza.it pisa.it pistoia.it pn.it po.it pordenone.it potenza.it pr.it prato.it pt.it pu.it pv.it pz.it ra.it ragusa.it ravenna.it rc.it re.it reggio-calabria.it reggio-emilia.it reggiocalabria.it reggioemilia.it rg.it ri.it rieti.it rimini.it rm.it rn.it ro.it roma.it rome.it rovigo.it sa.it salerno.it sassari.it savona.it si.it siena.it siracusa.it so.it sondrio.it sp.it sr.it ss.it suedtirol.it südtirol.it sv.it ta.it taranto.it te.it tempio-olbia.it tempioolbia.it teramo.it terni.it tn.it to.it torino.it tp.it tr.it trani-andria-barletta.it trani-barletta-andria.it traniandriabarletta.it tranibarlettaandria.it trapani.it trento.it treviso.it trieste.it ts.it turin.it tv.it ud.it udine.it urbino-pesaro.it urbinopesaro.it va.it varese.it vb.it vc.it ve.it venezia.it venice.it verbania.it vercelli.it verona.it vi.it vibo-valentia.it vibovalentia.it vicenza.it viterbo.it vr.it vs.it vt.it vv.it // je : http://www.channelisles.net/register-domains/ // Confirmed by registry 2013-11-28 je co.je net.je org.je // jm : http://www.com.jm/register.html *.jm // jo : http://www.dns.jo/Registration_policy.aspx jo com.jo org.jo net.jo edu.jo sch.jo gov.jo mil.jo name.jo // jobs : https://en.wikipedia.org/wiki/.jobs jobs // jp : https://en.wikipedia.org/wiki/.jp // http://jprs.co.jp/en/jpdomain.html // Submitted by registry jp // jp organizational type names ac.jp ad.jp co.jp ed.jp go.jp gr.jp lg.jp ne.jp or.jp // jp prefecture type names aichi.jp akita.jp aomori.jp chiba.jp ehime.jp fukui.jp fukuoka.jp fukushima.jp gifu.jp gunma.jp hiroshima.jp hokkaido.jp hyogo.jp ibaraki.jp ishikawa.jp iwate.jp kagawa.jp kagoshima.jp kanagawa.jp kochi.jp kumamoto.jp kyoto.jp mie.jp miyagi.jp miyazaki.jp nagano.jp nagasaki.jp nara.jp niigata.jp oita.jp okayama.jp okinawa.jp osaka.jp saga.jp saitama.jp shiga.jp shimane.jp shizuoka.jp tochigi.jp tokushima.jp tokyo.jp tottori.jp toyama.jp wakayama.jp yamagata.jp yamaguchi.jp yamanashi.jp 栃木.jp 愛知.jp 愛媛.jp 兵庫.jp 熊本.jp 茨城.jp 北海道.jp 千葉.jp 和歌山.jp 長崎.jp 長野.jp 新潟.jp 青森.jp 静岡.jp 東京.jp 石川.jp 埼玉.jp 三重.jp 京都.jp 佐賀.jp 大分.jp 大阪.jp 奈良.jp 宮城.jp 宮崎.jp 富山.jp 山口.jp 山形.jp 山梨.jp 岩手.jp 岐阜.jp 岡山.jp 島根.jp 広島.jp 徳島.jp 沖縄.jp 滋賀.jp 神奈川.jp 福井.jp 福岡.jp 福島.jp 秋田.jp 群馬.jp 香川.jp 高知.jp 鳥取.jp 鹿児島.jp // jp geographic type names // http://jprs.jp/doc/rule/saisoku-1.html *.kawasaki.jp *.kitakyushu.jp *.kobe.jp *.nagoya.jp *.sapporo.jp *.sendai.jp *.yokohama.jp !city.kawasaki.jp !city.kitakyushu.jp !city.kobe.jp !city.nagoya.jp !city.sapporo.jp !city.sendai.jp !city.yokohama.jp // 4th level registration aisai.aichi.jp ama.aichi.jp anjo.aichi.jp asuke.aichi.jp chiryu.aichi.jp chita.aichi.jp fuso.aichi.jp gamagori.aichi.jp handa.aichi.jp hazu.aichi.jp hekinan.aichi.jp higashiura.aichi.jp ichinomiya.aichi.jp inazawa.aichi.jp inuyama.aichi.jp isshiki.aichi.jp iwakura.aichi.jp kanie.aichi.jp kariya.aichi.jp kasugai.aichi.jp kira.aichi.jp kiyosu.aichi.jp komaki.aichi.jp konan.aichi.jp kota.aichi.jp mihama.aichi.jp miyoshi.aichi.jp nishio.aichi.jp nisshin.aichi.jp obu.aichi.jp oguchi.aichi.jp oharu.aichi.jp okazaki.aichi.jp owariasahi.aichi.jp seto.aichi.jp shikatsu.aichi.jp shinshiro.aichi.jp shitara.aichi.jp tahara.aichi.jp takahama.aichi.jp tobishima.aichi.jp toei.aichi.jp togo.aichi.jp tokai.aichi.jp tokoname.aichi.jp toyoake.aichi.jp toyohashi.aichi.jp toyokawa.aichi.jp toyone.aichi.jp toyota.aichi.jp tsushima.aichi.jp yatomi.aichi.jp akita.akita.jp daisen.akita.jp fujisato.akita.jp gojome.akita.jp hachirogata.akita.jp happou.akita.jp higashinaruse.akita.jp honjo.akita.jp honjyo.akita.jp ikawa.akita.jp kamikoani.akita.jp kamioka.akita.jp katagami.akita.jp kazuno.akita.jp kitaakita.akita.jp kosaka.akita.jp kyowa.akita.jp misato.akita.jp mitane.akita.jp moriyoshi.akita.jp nikaho.akita.jp noshiro.akita.jp odate.akita.jp oga.akita.jp ogata.akita.jp semboku.akita.jp yokote.akita.jp yurihonjo.akita.jp aomori.aomori.jp gonohe.aomori.jp hachinohe.aomori.jp hashikami.aomori.jp hiranai.aomori.jp hirosaki.aomori.jp itayanagi.aomori.jp kuroishi.aomori.jp misawa.aomori.jp mutsu.aomori.jp nakadomari.aomori.jp noheji.aomori.jp oirase.aomori.jp owani.aomori.jp rokunohe.aomori.jp sannohe.aomori.jp shichinohe.aomori.jp shingo.aomori.jp takko.aomori.jp towada.aomori.jp tsugaru.aomori.jp tsuruta.aomori.jp abiko.chiba.jp asahi.chiba.jp chonan.chiba.jp chosei.chiba.jp choshi.chiba.jp chuo.chiba.jp funabashi.chiba.jp futtsu.chiba.jp hanamigawa.chiba.jp ichihara.chiba.jp ichikawa.chiba.jp ichinomiya.chiba.jp inzai.chiba.jp isumi.chiba.jp kamagaya.chiba.jp kamogawa.chiba.jp kashiwa.chiba.jp katori.chiba.jp katsuura.chiba.jp kimitsu.chiba.jp kisarazu.chiba.jp kozaki.chiba.jp kujukuri.chiba.jp kyonan.chiba.jp matsudo.chiba.jp midori.chiba.jp mihama.chiba.jp minamiboso.chiba.jp mobara.chiba.jp mutsuzawa.chiba.jp nagara.chiba.jp nagareyama.chiba.jp narashino.chiba.jp narita.chiba.jp noda.chiba.jp oamishirasato.chiba.jp omigawa.chiba.jp onjuku.chiba.jp otaki.chiba.jp sakae.chiba.jp sakura.chiba.jp shimofusa.chiba.jp shirako.chiba.jp shiroi.chiba.jp shisui.chiba.jp sodegaura.chiba.jp sosa.chiba.jp tako.chiba.jp tateyama.chiba.jp togane.chiba.jp tohnosho.chiba.jp tomisato.chiba.jp urayasu.chiba.jp yachimata.chiba.jp yachiyo.chiba.jp yokaichiba.chiba.jp yokoshibahikari.chiba.jp yotsukaido.chiba.jp ainan.ehime.jp honai.ehime.jp ikata.ehime.jp imabari.ehime.jp iyo.ehime.jp kamijima.ehime.jp kihoku.ehime.jp kumakogen.ehime.jp masaki.ehime.jp matsuno.ehime.jp matsuyama.ehime.jp namikata.ehime.jp niihama.ehime.jp ozu.ehime.jp saijo.ehime.jp seiyo.ehime.jp shikokuchuo.ehime.jp tobe.ehime.jp toon.ehime.jp uchiko.ehime.jp uwajima.ehime.jp yawatahama.ehime.jp echizen.fukui.jp eiheiji.fukui.jp fukui.fukui.jp ikeda.fukui.jp katsuyama.fukui.jp mihama.fukui.jp minamiechizen.fukui.jp obama.fukui.jp ohi.fukui.jp ono.fukui.jp sabae.fukui.jp sakai.fukui.jp takahama.fukui.jp tsuruga.fukui.jp wakasa.fukui.jp ashiya.fukuoka.jp buzen.fukuoka.jp chikugo.fukuoka.jp chikuho.fukuoka.jp chikujo.fukuoka.jp chikushino.fukuoka.jp chikuzen.fukuoka.jp chuo.fukuoka.jp dazaifu.fukuoka.jp fukuchi.fukuoka.jp hakata.fukuoka.jp higashi.fukuoka.jp hirokawa.fukuoka.jp hisayama.fukuoka.jp iizuka.fukuoka.jp inatsuki.fukuoka.jp kaho.fukuoka.jp kasuga.fukuoka.jp kasuya.fukuoka.jp kawara.fukuoka.jp keisen.fukuoka.jp koga.fukuoka.jp kurate.fukuoka.jp kurogi.fukuoka.jp kurume.fukuoka.jp minami.fukuoka.jp miyako.fukuoka.jp miyama.fukuoka.jp miyawaka.fukuoka.jp mizumaki.fukuoka.jp munakata.fukuoka.jp nakagawa.fukuoka.jp nakama.fukuoka.jp nishi.fukuoka.jp nogata.fukuoka.jp ogori.fukuoka.jp okagaki.fukuoka.jp okawa.fukuoka.jp oki.fukuoka.jp omuta.fukuoka.jp onga.fukuoka.jp onojo.fukuoka.jp oto.fukuoka.jp saigawa.fukuoka.jp sasaguri.fukuoka.jp shingu.fukuoka.jp shinyoshitomi.fukuoka.jp shonai.fukuoka.jp soeda.fukuoka.jp sue.fukuoka.jp tachiarai.fukuoka.jp tagawa.fukuoka.jp takata.fukuoka.jp toho.fukuoka.jp toyotsu.fukuoka.jp tsuiki.fukuoka.jp ukiha.fukuoka.jp umi.fukuoka.jp usui.fukuoka.jp yamada.fukuoka.jp yame.fukuoka.jp yanagawa.fukuoka.jp yukuhashi.fukuoka.jp aizubange.fukushima.jp aizumisato.fukushima.jp aizuwakamatsu.fukushima.jp asakawa.fukushima.jp bandai.fukushima.jp date.fukushima.jp fukushima.fukushima.jp furudono.fukushima.jp futaba.fukushima.jp hanawa.fukushima.jp higashi.fukushima.jp hirata.fukushima.jp hirono.fukushima.jp iitate.fukushima.jp inawashiro.fukushima.jp ishikawa.fukushima.jp iwaki.fukushima.jp izumizaki.fukushima.jp kagamiishi.fukushima.jp kaneyama.fukushima.jp kawamata.fukushima.jp kitakata.fukushima.jp kitashiobara.fukushima.jp koori.fukushima.jp koriyama.fukushima.jp kunimi.fukushima.jp miharu.fukushima.jp mishima.fukushima.jp namie.fukushima.jp nango.fukushima.jp nishiaizu.fukushima.jp nishigo.fukushima.jp okuma.fukushima.jp omotego.fukushima.jp ono.fukushima.jp otama.fukushima.jp samegawa.fukushima.jp shimogo.fukushima.jp shirakawa.fukushima.jp showa.fukushima.jp soma.fukushima.jp sukagawa.fukushima.jp taishin.fukushima.jp tamakawa.fukushima.jp tanagura.fukushima.jp tenei.fukushima.jp yabuki.fukushima.jp yamato.fukushima.jp yamatsuri.fukushima.jp yanaizu.fukushima.jp yugawa.fukushima.jp anpachi.gifu.jp ena.gifu.jp gifu.gifu.jp ginan.gifu.jp godo.gifu.jp gujo.gifu.jp hashima.gifu.jp hichiso.gifu.jp hida.gifu.jp higashishirakawa.gifu.jp ibigawa.gifu.jp ikeda.gifu.jp kakamigahara.gifu.jp kani.gifu.jp kasahara.gifu.jp kasamatsu.gifu.jp kawaue.gifu.jp kitagata.gifu.jp mino.gifu.jp minokamo.gifu.jp mitake.gifu.jp mizunami.gifu.jp motosu.gifu.jp nakatsugawa.gifu.jp ogaki.gifu.jp sakahogi.gifu.jp seki.gifu.jp sekigahara.gifu.jp shirakawa.gifu.jp tajimi.gifu.jp takayama.gifu.jp tarui.gifu.jp toki.gifu.jp tomika.gifu.jp wanouchi.gifu.jp yamagata.gifu.jp yaotsu.gifu.jp yoro.gifu.jp annaka.gunma.jp chiyoda.gunma.jp fujioka.gunma.jp higashiagatsuma.gunma.jp isesaki.gunma.jp itakura.gunma.jp kanna.gunma.jp kanra.gunma.jp katashina.gunma.jp kawaba.gunma.jp kiryu.gunma.jp kusatsu.gunma.jp maebashi.gunma.jp meiwa.gunma.jp midori.gunma.jp minakami.gunma.jp naganohara.gunma.jp nakanojo.gunma.jp nanmoku.gunma.jp numata.gunma.jp oizumi.gunma.jp ora.gunma.jp ota.gunma.jp shibukawa.gunma.jp shimonita.gunma.jp shinto.gunma.jp showa.gunma.jp takasaki.gunma.jp takayama.gunma.jp tamamura.gunma.jp tatebayashi.gunma.jp tomioka.gunma.jp tsukiyono.gunma.jp tsumagoi.gunma.jp ueno.gunma.jp yoshioka.gunma.jp asaminami.hiroshima.jp daiwa.hiroshima.jp etajima.hiroshima.jp fuchu.hiroshima.jp fukuyama.hiroshima.jp hatsukaichi.hiroshima.jp higashihiroshima.hiroshima.jp hongo.hiroshima.jp jinsekikogen.hiroshima.jp kaita.hiroshima.jp kui.hiroshima.jp kumano.hiroshima.jp kure.hiroshima.jp mihara.hiroshima.jp miyoshi.hiroshima.jp naka.hiroshima.jp onomichi.hiroshima.jp osakikamijima.hiroshima.jp otake.hiroshima.jp saka.hiroshima.jp sera.hiroshima.jp seranishi.hiroshima.jp shinichi.hiroshima.jp shobara.hiroshima.jp takehara.hiroshima.jp abashiri.hokkaido.jp abira.hokkaido.jp aibetsu.hokkaido.jp akabira.hokkaido.jp akkeshi.hokkaido.jp asahikawa.hokkaido.jp ashibetsu.hokkaido.jp ashoro.hokkaido.jp assabu.hokkaido.jp atsuma.hokkaido.jp bibai.hokkaido.jp biei.hokkaido.jp bifuka.hokkaido.jp bihoro.hokkaido.jp biratori.hokkaido.jp chippubetsu.hokkaido.jp chitose.hokkaido.jp date.hokkaido.jp ebetsu.hokkaido.jp embetsu.hokkaido.jp eniwa.hokkaido.jp erimo.hokkaido.jp esan.hokkaido.jp esashi.hokkaido.jp fukagawa.hokkaido.jp fukushima.hokkaido.jp furano.hokkaido.jp furubira.hokkaido.jp haboro.hokkaido.jp hakodate.hokkaido.jp hamatonbetsu.hokkaido.jp hidaka.hokkaido.jp higashikagura.hokkaido.jp higashikawa.hokkaido.jp hiroo.hokkaido.jp hokuryu.hokkaido.jp hokuto.hokkaido.jp honbetsu.hokkaido.jp horokanai.hokkaido.jp horonobe.hokkaido.jp ikeda.hokkaido.jp imakane.hokkaido.jp ishikari.hokkaido.jp iwamizawa.hokkaido.jp iwanai.hokkaido.jp kamifurano.hokkaido.jp kamikawa.hokkaido.jp kamishihoro.hokkaido.jp kamisunagawa.hokkaido.jp kamoenai.hokkaido.jp kayabe.hokkaido.jp kembuchi.hokkaido.jp kikonai.hokkaido.jp kimobetsu.hokkaido.jp kitahiroshima.hokkaido.jp kitami.hokkaido.jp kiyosato.hokkaido.jp koshimizu.hokkaido.jp kunneppu.hokkaido.jp kuriyama.hokkaido.jp kuromatsunai.hokkaido.jp kushiro.hokkaido.jp kutchan.hokkaido.jp kyowa.hokkaido.jp mashike.hokkaido.jp matsumae.hokkaido.jp mikasa.hokkaido.jp minamifurano.hokkaido.jp mombetsu.hokkaido.jp moseushi.hokkaido.jp mukawa.hokkaido.jp muroran.hokkaido.jp naie.hokkaido.jp nakagawa.hokkaido.jp nakasatsunai.hokkaido.jp nakatombetsu.hokkaido.jp nanae.hokkaido.jp nanporo.hokkaido.jp nayoro.hokkaido.jp nemuro.hokkaido.jp niikappu.hokkaido.jp niki.hokkaido.jp nishiokoppe.hokkaido.jp noboribetsu.hokkaido.jp numata.hokkaido.jp obihiro.hokkaido.jp obira.hokkaido.jp oketo.hokkaido.jp okoppe.hokkaido.jp otaru.hokkaido.jp otobe.hokkaido.jp otofuke.hokkaido.jp otoineppu.hokkaido.jp oumu.hokkaido.jp ozora.hokkaido.jp pippu.hokkaido.jp rankoshi.hokkaido.jp rebun.hokkaido.jp rikubetsu.hokkaido.jp rishiri.hokkaido.jp rishirifuji.hokkaido.jp saroma.hokkaido.jp sarufutsu.hokkaido.jp shakotan.hokkaido.jp shari.hokkaido.jp shibecha.hokkaido.jp shibetsu.hokkaido.jp shikabe.hokkaido.jp shikaoi.hokkaido.jp shimamaki.hokkaido.jp shimizu.hokkaido.jp shimokawa.hokkaido.jp shinshinotsu.hokkaido.jp shintoku.hokkaido.jp shiranuka.hokkaido.jp shiraoi.hokkaido.jp shiriuchi.hokkaido.jp sobetsu.hokkaido.jp sunagawa.hokkaido.jp taiki.hokkaido.jp takasu.hokkaido.jp takikawa.hokkaido.jp takinoue.hokkaido.jp teshikaga.hokkaido.jp tobetsu.hokkaido.jp tohma.hokkaido.jp tomakomai.hokkaido.jp tomari.hokkaido.jp toya.hokkaido.jp toyako.hokkaido.jp toyotomi.hokkaido.jp toyoura.hokkaido.jp tsubetsu.hokkaido.jp tsukigata.hokkaido.jp urakawa.hokkaido.jp urausu.hokkaido.jp uryu.hokkaido.jp utashinai.hokkaido.jp wakkanai.hokkaido.jp wassamu.hokkaido.jp yakumo.hokkaido.jp yoichi.hokkaido.jp aioi.hyogo.jp akashi.hyogo.jp ako.hyogo.jp amagasaki.hyogo.jp aogaki.hyogo.jp asago.hyogo.jp ashiya.hyogo.jp awaji.hyogo.jp fukusaki.hyogo.jp goshiki.hyogo.jp harima.hyogo.jp himeji.hyogo.jp ichikawa.hyogo.jp inagawa.hyogo.jp itami.hyogo.jp kakogawa.hyogo.jp kamigori.hyogo.jp kamikawa.hyogo.jp kasai.hyogo.jp kasuga.hyogo.jp kawanishi.hyogo.jp miki.hyogo.jp minamiawaji.hyogo.jp nishinomiya.hyogo.jp nishiwaki.hyogo.jp ono.hyogo.jp sanda.hyogo.jp sannan.hyogo.jp sasayama.hyogo.jp sayo.hyogo.jp shingu.hyogo.jp shinonsen.hyogo.jp shiso.hyogo.jp sumoto.hyogo.jp taishi.hyogo.jp taka.hyogo.jp takarazuka.hyogo.jp takasago.hyogo.jp takino.hyogo.jp tamba.hyogo.jp tatsuno.hyogo.jp toyooka.hyogo.jp yabu.hyogo.jp yashiro.hyogo.jp yoka.hyogo.jp yokawa.hyogo.jp ami.ibaraki.jp asahi.ibaraki.jp bando.ibaraki.jp chikusei.ibaraki.jp daigo.ibaraki.jp fujishiro.ibaraki.jp hitachi.ibaraki.jp hitachinaka.ibaraki.jp hitachiomiya.ibaraki.jp hitachiota.ibaraki.jp ibaraki.ibaraki.jp ina.ibaraki.jp inashiki.ibaraki.jp itako.ibaraki.jp iwama.ibaraki.jp joso.ibaraki.jp kamisu.ibaraki.jp kasama.ibaraki.jp kashima.ibaraki.jp kasumigaura.ibaraki.jp koga.ibaraki.jp miho.ibaraki.jp mito.ibaraki.jp moriya.ibaraki.jp naka.ibaraki.jp namegata.ibaraki.jp oarai.ibaraki.jp ogawa.ibaraki.jp omitama.ibaraki.jp ryugasaki.ibaraki.jp sakai.ibaraki.jp sakuragawa.ibaraki.jp shimodate.ibaraki.jp shimotsuma.ibaraki.jp shirosato.ibaraki.jp sowa.ibaraki.jp suifu.ibaraki.jp takahagi.ibaraki.jp tamatsukuri.ibaraki.jp tokai.ibaraki.jp tomobe.ibaraki.jp tone.ibaraki.jp toride.ibaraki.jp tsuchiura.ibaraki.jp tsukuba.ibaraki.jp uchihara.ibaraki.jp ushiku.ibaraki.jp yachiyo.ibaraki.jp yamagata.ibaraki.jp yawara.ibaraki.jp yuki.ibaraki.jp anamizu.ishikawa.jp hakui.ishikawa.jp hakusan.ishikawa.jp kaga.ishikawa.jp kahoku.ishikawa.jp kanazawa.ishikawa.jp kawakita.ishikawa.jp komatsu.ishikawa.jp nakanoto.ishikawa.jp nanao.ishikawa.jp nomi.ishikawa.jp nonoichi.ishikawa.jp noto.ishikawa.jp shika.ishikawa.jp suzu.ishikawa.jp tsubata.ishikawa.jp tsurugi.ishikawa.jp uchinada.ishikawa.jp wajima.ishikawa.jp fudai.iwate.jp fujisawa.iwate.jp hanamaki.iwate.jp hiraizumi.iwate.jp hirono.iwate.jp ichinohe.iwate.jp ichinoseki.iwate.jp iwaizumi.iwate.jp iwate.iwate.jp joboji.iwate.jp kamaishi.iwate.jp kanegasaki.iwate.jp karumai.iwate.jp kawai.iwate.jp kitakami.iwate.jp kuji.iwate.jp kunohe.iwate.jp kuzumaki.iwate.jp miyako.iwate.jp mizusawa.iwate.jp morioka.iwate.jp ninohe.iwate.jp noda.iwate.jp ofunato.iwate.jp oshu.iwate.jp otsuchi.iwate.jp rikuzentakata.iwate.jp shiwa.iwate.jp shizukuishi.iwate.jp sumita.iwate.jp tanohata.iwate.jp tono.iwate.jp yahaba.iwate.jp yamada.iwate.jp ayagawa.kagawa.jp higashikagawa.kagawa.jp kanonji.kagawa.jp kotohira.kagawa.jp manno.kagawa.jp marugame.kagawa.jp mitoyo.kagawa.jp naoshima.kagawa.jp sanuki.kagawa.jp tadotsu.kagawa.jp takamatsu.kagawa.jp tonosho.kagawa.jp uchinomi.kagawa.jp utazu.kagawa.jp zentsuji.kagawa.jp akune.kagoshima.jp amami.kagoshima.jp hioki.kagoshima.jp isa.kagoshima.jp isen.kagoshima.jp izumi.kagoshima.jp kagoshima.kagoshima.jp kanoya.kagoshima.jp kawanabe.kagoshima.jp kinko.kagoshima.jp kouyama.kagoshima.jp makurazaki.kagoshima.jp matsumoto.kagoshima.jp minamitane.kagoshima.jp nakatane.kagoshima.jp nishinoomote.kagoshima.jp satsumasendai.kagoshima.jp soo.kagoshima.jp tarumizu.kagoshima.jp yusui.kagoshima.jp aikawa.kanagawa.jp atsugi.kanagawa.jp ayase.kanagawa.jp chigasaki.kanagawa.jp ebina.kanagawa.jp fujisawa.kanagawa.jp hadano.kanagawa.jp hakone.kanagawa.jp hiratsuka.kanagawa.jp isehara.kanagawa.jp kaisei.kanagawa.jp kamakura.kanagawa.jp kiyokawa.kanagawa.jp matsuda.kanagawa.jp minamiashigara.kanagawa.jp miura.kanagawa.jp nakai.kanagawa.jp ninomiya.kanagawa.jp odawara.kanagawa.jp oi.kanagawa.jp oiso.kanagawa.jp sagamihara.kanagawa.jp samukawa.kanagawa.jp tsukui.kanagawa.jp yamakita.kanagawa.jp yamato.kanagawa.jp yokosuka.kanagawa.jp yugawara.kanagawa.jp zama.kanagawa.jp zushi.kanagawa.jp aki.kochi.jp geisei.kochi.jp hidaka.kochi.jp higashitsuno.kochi.jp ino.kochi.jp kagami.kochi.jp kami.kochi.jp kitagawa.kochi.jp kochi.kochi.jp mihara.kochi.jp motoyama.kochi.jp muroto.kochi.jp nahari.kochi.jp nakamura.kochi.jp nankoku.kochi.jp nishitosa.kochi.jp niyodogawa.kochi.jp ochi.kochi.jp okawa.kochi.jp otoyo.kochi.jp otsuki.kochi.jp sakawa.kochi.jp sukumo.kochi.jp susaki.kochi.jp tosa.kochi.jp tosashimizu.kochi.jp toyo.kochi.jp tsuno.kochi.jp umaji.kochi.jp yasuda.kochi.jp yusuhara.kochi.jp amakusa.kumamoto.jp arao.kumamoto.jp aso.kumamoto.jp choyo.kumamoto.jp gyokuto.kumamoto.jp kamiamakusa.kumamoto.jp kikuchi.kumamoto.jp kumamoto.kumamoto.jp mashiki.kumamoto.jp mifune.kumamoto.jp minamata.kumamoto.jp minamioguni.kumamoto.jp nagasu.kumamoto.jp nishihara.kumamoto.jp oguni.kumamoto.jp ozu.kumamoto.jp sumoto.kumamoto.jp takamori.kumamoto.jp uki.kumamoto.jp uto.kumamoto.jp yamaga.kumamoto.jp yamato.kumamoto.jp yatsushiro.kumamoto.jp ayabe.kyoto.jp fukuchiyama.kyoto.jp higashiyama.kyoto.jp ide.kyoto.jp ine.kyoto.jp joyo.kyoto.jp kameoka.kyoto.jp kamo.kyoto.jp kita.kyoto.jp kizu.kyoto.jp kumiyama.kyoto.jp kyotamba.kyoto.jp kyotanabe.kyoto.jp kyotango.kyoto.jp maizuru.kyoto.jp minami.kyoto.jp minamiyamashiro.kyoto.jp miyazu.kyoto.jp muko.kyoto.jp nagaokakyo.kyoto.jp nakagyo.kyoto.jp nantan.kyoto.jp oyamazaki.kyoto.jp sakyo.kyoto.jp seika.kyoto.jp tanabe.kyoto.jp uji.kyoto.jp ujitawara.kyoto.jp wazuka.kyoto.jp yamashina.kyoto.jp yawata.kyoto.jp asahi.mie.jp inabe.mie.jp ise.mie.jp kameyama.mie.jp kawagoe.mie.jp kiho.mie.jp kisosaki.mie.jp kiwa.mie.jp komono.mie.jp kumano.mie.jp kuwana.mie.jp matsusaka.mie.jp meiwa.mie.jp mihama.mie.jp minamiise.mie.jp misugi.mie.jp miyama.mie.jp nabari.mie.jp shima.mie.jp suzuka.mie.jp tado.mie.jp taiki.mie.jp taki.mie.jp tamaki.mie.jp toba.mie.jp tsu.mie.jp udono.mie.jp ureshino.mie.jp watarai.mie.jp yokkaichi.mie.jp furukawa.miyagi.jp higashimatsushima.miyagi.jp ishinomaki.miyagi.jp iwanuma.miyagi.jp kakuda.miyagi.jp kami.miyagi.jp kawasaki.miyagi.jp marumori.miyagi.jp matsushima.miyagi.jp minamisanriku.miyagi.jp misato.miyagi.jp murata.miyagi.jp natori.miyagi.jp ogawara.miyagi.jp ohira.miyagi.jp onagawa.miyagi.jp osaki.miyagi.jp rifu.miyagi.jp semine.miyagi.jp shibata.miyagi.jp shichikashuku.miyagi.jp shikama.miyagi.jp shiogama.miyagi.jp shiroishi.miyagi.jp tagajo.miyagi.jp taiwa.miyagi.jp tome.miyagi.jp tomiya.miyagi.jp wakuya.miyagi.jp watari.miyagi.jp yamamoto.miyagi.jp zao.miyagi.jp aya.miyazaki.jp ebino.miyazaki.jp gokase.miyazaki.jp hyuga.miyazaki.jp kadogawa.miyazaki.jp kawaminami.miyazaki.jp kijo.miyazaki.jp kitagawa.miyazaki.jp kitakata.miyazaki.jp kitaura.miyazaki.jp kobayashi.miyazaki.jp kunitomi.miyazaki.jp kushima.miyazaki.jp mimata.miyazaki.jp miyakonojo.miyazaki.jp miyazaki.miyazaki.jp morotsuka.miyazaki.jp nichinan.miyazaki.jp nishimera.miyazaki.jp nobeoka.miyazaki.jp saito.miyazaki.jp shiiba.miyazaki.jp shintomi.miyazaki.jp takaharu.miyazaki.jp takanabe.miyazaki.jp takazaki.miyazaki.jp tsuno.miyazaki.jp achi.nagano.jp agematsu.nagano.jp anan.nagano.jp aoki.nagano.jp asahi.nagano.jp azumino.nagano.jp chikuhoku.nagano.jp chikuma.nagano.jp chino.nagano.jp fujimi.nagano.jp hakuba.nagano.jp hara.nagano.jp hiraya.nagano.jp iida.nagano.jp iijima.nagano.jp iiyama.nagano.jp iizuna.nagano.jp ikeda.nagano.jp ikusaka.nagano.jp ina.nagano.jp karuizawa.nagano.jp kawakami.nagano.jp kiso.nagano.jp kisofukushima.nagano.jp kitaaiki.nagano.jp komagane.nagano.jp komoro.nagano.jp matsukawa.nagano.jp matsumoto.nagano.jp miasa.nagano.jp minamiaiki.nagano.jp minamimaki.nagano.jp minamiminowa.nagano.jp minowa.nagano.jp miyada.nagano.jp miyota.nagano.jp mochizuki.nagano.jp nagano.nagano.jp nagawa.nagano.jp nagiso.nagano.jp nakagawa.nagano.jp nakano.nagano.jp nozawaonsen.nagano.jp obuse.nagano.jp ogawa.nagano.jp okaya.nagano.jp omachi.nagano.jp omi.nagano.jp ookuwa.nagano.jp ooshika.nagano.jp otaki.nagano.jp otari.nagano.jp sakae.nagano.jp sakaki.nagano.jp saku.nagano.jp sakuho.nagano.jp shimosuwa.nagano.jp shinanomachi.nagano.jp shiojiri.nagano.jp suwa.nagano.jp suzaka.nagano.jp takagi.nagano.jp takamori.nagano.jp takayama.nagano.jp tateshina.nagano.jp tatsuno.nagano.jp togakushi.nagano.jp togura.nagano.jp tomi.nagano.jp ueda.nagano.jp wada.nagano.jp yamagata.nagano.jp yamanouchi.nagano.jp yasaka.nagano.jp yasuoka.nagano.jp chijiwa.nagasaki.jp futsu.nagasaki.jp goto.nagasaki.jp hasami.nagasaki.jp hirado.nagasaki.jp iki.nagasaki.jp isahaya.nagasaki.jp kawatana.nagasaki.jp kuchinotsu.nagasaki.jp matsuura.nagasaki.jp nagasaki.nagasaki.jp obama.nagasaki.jp omura.nagasaki.jp oseto.nagasaki.jp saikai.nagasaki.jp sasebo.nagasaki.jp seihi.nagasaki.jp shimabara.nagasaki.jp shinkamigoto.nagasaki.jp togitsu.nagasaki.jp tsushima.nagasaki.jp unzen.nagasaki.jp ando.nara.jp gose.nara.jp heguri.nara.jp higashiyoshino.nara.jp ikaruga.nara.jp ikoma.nara.jp kamikitayama.nara.jp kanmaki.nara.jp kashiba.nara.jp kashihara.nara.jp katsuragi.nara.jp kawai.nara.jp kawakami.nara.jp kawanishi.nara.jp koryo.nara.jp kurotaki.nara.jp mitsue.nara.jp miyake.nara.jp nara.nara.jp nosegawa.nara.jp oji.nara.jp ouda.nara.jp oyodo.nara.jp sakurai.nara.jp sango.nara.jp shimoichi.nara.jp shimokitayama.nara.jp shinjo.nara.jp soni.nara.jp takatori.nara.jp tawaramoto.nara.jp tenkawa.nara.jp tenri.nara.jp uda.nara.jp yamatokoriyama.nara.jp yamatotakada.nara.jp yamazoe.nara.jp yoshino.nara.jp aga.niigata.jp agano.niigata.jp gosen.niigata.jp itoigawa.niigata.jp izumozaki.niigata.jp joetsu.niigata.jp kamo.niigata.jp kariwa.niigata.jp kashiwazaki.niigata.jp minamiuonuma.niigata.jp mitsuke.niigata.jp muika.niigata.jp murakami.niigata.jp myoko.niigata.jp nagaoka.niigata.jp niigata.niigata.jp ojiya.niigata.jp omi.niigata.jp sado.niigata.jp sanjo.niigata.jp seiro.niigata.jp seirou.niigata.jp sekikawa.niigata.jp shibata.niigata.jp tagami.niigata.jp tainai.niigata.jp tochio.niigata.jp tokamachi.niigata.jp tsubame.niigata.jp tsunan.niigata.jp uonuma.niigata.jp yahiko.niigata.jp yoita.niigata.jp yuzawa.niigata.jp beppu.oita.jp bungoono.oita.jp bungotakada.oita.jp hasama.oita.jp hiji.oita.jp himeshima.oita.jp hita.oita.jp kamitsue.oita.jp kokonoe.oita.jp kuju.oita.jp kunisaki.oita.jp kusu.oita.jp oita.oita.jp saiki.oita.jp taketa.oita.jp tsukumi.oita.jp usa.oita.jp usuki.oita.jp yufu.oita.jp akaiwa.okayama.jp asakuchi.okayama.jp bizen.okayama.jp hayashima.okayama.jp ibara.okayama.jp kagamino.okayama.jp kasaoka.okayama.jp kibichuo.okayama.jp kumenan.okayama.jp kurashiki.okayama.jp maniwa.okayama.jp misaki.okayama.jp nagi.okayama.jp niimi.okayama.jp nishiawakura.okayama.jp okayama.okayama.jp satosho.okayama.jp setouchi.okayama.jp shinjo.okayama.jp shoo.okayama.jp soja.okayama.jp takahashi.okayama.jp tamano.okayama.jp tsuyama.okayama.jp wake.okayama.jp yakage.okayama.jp aguni.okinawa.jp ginowan.okinawa.jp ginoza.okinawa.jp gushikami.okinawa.jp haebaru.okinawa.jp higashi.okinawa.jp hirara.okinawa.jp iheya.okinawa.jp ishigaki.okinawa.jp ishikawa.okinawa.jp itoman.okinawa.jp izena.okinawa.jp kadena.okinawa.jp kin.okinawa.jp kitadaito.okinawa.jp kitanakagusuku.okinawa.jp kumejima.okinawa.jp kunigami.okinawa.jp minamidaito.okinawa.jp motobu.okinawa.jp nago.okinawa.jp naha.okinawa.jp nakagusuku.okinawa.jp nakijin.okinawa.jp nanjo.okinawa.jp nishihara.okinawa.jp ogimi.okinawa.jp okinawa.okinawa.jp onna.okinawa.jp shimoji.okinawa.jp taketomi.okinawa.jp tarama.okinawa.jp tokashiki.okinawa.jp tomigusuku.okinawa.jp tonaki.okinawa.jp urasoe.okinawa.jp uruma.okinawa.jp yaese.okinawa.jp yomitan.okinawa.jp yonabaru.okinawa.jp yonaguni.okinawa.jp zamami.okinawa.jp abeno.osaka.jp chihayaakasaka.osaka.jp chuo.osaka.jp daito.osaka.jp fujiidera.osaka.jp habikino.osaka.jp hannan.osaka.jp higashiosaka.osaka.jp higashisumiyoshi.osaka.jp higashiyodogawa.osaka.jp hirakata.osaka.jp ibaraki.osaka.jp ikeda.osaka.jp izumi.osaka.jp izumiotsu.osaka.jp izumisano.osaka.jp kadoma.osaka.jp kaizuka.osaka.jp kanan.osaka.jp kashiwara.osaka.jp katano.osaka.jp kawachinagano.osaka.jp kishiwada.osaka.jp kita.osaka.jp kumatori.osaka.jp matsubara.osaka.jp minato.osaka.jp minoh.osaka.jp misaki.osaka.jp moriguchi.osaka.jp neyagawa.osaka.jp nishi.osaka.jp nose.osaka.jp osakasayama.osaka.jp sakai.osaka.jp sayama.osaka.jp sennan.osaka.jp settsu.osaka.jp shijonawate.osaka.jp shimamoto.osaka.jp suita.osaka.jp tadaoka.osaka.jp taishi.osaka.jp tajiri.osaka.jp takaishi.osaka.jp takatsuki.osaka.jp tondabayashi.osaka.jp toyonaka.osaka.jp toyono.osaka.jp yao.osaka.jp ariake.saga.jp arita.saga.jp fukudomi.saga.jp genkai.saga.jp hamatama.saga.jp hizen.saga.jp imari.saga.jp kamimine.saga.jp kanzaki.saga.jp karatsu.saga.jp kashima.saga.jp kitagata.saga.jp kitahata.saga.jp kiyama.saga.jp kouhoku.saga.jp kyuragi.saga.jp nishiarita.saga.jp ogi.saga.jp omachi.saga.jp ouchi.saga.jp saga.saga.jp shiroishi.saga.jp taku.saga.jp tara.saga.jp tosu.saga.jp yoshinogari.saga.jp arakawa.saitama.jp asaka.saitama.jp chichibu.saitama.jp fujimi.saitama.jp fujimino.saitama.jp fukaya.saitama.jp hanno.saitama.jp hanyu.saitama.jp hasuda.saitama.jp hatogaya.saitama.jp hatoyama.saitama.jp hidaka.saitama.jp higashichichibu.saitama.jp higashimatsuyama.saitama.jp honjo.saitama.jp ina.saitama.jp iruma.saitama.jp iwatsuki.saitama.jp kamiizumi.saitama.jp kamikawa.saitama.jp kamisato.saitama.jp kasukabe.saitama.jp kawagoe.saitama.jp kawaguchi.saitama.jp kawajima.saitama.jp kazo.saitama.jp kitamoto.saitama.jp koshigaya.saitama.jp kounosu.saitama.jp kuki.saitama.jp kumagaya.saitama.jp matsubushi.saitama.jp minano.saitama.jp misato.saitama.jp miyashiro.saitama.jp miyoshi.saitama.jp moroyama.saitama.jp nagatoro.saitama.jp namegawa.saitama.jp niiza.saitama.jp ogano.saitama.jp ogawa.saitama.jp ogose.saitama.jp okegawa.saitama.jp omiya.saitama.jp otaki.saitama.jp ranzan.saitama.jp ryokami.saitama.jp saitama.saitama.jp sakado.saitama.jp satte.saitama.jp sayama.saitama.jp shiki.saitama.jp shiraoka.saitama.jp soka.saitama.jp sugito.saitama.jp toda.saitama.jp tokigawa.saitama.jp tokorozawa.saitama.jp tsurugashima.saitama.jp urawa.saitama.jp warabi.saitama.jp yashio.saitama.jp yokoze.saitama.jp yono.saitama.jp yorii.saitama.jp yoshida.saitama.jp yoshikawa.saitama.jp yoshimi.saitama.jp aisho.shiga.jp gamo.shiga.jp higashiomi.shiga.jp hikone.shiga.jp koka.shiga.jp konan.shiga.jp kosei.shiga.jp koto.shiga.jp kusatsu.shiga.jp maibara.shiga.jp moriyama.shiga.jp nagahama.shiga.jp nishiazai.shiga.jp notogawa.shiga.jp omihachiman.shiga.jp otsu.shiga.jp ritto.shiga.jp ryuoh.shiga.jp takashima.shiga.jp takatsuki.shiga.jp torahime.shiga.jp toyosato.shiga.jp yasu.shiga.jp akagi.shimane.jp ama.shimane.jp gotsu.shimane.jp hamada.shimane.jp higashiizumo.shimane.jp hikawa.shimane.jp hikimi.shimane.jp izumo.shimane.jp kakinoki.shimane.jp masuda.shimane.jp matsue.shimane.jp misato.shimane.jp nishinoshima.shimane.jp ohda.shimane.jp okinoshima.shimane.jp okuizumo.shimane.jp shimane.shimane.jp tamayu.shimane.jp tsuwano.shimane.jp unnan.shimane.jp yakumo.shimane.jp yasugi.shimane.jp yatsuka.shimane.jp arai.shizuoka.jp atami.shizuoka.jp fuji.shizuoka.jp fujieda.shizuoka.jp fujikawa.shizuoka.jp fujinomiya.shizuoka.jp fukuroi.shizuoka.jp gotemba.shizuoka.jp haibara.shizuoka.jp hamamatsu.shizuoka.jp higashiizu.shizuoka.jp ito.shizuoka.jp iwata.shizuoka.jp izu.shizuoka.jp izunokuni.shizuoka.jp kakegawa.shizuoka.jp kannami.shizuoka.jp kawanehon.shizuoka.jp kawazu.shizuoka.jp kikugawa.shizuoka.jp kosai.shizuoka.jp makinohara.shizuoka.jp matsuzaki.shizuoka.jp minamiizu.shizuoka.jp mishima.shizuoka.jp morimachi.shizuoka.jp nishiizu.shizuoka.jp numazu.shizuoka.jp omaezaki.shizuoka.jp shimada.shizuoka.jp shimizu.shizuoka.jp shimoda.shizuoka.jp shizuoka.shizuoka.jp susono.shizuoka.jp yaizu.shizuoka.jp yoshida.shizuoka.jp ashikaga.tochigi.jp bato.tochigi.jp haga.tochigi.jp ichikai.tochigi.jp iwafune.tochigi.jp kaminokawa.tochigi.jp kanuma.tochigi.jp karasuyama.tochigi.jp kuroiso.tochigi.jp mashiko.tochigi.jp mibu.tochigi.jp moka.tochigi.jp motegi.tochigi.jp nasu.tochigi.jp nasushiobara.tochigi.jp nikko.tochigi.jp nishikata.tochigi.jp nogi.tochigi.jp ohira.tochigi.jp ohtawara.tochigi.jp oyama.tochigi.jp sakura.tochigi.jp sano.tochigi.jp shimotsuke.tochigi.jp shioya.tochigi.jp takanezawa.tochigi.jp tochigi.tochigi.jp tsuga.tochigi.jp ujiie.tochigi.jp utsunomiya.tochigi.jp yaita.tochigi.jp aizumi.tokushima.jp anan.tokushima.jp ichiba.tokushima.jp itano.tokushima.jp kainan.tokushima.jp komatsushima.tokushima.jp matsushige.tokushima.jp mima.tokushima.jp minami.tokushima.jp miyoshi.tokushima.jp mugi.tokushima.jp nakagawa.tokushima.jp naruto.tokushima.jp sanagochi.tokushima.jp shishikui.tokushima.jp tokushima.tokushima.jp wajiki.tokushima.jp adachi.tokyo.jp akiruno.tokyo.jp akishima.tokyo.jp aogashima.tokyo.jp arakawa.tokyo.jp bunkyo.tokyo.jp chiyoda.tokyo.jp chofu.tokyo.jp chuo.tokyo.jp edogawa.tokyo.jp fuchu.tokyo.jp fussa.tokyo.jp hachijo.tokyo.jp hachioji.tokyo.jp hamura.tokyo.jp higashikurume.tokyo.jp higashimurayama.tokyo.jp higashiyamato.tokyo.jp hino.tokyo.jp hinode.tokyo.jp hinohara.tokyo.jp inagi.tokyo.jp itabashi.tokyo.jp katsushika.tokyo.jp kita.tokyo.jp kiyose.tokyo.jp kodaira.tokyo.jp koganei.tokyo.jp kokubunji.tokyo.jp komae.tokyo.jp koto.tokyo.jp kouzushima.tokyo.jp kunitachi.tokyo.jp machida.tokyo.jp meguro.tokyo.jp minato.tokyo.jp mitaka.tokyo.jp mizuho.tokyo.jp musashimurayama.tokyo.jp musashino.tokyo.jp nakano.tokyo.jp nerima.tokyo.jp ogasawara.tokyo.jp okutama.tokyo.jp ome.tokyo.jp oshima.tokyo.jp ota.tokyo.jp setagaya.tokyo.jp shibuya.tokyo.jp shinagawa.tokyo.jp shinjuku.tokyo.jp suginami.tokyo.jp sumida.tokyo.jp tachikawa.tokyo.jp taito.tokyo.jp tama.tokyo.jp toshima.tokyo.jp chizu.tottori.jp hino.tottori.jp kawahara.tottori.jp koge.tottori.jp kotoura.tottori.jp misasa.tottori.jp nanbu.tottori.jp nichinan.tottori.jp sakaiminato.tottori.jp tottori.tottori.jp wakasa.tottori.jp yazu.tottori.jp yonago.tottori.jp asahi.toyama.jp fuchu.toyama.jp fukumitsu.toyama.jp funahashi.toyama.jp himi.toyama.jp imizu.toyama.jp inami.toyama.jp johana.toyama.jp kamiichi.toyama.jp kurobe.toyama.jp nakaniikawa.toyama.jp namerikawa.toyama.jp nanto.toyama.jp nyuzen.toyama.jp oyabe.toyama.jp taira.toyama.jp takaoka.toyama.jp tateyama.toyama.jp toga.toyama.jp tonami.toyama.jp toyama.toyama.jp unazuki.toyama.jp uozu.toyama.jp yamada.toyama.jp arida.wakayama.jp aridagawa.wakayama.jp gobo.wakayama.jp hashimoto.wakayama.jp hidaka.wakayama.jp hirogawa.wakayama.jp inami.wakayama.jp iwade.wakayama.jp kainan.wakayama.jp kamitonda.wakayama.jp katsuragi.wakayama.jp kimino.wakayama.jp kinokawa.wakayama.jp kitayama.wakayama.jp koya.wakayama.jp koza.wakayama.jp kozagawa.wakayama.jp kudoyama.wakayama.jp kushimoto.wakayama.jp mihama.wakayama.jp misato.wakayama.jp nachikatsuura.wakayama.jp shingu.wakayama.jp shirahama.wakayama.jp taiji.wakayama.jp tanabe.wakayama.jp wakayama.wakayama.jp yuasa.wakayama.jp yura.wakayama.jp asahi.yamagata.jp funagata.yamagata.jp higashine.yamagata.jp iide.yamagata.jp kahoku.yamagata.jp kaminoyama.yamagata.jp kaneyama.yamagata.jp kawanishi.yamagata.jp mamurogawa.yamagata.jp mikawa.yamagata.jp murayama.yamagata.jp nagai.yamagata.jp nakayama.yamagata.jp nanyo.yamagata.jp nishikawa.yamagata.jp obanazawa.yamagata.jp oe.yamagata.jp oguni.yamagata.jp ohkura.yamagata.jp oishida.yamagata.jp sagae.yamagata.jp sakata.yamagata.jp sakegawa.yamagata.jp shinjo.yamagata.jp shirataka.yamagata.jp shonai.yamagata.jp takahata.yamagata.jp tendo.yamagata.jp tozawa.yamagata.jp tsuruoka.yamagata.jp yamagata.yamagata.jp yamanobe.yamagata.jp yonezawa.yamagata.jp yuza.yamagata.jp abu.yamaguchi.jp hagi.yamaguchi.jp hikari.yamaguchi.jp hofu.yamaguchi.jp iwakuni.yamaguchi.jp kudamatsu.yamaguchi.jp mitou.yamaguchi.jp nagato.yamaguchi.jp oshima.yamaguchi.jp shimonoseki.yamaguchi.jp shunan.yamaguchi.jp tabuse.yamaguchi.jp tokuyama.yamaguchi.jp toyota.yamaguchi.jp ube.yamaguchi.jp yuu.yamaguchi.jp chuo.yamanashi.jp doshi.yamanashi.jp fuefuki.yamanashi.jp fujikawa.yamanashi.jp fujikawaguchiko.yamanashi.jp fujiyoshida.yamanashi.jp hayakawa.yamanashi.jp hokuto.yamanashi.jp ichikawamisato.yamanashi.jp kai.yamanashi.jp kofu.yamanashi.jp koshu.yamanashi.jp kosuge.yamanashi.jp minami-alps.yamanashi.jp minobu.yamanashi.jp nakamichi.yamanashi.jp nanbu.yamanashi.jp narusawa.yamanashi.jp nirasaki.yamanashi.jp nishikatsura.yamanashi.jp oshino.yamanashi.jp otsuki.yamanashi.jp showa.yamanashi.jp tabayama.yamanashi.jp tsuru.yamanashi.jp uenohara.yamanashi.jp yamanakako.yamanashi.jp yamanashi.yamanashi.jp // ke : http://www.kenic.or.ke/index.php/en/ke-domains/ke-domains ke ac.ke co.ke go.ke info.ke me.ke mobi.ke ne.ke or.ke sc.ke // kg : http://www.domain.kg/dmn_n.html kg org.kg net.kg com.kg edu.kg gov.kg mil.kg // kh : http://www.mptc.gov.kh/dns_registration.htm *.kh // ki : http://www.ki/dns/index.html ki edu.ki biz.ki net.ki org.ki gov.ki info.ki com.ki // km : https://en.wikipedia.org/wiki/.km // http://www.domaine.km/documents/charte.doc km org.km nom.km gov.km prd.km tm.km edu.km mil.km ass.km com.km // These are only mentioned as proposed suggestions at domaine.km, but // https://en.wikipedia.org/wiki/.km says they're available for registration: coop.km asso.km presse.km medecin.km notaires.km pharmaciens.km veterinaire.km gouv.km // kn : https://en.wikipedia.org/wiki/.kn // http://www.dot.kn/domainRules.html kn net.kn org.kn edu.kn gov.kn // kp : http://www.kcce.kp/en_index.php kp com.kp edu.kp gov.kp org.kp rep.kp tra.kp // kr : https://en.wikipedia.org/wiki/.kr // see also: http://domain.nida.or.kr/eng/registration.jsp kr ac.kr co.kr es.kr go.kr hs.kr kg.kr mil.kr ms.kr ne.kr or.kr pe.kr re.kr sc.kr // kr geographical names busan.kr chungbuk.kr chungnam.kr daegu.kr daejeon.kr gangwon.kr gwangju.kr gyeongbuk.kr gyeonggi.kr gyeongnam.kr incheon.kr jeju.kr jeonbuk.kr jeonnam.kr seoul.kr ulsan.kr // kw : https://www.nic.kw/policies/ // Confirmed by registry kw com.kw edu.kw emb.kw gov.kw ind.kw net.kw org.kw // ky : http://www.icta.ky/da_ky_reg_dom.php // Confirmed by registry 2008-06-17 ky com.ky edu.ky net.ky org.ky // kz : https://en.wikipedia.org/wiki/.kz // see also: http://www.nic.kz/rules/index.jsp kz org.kz edu.kz net.kz gov.kz mil.kz com.kz // la : https://en.wikipedia.org/wiki/.la // Submitted by registry la int.la net.la info.la edu.la gov.la per.la com.la org.la // lb : https://en.wikipedia.org/wiki/.lb // Submitted by registry lb com.lb edu.lb gov.lb net.lb org.lb // lc : https://en.wikipedia.org/wiki/.lc // see also: http://www.nic.lc/rules.htm lc com.lc net.lc co.lc org.lc edu.lc gov.lc // li : https://en.wikipedia.org/wiki/.li li // lk : https://www.nic.lk/index.php/domain-registration/lk-domain-naming-structure lk gov.lk sch.lk net.lk int.lk com.lk org.lk edu.lk ngo.lk soc.lk web.lk ltd.lk assn.lk grp.lk hotel.lk ac.lk // lr : http://psg.com/dns/lr/lr.txt // Submitted by registry lr com.lr edu.lr gov.lr org.lr net.lr // ls : http://www.nic.ls/ // Confirmed by registry ls ac.ls biz.ls co.ls edu.ls gov.ls info.ls net.ls org.ls sc.ls // lt : https://en.wikipedia.org/wiki/.lt lt // gov.lt : http://www.gov.lt/index_en.php gov.lt // lu : http://www.dns.lu/en/ lu // lv : http://www.nic.lv/DNS/En/generic.php lv com.lv edu.lv gov.lv org.lv mil.lv id.lv net.lv asn.lv conf.lv // ly : http://www.nic.ly/regulations.php ly com.ly net.ly gov.ly plc.ly edu.ly sch.ly med.ly org.ly id.ly // ma : https://en.wikipedia.org/wiki/.ma // http://www.anrt.ma/fr/admin/download/upload/file_fr782.pdf ma co.ma net.ma gov.ma org.ma ac.ma press.ma // mc : http://www.nic.mc/ mc tm.mc asso.mc // md : https://en.wikipedia.org/wiki/.md md // me : https://en.wikipedia.org/wiki/.me me co.me net.me org.me edu.me ac.me gov.me its.me priv.me // mg : http://nic.mg/nicmg/?page_id=39 mg org.mg nom.mg gov.mg prd.mg tm.mg edu.mg mil.mg com.mg co.mg // mh : https://en.wikipedia.org/wiki/.mh mh // mil : https://en.wikipedia.org/wiki/.mil mil // mk : https://en.wikipedia.org/wiki/.mk // see also: http://dns.marnet.net.mk/postapka.php mk com.mk org.mk net.mk edu.mk gov.mk inf.mk name.mk // ml : http://www.gobin.info/domainname/ml-template.doc // see also: https://en.wikipedia.org/wiki/.ml ml com.ml edu.ml gouv.ml gov.ml net.ml org.ml presse.ml // mm : https://en.wikipedia.org/wiki/.mm *.mm // mn : https://en.wikipedia.org/wiki/.mn mn gov.mn edu.mn org.mn // mo : http://www.monic.net.mo/ mo com.mo net.mo org.mo edu.mo gov.mo // mobi : https://en.wikipedia.org/wiki/.mobi mobi // mp : http://www.dot.mp/ // Confirmed by registry 2008-06-17 mp // mq : https://en.wikipedia.org/wiki/.mq mq // mr : https://en.wikipedia.org/wiki/.mr mr gov.mr // ms : http://www.nic.ms/pdf/MS_Domain_Name_Rules.pdf ms com.ms edu.ms gov.ms net.ms org.ms // mt : https://www.nic.org.mt/go/policy // Submitted by registry mt com.mt edu.mt net.mt org.mt // mu : https://en.wikipedia.org/wiki/.mu mu com.mu net.mu org.mu gov.mu ac.mu co.mu or.mu // museum : https://welcome.museum/wp-content/uploads/2018/05/20180525-Registration-Policy-MUSEUM-EN_VF-2.pdf https://welcome.museum/buy-your-dot-museum-2/ museum // mv : https://en.wikipedia.org/wiki/.mv // "mv" included because, contra Wikipedia, google.mv exists. mv aero.mv biz.mv com.mv coop.mv edu.mv gov.mv info.mv int.mv mil.mv museum.mv name.mv net.mv org.mv pro.mv // mw : http://www.registrar.mw/ mw ac.mw biz.mw co.mw com.mw coop.mw edu.mw gov.mw int.mw museum.mw net.mw org.mw // mx : http://www.nic.mx/ // Submitted by registry mx com.mx org.mx gob.mx edu.mx net.mx // my : http://www.mynic.my/ // Available strings: https://mynic.my/resources/domains/buying-a-domain/ my biz.my com.my edu.my gov.my mil.my name.my net.my org.my // mz : http://www.uem.mz/ // Submitted by registry mz ac.mz adv.mz co.mz edu.mz gov.mz mil.mz net.mz org.mz // na : http://www.na-nic.com.na/ // http://www.info.na/domain/ na info.na pro.na name.na school.na or.na dr.na us.na mx.na ca.na in.na cc.na tv.na ws.na mobi.na co.na com.na org.na // name : has 2nd-level tlds, but there's no list of them name // nc : http://www.cctld.nc/ nc asso.nc nom.nc // ne : https://en.wikipedia.org/wiki/.ne ne // net : https://en.wikipedia.org/wiki/.net net // nf : https://en.wikipedia.org/wiki/.nf nf com.nf net.nf per.nf rec.nf web.nf arts.nf firm.nf info.nf other.nf store.nf // ng : http://www.nira.org.ng/index.php/join-us/register-ng-domain/189-nira-slds ng com.ng edu.ng gov.ng i.ng mil.ng mobi.ng name.ng net.ng org.ng sch.ng // ni : http://www.nic.ni/ ni ac.ni biz.ni co.ni com.ni edu.ni gob.ni in.ni info.ni int.ni mil.ni net.ni nom.ni org.ni web.ni // nl : https://en.wikipedia.org/wiki/.nl // https://www.sidn.nl/ // ccTLD for the Netherlands nl // no : https://www.norid.no/en/om-domenenavn/regelverk-for-no/ // Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/ // Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/ // Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/ // RSS feed: https://teknisk.norid.no/en/feed/ no // Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/ fhs.no vgs.no fylkesbibl.no folkebibl.no museum.no idrett.no priv.no // Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/ mil.no stat.no dep.no kommune.no herad.no // Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/ // counties aa.no ah.no bu.no fm.no hl.no hm.no jan-mayen.no mr.no nl.no nt.no of.no ol.no oslo.no rl.no sf.no st.no svalbard.no tm.no tr.no va.no vf.no // primary and lower secondary schools per county gs.aa.no gs.ah.no gs.bu.no gs.fm.no gs.hl.no gs.hm.no gs.jan-mayen.no gs.mr.no gs.nl.no gs.nt.no gs.of.no gs.ol.no gs.oslo.no gs.rl.no gs.sf.no gs.st.no gs.svalbard.no gs.tm.no gs.tr.no gs.va.no gs.vf.no // cities akrehamn.no åkrehamn.no algard.no ålgård.no arna.no brumunddal.no bryne.no bronnoysund.no brønnøysund.no drobak.no drøbak.no egersund.no fetsund.no floro.no florø.no fredrikstad.no hokksund.no honefoss.no hønefoss.no jessheim.no jorpeland.no jørpeland.no kirkenes.no kopervik.no krokstadelva.no langevag.no langevåg.no leirvik.no mjondalen.no mjøndalen.no mo-i-rana.no mosjoen.no mosjøen.no nesoddtangen.no orkanger.no osoyro.no osøyro.no raholt.no råholt.no sandnessjoen.no sandnessjøen.no skedsmokorset.no slattum.no spjelkavik.no stathelle.no stavern.no stjordalshalsen.no stjørdalshalsen.no tananger.no tranby.no vossevangen.no // communities afjord.no åfjord.no agdenes.no al.no ål.no alesund.no ålesund.no alstahaug.no alta.no áltá.no alaheadju.no álaheadju.no alvdal.no amli.no åmli.no amot.no åmot.no andebu.no andoy.no andøy.no andasuolo.no ardal.no årdal.no aremark.no arendal.no ås.no aseral.no åseral.no asker.no askim.no askvoll.no askoy.no askøy.no asnes.no åsnes.no audnedaln.no aukra.no aure.no aurland.no aurskog-holand.no aurskog-høland.no austevoll.no austrheim.no averoy.no averøy.no balestrand.no ballangen.no balat.no bálát.no balsfjord.no bahccavuotna.no báhccavuotna.no bamble.no bardu.no beardu.no beiarn.no bajddar.no bájddar.no baidar.no báidár.no berg.no bergen.no berlevag.no berlevåg.no bearalvahki.no bearalváhki.no bindal.no birkenes.no bjarkoy.no bjarkøy.no bjerkreim.no bjugn.no bodo.no bodø.no badaddja.no bådåddjå.no budejju.no bokn.no bremanger.no bronnoy.no brønnøy.no bygland.no bykle.no barum.no bærum.no bo.telemark.no bø.telemark.no bo.nordland.no bø.nordland.no bievat.no bievát.no bomlo.no bømlo.no batsfjord.no båtsfjord.no bahcavuotna.no báhcavuotna.no dovre.no drammen.no drangedal.no dyroy.no dyrøy.no donna.no dønna.no eid.no eidfjord.no eidsberg.no eidskog.no eidsvoll.no eigersund.no elverum.no enebakk.no engerdal.no etne.no etnedal.no evenes.no evenassi.no evenášši.no evje-og-hornnes.no farsund.no fauske.no fuossko.no fuoisku.no fedje.no fet.no finnoy.no finnøy.no fitjar.no fjaler.no fjell.no flakstad.no flatanger.no flekkefjord.no flesberg.no flora.no fla.no flå.no folldal.no forsand.no fosnes.no frei.no frogn.no froland.no frosta.no frana.no fræna.no froya.no frøya.no fusa.no fyresdal.no forde.no førde.no gamvik.no gangaviika.no gáŋgaviika.no gaular.no gausdal.no gildeskal.no gildeskål.no giske.no gjemnes.no gjerdrum.no gjerstad.no gjesdal.no gjovik.no gjøvik.no gloppen.no gol.no gran.no grane.no granvin.no gratangen.no grimstad.no grong.no kraanghke.no kråanghke.no grue.no gulen.no hadsel.no halden.no halsa.no hamar.no hamaroy.no habmer.no hábmer.no hapmir.no hápmir.no hammerfest.no hammarfeasta.no hámmárfeasta.no haram.no hareid.no harstad.no hasvik.no aknoluokta.no ákŋoluokta.no hattfjelldal.no aarborte.no haugesund.no hemne.no hemnes.no hemsedal.no heroy.more-og-romsdal.no herøy.møre-og-romsdal.no heroy.nordland.no herøy.nordland.no hitra.no hjartdal.no hjelmeland.no hobol.no hobøl.no hof.no hol.no hole.no holmestrand.no holtalen.no holtålen.no hornindal.no horten.no hurdal.no hurum.no hvaler.no hyllestad.no hagebostad.no hægebostad.no hoyanger.no høyanger.no hoylandet.no høylandet.no ha.no hå.no ibestad.no inderoy.no inderøy.no iveland.no jevnaker.no jondal.no jolster.no jølster.no karasjok.no karasjohka.no kárášjohka.no karlsoy.no galsa.no gálsá.no karmoy.no karmøy.no kautokeino.no guovdageaidnu.no klepp.no klabu.no klæbu.no kongsberg.no kongsvinger.no kragero.no kragerø.no kristiansand.no kristiansund.no krodsherad.no krødsherad.no kvalsund.no rahkkeravju.no ráhkkerávju.no kvam.no kvinesdal.no kvinnherad.no kviteseid.no kvitsoy.no kvitsøy.no kvafjord.no kvæfjord.no giehtavuoatna.no kvanangen.no kvænangen.no navuotna.no návuotna.no kafjord.no kåfjord.no gaivuotna.no gáivuotna.no larvik.no lavangen.no lavagis.no loabat.no loabát.no lebesby.no davvesiida.no leikanger.no leirfjord.no leka.no leksvik.no lenvik.no leangaviika.no leaŋgaviika.no lesja.no levanger.no lier.no lierne.no lillehammer.no lillesand.no lindesnes.no lindas.no lindås.no lom.no loppa.no lahppi.no láhppi.no lund.no lunner.no luroy.no lurøy.no luster.no lyngdal.no lyngen.no ivgu.no lardal.no lerdal.no lærdal.no lodingen.no lødingen.no lorenskog.no lørenskog.no loten.no løten.no malvik.no masoy.no måsøy.no muosat.no muosát.no mandal.no marker.no marnardal.no masfjorden.no meland.no meldal.no melhus.no meloy.no meløy.no meraker.no meråker.no moareke.no moåreke.no midsund.no midtre-gauldal.no modalen.no modum.no molde.no moskenes.no moss.no mosvik.no malselv.no målselv.no malatvuopmi.no málatvuopmi.no namdalseid.no aejrie.no namsos.no namsskogan.no naamesjevuemie.no nååmesjevuemie.no laakesvuemie.no nannestad.no narvik.no narviika.no naustdal.no nedre-eiker.no nes.akershus.no nes.buskerud.no nesna.no nesodden.no nesseby.no unjarga.no unjárga.no nesset.no nissedal.no nittedal.no nord-aurdal.no nord-fron.no nord-odal.no norddal.no nordkapp.no davvenjarga.no davvenjárga.no nordre-land.no nordreisa.no raisa.no ráisa.no nore-og-uvdal.no notodden.no naroy.no nærøy.no notteroy.no nøtterøy.no odda.no oksnes.no øksnes.no oppdal.no oppegard.no oppegård.no orkdal.no orland.no ørland.no orskog.no ørskog.no orsta.no ørsta.no os.hedmark.no os.hordaland.no osen.no osteroy.no osterøy.no ostre-toten.no østre-toten.no overhalla.no ovre-eiker.no øvre-eiker.no oyer.no øyer.no oygarden.no øygarden.no oystre-slidre.no øystre-slidre.no porsanger.no porsangu.no porsáŋgu.no porsgrunn.no radoy.no radøy.no rakkestad.no rana.no ruovat.no randaberg.no rauma.no rendalen.no rennebu.no rennesoy.no rennesøy.no rindal.no ringebu.no ringerike.no ringsaker.no rissa.no risor.no risør.no roan.no rollag.no rygge.no ralingen.no rælingen.no rodoy.no rødøy.no romskog.no rømskog.no roros.no røros.no rost.no røst.no royken.no røyken.no royrvik.no røyrvik.no rade.no råde.no salangen.no siellak.no saltdal.no salat.no sálát.no sálat.no samnanger.no sande.more-og-romsdal.no sande.møre-og-romsdal.no sande.vestfold.no sandefjord.no sandnes.no sandoy.no sandøy.no sarpsborg.no sauda.no sauherad.no sel.no selbu.no selje.no seljord.no sigdal.no siljan.no sirdal.no skaun.no skedsmo.no ski.no skien.no skiptvet.no skjervoy.no skjervøy.no skierva.no skiervá.no skjak.no skjåk.no skodje.no skanland.no skånland.no skanit.no skánit.no smola.no smøla.no snillfjord.no snasa.no snåsa.no snoasa.no snaase.no snåase.no sogndal.no sokndal.no sola.no solund.no songdalen.no sortland.no spydeberg.no stange.no stavanger.no steigen.no steinkjer.no stjordal.no stjørdal.no stokke.no stor-elvdal.no stord.no stordal.no storfjord.no omasvuotna.no strand.no stranda.no stryn.no sula.no suldal.no sund.no sunndal.no surnadal.no sveio.no svelvik.no sykkylven.no sogne.no søgne.no somna.no sømna.no sondre-land.no søndre-land.no sor-aurdal.no sør-aurdal.no sor-fron.no sør-fron.no sor-odal.no sør-odal.no sor-varanger.no sør-varanger.no matta-varjjat.no mátta-várjjat.no sorfold.no sørfold.no sorreisa.no sørreisa.no sorum.no sørum.no tana.no deatnu.no time.no tingvoll.no tinn.no tjeldsund.no dielddanuorri.no tjome.no tjøme.no tokke.no tolga.no torsken.no tranoy.no tranøy.no tromso.no tromsø.no tromsa.no romsa.no trondheim.no troandin.no trysil.no trana.no træna.no trogstad.no trøgstad.no tvedestrand.no tydal.no tynset.no tysfjord.no divtasvuodna.no divttasvuotna.no tysnes.no tysvar.no tysvær.no tonsberg.no tønsberg.no ullensaker.no ullensvang.no ulvik.no utsira.no vadso.no vadsø.no cahcesuolo.no čáhcesuolo.no vaksdal.no valle.no vang.no vanylven.no vardo.no vardø.no varggat.no várggát.no vefsn.no vaapste.no vega.no vegarshei.no vegårshei.no vennesla.no verdal.no verran.no vestby.no vestnes.no vestre-slidre.no vestre-toten.no vestvagoy.no vestvågøy.no vevelstad.no vik.no vikna.no vindafjord.no volda.no voss.no varoy.no værøy.no vagan.no vågan.no voagat.no vagsoy.no vågsøy.no vaga.no vågå.no valer.ostfold.no våler.østfold.no valer.hedmark.no våler.hedmark.no // np : http://www.mos.com.np/register.html *.np // nr : http://cenpac.net.nr/dns/index.html // Submitted by registry nr biz.nr info.nr gov.nr edu.nr org.nr net.nr com.nr // nu : https://en.wikipedia.org/wiki/.nu nu // nz : https://en.wikipedia.org/wiki/.nz // Submitted by registry nz ac.nz co.nz cri.nz geek.nz gen.nz govt.nz health.nz iwi.nz kiwi.nz maori.nz mil.nz māori.nz net.nz org.nz parliament.nz school.nz // om : https://en.wikipedia.org/wiki/.om om co.om com.om edu.om gov.om med.om museum.om net.om org.om pro.om // onion : https://tools.ietf.org/html/rfc7686 onion // org : https://en.wikipedia.org/wiki/.org org // pa : http://www.nic.pa/ // Some additional second level "domains" resolve directly as hostnames, such as // pannet.pa, so we add a rule for "pa". pa ac.pa gob.pa com.pa org.pa sld.pa edu.pa net.pa ing.pa abo.pa med.pa nom.pa // pe : https://www.nic.pe/InformeFinalComision.pdf pe edu.pe gob.pe nom.pe mil.pe org.pe com.pe net.pe // pf : http://www.gobin.info/domainname/formulaire-pf.pdf pf com.pf org.pf edu.pf // pg : https://en.wikipedia.org/wiki/.pg *.pg // ph : http://www.domains.ph/FAQ2.asp // Submitted by registry ph com.ph net.ph org.ph gov.ph edu.ph ngo.ph mil.ph i.ph // pk : http://pk5.pknic.net.pk/pk5/msgNamepk.PK pk com.pk net.pk edu.pk org.pk fam.pk biz.pk web.pk gov.pk gob.pk gok.pk gon.pk gop.pk gos.pk info.pk // pl http://www.dns.pl/english/index.html // Submitted by registry pl com.pl net.pl org.pl // pl functional domains (http://www.dns.pl/english/index.html) aid.pl agro.pl atm.pl auto.pl biz.pl edu.pl gmina.pl gsm.pl info.pl mail.pl miasta.pl media.pl mil.pl nieruchomosci.pl nom.pl pc.pl powiat.pl priv.pl realestate.pl rel.pl sex.pl shop.pl sklep.pl sos.pl szkola.pl targi.pl tm.pl tourism.pl travel.pl turystyka.pl // Government domains gov.pl ap.gov.pl griw.gov.pl ic.gov.pl is.gov.pl kmpsp.gov.pl konsulat.gov.pl kppsp.gov.pl kwp.gov.pl kwpsp.gov.pl mup.gov.pl mw.gov.pl oia.gov.pl oirm.gov.pl oke.gov.pl oow.gov.pl oschr.gov.pl oum.gov.pl pa.gov.pl pinb.gov.pl piw.gov.pl po.gov.pl pr.gov.pl psp.gov.pl psse.gov.pl pup.gov.pl rzgw.gov.pl sa.gov.pl sdn.gov.pl sko.gov.pl so.gov.pl sr.gov.pl starostwo.gov.pl ug.gov.pl ugim.gov.pl um.gov.pl umig.gov.pl upow.gov.pl uppo.gov.pl us.gov.pl uw.gov.pl uzs.gov.pl wif.gov.pl wiih.gov.pl winb.gov.pl wios.gov.pl witd.gov.pl wiw.gov.pl wkz.gov.pl wsa.gov.pl wskr.gov.pl wsse.gov.pl wuoz.gov.pl wzmiuw.gov.pl zp.gov.pl zpisdn.gov.pl // pl regional domains (http://www.dns.pl/english/index.html) augustow.pl babia-gora.pl bedzin.pl beskidy.pl bialowieza.pl bialystok.pl bielawa.pl bieszczady.pl boleslawiec.pl bydgoszcz.pl bytom.pl cieszyn.pl czeladz.pl czest.pl dlugoleka.pl elblag.pl elk.pl glogow.pl gniezno.pl gorlice.pl grajewo.pl ilawa.pl jaworzno.pl jelenia-gora.pl jgora.pl kalisz.pl kazimierz-dolny.pl karpacz.pl kartuzy.pl kaszuby.pl katowice.pl kepno.pl ketrzyn.pl klodzko.pl kobierzyce.pl kolobrzeg.pl konin.pl konskowola.pl kutno.pl lapy.pl lebork.pl legnica.pl lezajsk.pl limanowa.pl lomza.pl lowicz.pl lubin.pl lukow.pl malbork.pl malopolska.pl mazowsze.pl mazury.pl mielec.pl mielno.pl mragowo.pl naklo.pl nowaruda.pl nysa.pl olawa.pl olecko.pl olkusz.pl olsztyn.pl opoczno.pl opole.pl ostroda.pl ostroleka.pl ostrowiec.pl ostrowwlkp.pl pila.pl pisz.pl podhale.pl podlasie.pl polkowice.pl pomorze.pl pomorskie.pl prochowice.pl pruszkow.pl przeworsk.pl pulawy.pl radom.pl rawa-maz.pl rybnik.pl rzeszow.pl sanok.pl sejny.pl slask.pl slupsk.pl sosnowiec.pl stalowa-wola.pl skoczow.pl starachowice.pl stargard.pl suwalki.pl swidnica.pl swiebodzin.pl swinoujscie.pl szczecin.pl szczytno.pl tarnobrzeg.pl tgory.pl turek.pl tychy.pl ustka.pl walbrzych.pl warmia.pl warszawa.pl waw.pl wegrow.pl wielun.pl wlocl.pl wloclawek.pl wodzislaw.pl wolomin.pl wroclaw.pl zachpomor.pl zagan.pl zarow.pl zgora.pl zgorzelec.pl // pm : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf pm // pn : http://www.government.pn/PnRegistry/policies.htm pn gov.pn co.pn org.pn edu.pn net.pn // post : https://en.wikipedia.org/wiki/.post post // pr : http://www.nic.pr/index.asp?f=1 pr com.pr net.pr org.pr gov.pr edu.pr isla.pr pro.pr biz.pr info.pr name.pr // these aren't mentioned on nic.pr, but on https://en.wikipedia.org/wiki/.pr est.pr prof.pr ac.pr // pro : http://registry.pro/get-pro pro aaa.pro aca.pro acct.pro avocat.pro bar.pro cpa.pro eng.pro jur.pro law.pro med.pro recht.pro // ps : https://en.wikipedia.org/wiki/.ps // http://www.nic.ps/registration/policy.html#reg ps edu.ps gov.ps sec.ps plo.ps com.ps org.ps net.ps // pt : https://www.dns.pt/en/domain/pt-terms-and-conditions-registration-rules/ pt net.pt gov.pt org.pt edu.pt int.pt publ.pt com.pt nome.pt // pw : https://en.wikipedia.org/wiki/.pw pw co.pw ne.pw or.pw ed.pw go.pw belau.pw // py : http://www.nic.py/pautas.html#seccion_9 // Submitted by registry py com.py coop.py edu.py gov.py mil.py net.py org.py // qa : http://domains.qa/en/ qa com.qa edu.qa gov.qa mil.qa name.qa net.qa org.qa sch.qa // re : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf re asso.re com.re nom.re // ro : http://www.rotld.ro/ ro arts.ro com.ro firm.ro info.ro nom.ro nt.ro org.ro rec.ro store.ro tm.ro www.ro // rs : https://www.rnids.rs/en/domains/national-domains rs ac.rs co.rs edu.rs gov.rs in.rs org.rs // ru : https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf // Submitted by George Georgievsky ru // rw : https://www.ricta.org.rw/sites/default/files/resources/registry_registrar_contract_0.pdf rw ac.rw co.rw coop.rw gov.rw mil.rw net.rw org.rw // sa : http://www.nic.net.sa/ sa com.sa net.sa org.sa gov.sa med.sa pub.sa edu.sa sch.sa // sb : http://www.sbnic.net.sb/ // Submitted by registry sb com.sb edu.sb gov.sb net.sb org.sb // sc : http://www.nic.sc/ sc com.sc gov.sc net.sc org.sc edu.sc // sd : http://www.isoc.sd/sudanic.isoc.sd/billing_pricing.htm // Submitted by registry sd com.sd net.sd org.sd edu.sd med.sd tv.sd gov.sd info.sd // se : https://en.wikipedia.org/wiki/.se // Submitted by registry se a.se ac.se b.se bd.se brand.se c.se d.se e.se f.se fh.se fhsk.se fhv.se g.se h.se i.se k.se komforb.se kommunalforbund.se komvux.se l.se lanbib.se m.se n.se naturbruksgymn.se o.se org.se p.se parti.se pp.se press.se r.se s.se t.se tm.se u.se w.se x.se y.se z.se // sg : http://www.nic.net.sg/page/registration-policies-procedures-and-guidelines sg com.sg net.sg org.sg gov.sg edu.sg per.sg // sh : http://nic.sh/rules.htm sh com.sh net.sh gov.sh org.sh mil.sh // si : https://en.wikipedia.org/wiki/.si si // sj : No registrations at this time. // Submitted by registry sj // sk : https://en.wikipedia.org/wiki/.sk // list of 2nd level domains ? sk // sl : http://www.nic.sl // Submitted by registry sl com.sl net.sl edu.sl gov.sl org.sl // sm : https://en.wikipedia.org/wiki/.sm sm // sn : https://en.wikipedia.org/wiki/.sn sn art.sn com.sn edu.sn gouv.sn org.sn perso.sn univ.sn // so : http://sonic.so/policies/ so com.so edu.so gov.so me.so net.so org.so // sr : https://en.wikipedia.org/wiki/.sr sr // ss : https://registry.nic.ss/ // Submitted by registry ss biz.ss com.ss edu.ss gov.ss me.ss net.ss org.ss sch.ss // st : http://www.nic.st/html/policyrules/ st co.st com.st consulado.st edu.st embaixada.st mil.st net.st org.st principe.st saotome.st store.st // su : https://en.wikipedia.org/wiki/.su su // sv : http://www.svnet.org.sv/niveldos.pdf sv com.sv edu.sv gob.sv org.sv red.sv // sx : https://en.wikipedia.org/wiki/.sx // Submitted by registry sx gov.sx // sy : https://en.wikipedia.org/wiki/.sy // see also: http://www.gobin.info/domainname/sy.doc sy edu.sy gov.sy net.sy mil.sy com.sy org.sy // sz : https://en.wikipedia.org/wiki/.sz // http://www.sispa.org.sz/ sz co.sz ac.sz org.sz // tc : https://en.wikipedia.org/wiki/.tc tc // td : https://en.wikipedia.org/wiki/.td td // tel: https://en.wikipedia.org/wiki/.tel // http://www.telnic.org/ tel // tf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf tf // tg : https://en.wikipedia.org/wiki/.tg // http://www.nic.tg/ tg // th : https://en.wikipedia.org/wiki/.th // Submitted by registry th ac.th co.th go.th in.th mi.th net.th or.th // tj : http://www.nic.tj/policy.html tj ac.tj biz.tj co.tj com.tj edu.tj go.tj gov.tj int.tj mil.tj name.tj net.tj nic.tj org.tj test.tj web.tj // tk : https://en.wikipedia.org/wiki/.tk tk // tl : https://en.wikipedia.org/wiki/.tl tl gov.tl // tm : http://www.nic.tm/local.html tm com.tm co.tm org.tm net.tm nom.tm gov.tm mil.tm edu.tm // tn : http://www.registre.tn/fr/ // https://whois.ati.tn/ tn com.tn ens.tn fin.tn gov.tn ind.tn info.tn intl.tn mincom.tn nat.tn net.tn org.tn perso.tn tourism.tn // to : https://en.wikipedia.org/wiki/.to // Submitted by registry to com.to gov.to net.to org.to edu.to mil.to // tr : https://nic.tr/ // https://nic.tr/forms/eng/policies.pdf // https://nic.tr/index.php?USRACTN=PRICELST tr av.tr bbs.tr bel.tr biz.tr com.tr dr.tr edu.tr gen.tr gov.tr info.tr mil.tr k12.tr kep.tr name.tr net.tr org.tr pol.tr tel.tr tsk.tr tv.tr web.tr // Used by Northern Cyprus nc.tr // Used by government agencies of Northern Cyprus gov.nc.tr // tt : http://www.nic.tt/ tt co.tt com.tt org.tt net.tt biz.tt info.tt pro.tt int.tt coop.tt jobs.tt mobi.tt travel.tt museum.tt aero.tt name.tt gov.tt edu.tt // tv : https://en.wikipedia.org/wiki/.tv // Not listing any 2LDs as reserved since none seem to exist in practice, // Wikipedia notwithstanding. tv // tw : https://en.wikipedia.org/wiki/.tw tw edu.tw gov.tw mil.tw com.tw net.tw org.tw idv.tw game.tw ebiz.tw club.tw 網路.tw 組織.tw 商業.tw // tz : http://www.tznic.or.tz/index.php/domains // Submitted by registry tz ac.tz co.tz go.tz hotel.tz info.tz me.tz mil.tz mobi.tz ne.tz or.tz sc.tz tv.tz // ua : https://hostmaster.ua/policy/?ua // Submitted by registry ua // ua 2LD com.ua edu.ua gov.ua in.ua net.ua org.ua // ua geographic names // https://hostmaster.ua/2ld/ cherkassy.ua cherkasy.ua chernigov.ua chernihiv.ua chernivtsi.ua chernovtsy.ua ck.ua cn.ua cr.ua crimea.ua cv.ua dn.ua dnepropetrovsk.ua dnipropetrovsk.ua donetsk.ua dp.ua if.ua ivano-frankivsk.ua kh.ua kharkiv.ua kharkov.ua kherson.ua khmelnitskiy.ua khmelnytskyi.ua kiev.ua kirovograd.ua km.ua kr.ua kropyvnytskyi.ua krym.ua ks.ua kv.ua kyiv.ua lg.ua lt.ua lugansk.ua luhansk.ua lutsk.ua lv.ua lviv.ua mk.ua mykolaiv.ua nikolaev.ua od.ua odesa.ua odessa.ua pl.ua poltava.ua rivne.ua rovno.ua rv.ua sb.ua sebastopol.ua sevastopol.ua sm.ua sumy.ua te.ua ternopil.ua uz.ua uzhgorod.ua uzhhorod.ua vinnica.ua vinnytsia.ua vn.ua volyn.ua yalta.ua zakarpattia.ua zaporizhzhe.ua zaporizhzhia.ua zhitomir.ua zhytomyr.ua zp.ua zt.ua // ug : https://www.registry.co.ug/ ug co.ug or.ug ac.ug sc.ug go.ug ne.ug com.ug org.ug // uk : https://en.wikipedia.org/wiki/.uk // Submitted by registry uk ac.uk co.uk gov.uk ltd.uk me.uk net.uk nhs.uk org.uk plc.uk police.uk *.sch.uk // us : https://en.wikipedia.org/wiki/.us us dni.us fed.us isa.us kids.us nsn.us // us geographic names ak.us al.us ar.us as.us az.us ca.us co.us ct.us dc.us de.us fl.us ga.us gu.us hi.us ia.us id.us il.us in.us ks.us ky.us la.us ma.us md.us me.us mi.us mn.us mo.us ms.us mt.us nc.us nd.us ne.us nh.us nj.us nm.us nv.us ny.us oh.us ok.us or.us pa.us pr.us ri.us sc.us sd.us tn.us tx.us ut.us vi.us vt.us va.us wa.us wi.us wv.us wy.us // The registrar notes several more specific domains available in each state, // such as state.*.us, dst.*.us, etc., but resolution of these is somewhat // haphazard; in some states these domains resolve as addresses, while in others // only subdomains are available, or even nothing at all. We include the // most common ones where it's clear that different sites are different // entities. k12.ak.us k12.al.us k12.ar.us k12.as.us k12.az.us k12.ca.us k12.co.us k12.ct.us k12.dc.us k12.fl.us k12.ga.us k12.gu.us // k12.hi.us Bug 614565 - Hawaii has a state-wide DOE login k12.ia.us k12.id.us k12.il.us k12.in.us k12.ks.us k12.ky.us k12.la.us k12.ma.us k12.md.us k12.me.us k12.mi.us k12.mn.us k12.mo.us k12.ms.us k12.mt.us k12.nc.us // k12.nd.us Bug 1028347 - Removed at request of Travis Rosso k12.ne.us k12.nh.us k12.nj.us k12.nm.us k12.nv.us k12.ny.us k12.oh.us k12.ok.us k12.or.us k12.pa.us k12.pr.us // k12.ri.us Removed at request of Kim Cournoyer k12.sc.us // k12.sd.us Bug 934131 - Removed at request of James Booze k12.tn.us k12.tx.us k12.ut.us k12.vi.us k12.vt.us k12.va.us k12.wa.us k12.wi.us // k12.wv.us Bug 947705 - Removed at request of Verne Britton k12.wy.us cc.ak.us cc.al.us cc.ar.us cc.as.us cc.az.us cc.ca.us cc.co.us cc.ct.us cc.dc.us cc.de.us cc.fl.us cc.ga.us cc.gu.us cc.hi.us cc.ia.us cc.id.us cc.il.us cc.in.us cc.ks.us cc.ky.us cc.la.us cc.ma.us cc.md.us cc.me.us cc.mi.us cc.mn.us cc.mo.us cc.ms.us cc.mt.us cc.nc.us cc.nd.us cc.ne.us cc.nh.us cc.nj.us cc.nm.us cc.nv.us cc.ny.us cc.oh.us cc.ok.us cc.or.us cc.pa.us cc.pr.us cc.ri.us cc.sc.us cc.sd.us cc.tn.us cc.tx.us cc.ut.us cc.vi.us cc.vt.us cc.va.us cc.wa.us cc.wi.us cc.wv.us cc.wy.us lib.ak.us lib.al.us lib.ar.us lib.as.us lib.az.us lib.ca.us lib.co.us lib.ct.us lib.dc.us // lib.de.us Issue #243 - Moved to Private section at request of Ed Moore lib.fl.us lib.ga.us lib.gu.us lib.hi.us lib.ia.us lib.id.us lib.il.us lib.in.us lib.ks.us lib.ky.us lib.la.us lib.ma.us lib.md.us lib.me.us lib.mi.us lib.mn.us lib.mo.us lib.ms.us lib.mt.us lib.nc.us lib.nd.us lib.ne.us lib.nh.us lib.nj.us lib.nm.us lib.nv.us lib.ny.us lib.oh.us lib.ok.us lib.or.us lib.pa.us lib.pr.us lib.ri.us lib.sc.us lib.sd.us lib.tn.us lib.tx.us lib.ut.us lib.vi.us lib.vt.us lib.va.us lib.wa.us lib.wi.us // lib.wv.us Bug 941670 - Removed at request of Larry W Arnold lib.wy.us // k12.ma.us contains school districts in Massachusetts. The 4LDs are // managed independently except for private (PVT), charter (CHTR) and // parochial (PAROCH) schools. Those are delegated directly to the // 5LD operators. pvt.k12.ma.us chtr.k12.ma.us paroch.k12.ma.us // Merit Network, Inc. maintains the registry for =~ /(k12|cc|lib).mi.us/ and the following // see also: http://domreg.merit.edu // see also: whois -h whois.domreg.merit.edu help ann-arbor.mi.us cog.mi.us dst.mi.us eaton.mi.us gen.mi.us mus.mi.us tec.mi.us washtenaw.mi.us // uy : http://www.nic.org.uy/ uy com.uy edu.uy gub.uy mil.uy net.uy org.uy // uz : http://www.reg.uz/ uz co.uz com.uz net.uz org.uz // va : https://en.wikipedia.org/wiki/.va va // vc : https://en.wikipedia.org/wiki/.vc // Submitted by registry vc com.vc net.vc org.vc gov.vc mil.vc edu.vc // ve : https://registro.nic.ve/ // Submitted by registry nic@nic.ve and nicve@conatel.gob.ve ve arts.ve bib.ve co.ve com.ve e12.ve edu.ve firm.ve gob.ve gov.ve info.ve int.ve mil.ve net.ve nom.ve org.ve rar.ve rec.ve store.ve tec.ve web.ve // vg : https://en.wikipedia.org/wiki/.vg vg // vi : http://www.nic.vi/newdomainform.htm // http://www.nic.vi/Domain_Rules/body_domain_rules.html indicates some other // TLDs are "reserved", such as edu.vi and gov.vi, but doesn't actually say they // are available for registration (which they do not seem to be). vi co.vi com.vi k12.vi net.vi org.vi // vn : https://www.vnnic.vn/en/domain/cctld-vn // https://vnnic.vn/sites/default/files/tailieu/vn.cctld.domains.txt vn ac.vn ai.vn biz.vn com.vn edu.vn gov.vn health.vn id.vn info.vn int.vn io.vn name.vn net.vn org.vn pro.vn // vn geographical names angiang.vn bacgiang.vn backan.vn baclieu.vn bacninh.vn baria-vungtau.vn bentre.vn binhdinh.vn binhduong.vn binhphuoc.vn binhthuan.vn camau.vn cantho.vn caobang.vn daklak.vn daknong.vn danang.vn dienbien.vn dongnai.vn dongthap.vn gialai.vn hagiang.vn haiduong.vn haiphong.vn hanam.vn hanoi.vn hatinh.vn haugiang.vn hoabinh.vn hungyen.vn khanhhoa.vn kiengiang.vn kontum.vn laichau.vn lamdong.vn langson.vn laocai.vn longan.vn namdinh.vn nghean.vn ninhbinh.vn ninhthuan.vn phutho.vn phuyen.vn quangbinh.vn quangnam.vn quangngai.vn quangninh.vn quangtri.vn soctrang.vn sonla.vn tayninh.vn thaibinh.vn thainguyen.vn thanhhoa.vn thanhphohochiminh.vn thuathienhue.vn tiengiang.vn travinh.vn tuyenquang.vn vinhlong.vn vinhphuc.vn yenbai.vn // vu : https://en.wikipedia.org/wiki/.vu // http://www.vunic.vu/ vu com.vu edu.vu net.vu org.vu // wf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf wf // ws : https://en.wikipedia.org/wiki/.ws // http://samoanic.ws/index.dhtml ws com.ws net.ws org.ws gov.ws edu.ws // yt : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf yt // IDN ccTLDs // When submitting patches, please maintain a sort by ISO 3166 ccTLD, then // U-label, and follow this format: // // A-Label ("", [, variant info]) : // // [sponsoring org] // U-Label // xn--mgbaam7a8h ("Emerat", Arabic) : AE // http://nic.ae/english/arabicdomain/rules.jsp امارات // xn--y9a3aq ("hye", Armenian) : AM // ISOC AM (operated by .am Registry) հայ // xn--54b7fta0cc ("Bangla", Bangla) : BD বাংলা // xn--90ae ("bg", Bulgarian) : BG бг // xn--mgbcpq6gpa1a ("albahrain", Arabic) : BH البحرين // xn--90ais ("bel", Belarusian/Russian Cyrillic) : BY // Operated by .by registry бел // xn--fiqs8s ("Zhongguo/China", Chinese, Simplified) : CN // CNNIC // http://cnnic.cn/html/Dir/2005/10/11/3218.htm 中国 // xn--fiqz9s ("Zhongguo/China", Chinese, Traditional) : CN // CNNIC // http://cnnic.cn/html/Dir/2005/10/11/3218.htm 中國 // xn--lgbbat1ad8j ("Algeria/Al Jazair", Arabic) : DZ الجزائر // xn--wgbh1c ("Egypt/Masr", Arabic) : EG // http://www.dotmasr.eg/ مصر // xn--e1a4c ("eu", Cyrillic) : EU // https://eurid.eu ею // xn--qxa6a ("eu", Greek) : EU // https://eurid.eu ευ // xn--mgbah1a3hjkrd ("Mauritania", Arabic) : MR موريتانيا // xn--node ("ge", Georgian Mkhedruli) : GE გე // xn--qxam ("el", Greek) : GR // Hellenic Ministry of Infrastructure, Transport, and Networks ελ // xn--j6w193g ("Hong Kong", Chinese) : HK // https://www.hkirc.hk // Submitted by registry // https://www.hkirc.hk/content.jsp?id=30#!/34 香港 公司.香港 教育.香港 政府.香港 個人.香港 網絡.香港 組織.香港 // xn--2scrj9c ("Bharat", Kannada) : IN // India ಭಾರತ // xn--3hcrj9c ("Bharat", Oriya) : IN // India ଭାରତ // xn--45br5cyl ("Bharatam", Assamese) : IN // India ভাৰত // xn--h2breg3eve ("Bharatam", Sanskrit) : IN // India भारतम् // xn--h2brj9c8c ("Bharot", Santali) : IN // India भारोत // xn--mgbgu82a ("Bharat", Sindhi) : IN // India ڀارت // xn--rvc1e0am3e ("Bharatam", Malayalam) : IN // India ഭാരതം // xn--h2brj9c ("Bharat", Devanagari) : IN // India भारत // xn--mgbbh1a ("Bharat", Kashmiri) : IN // India بارت // xn--mgbbh1a71e ("Bharat", Arabic) : IN // India بھارت // xn--fpcrj9c3d ("Bharat", Telugu) : IN // India భారత్ // xn--gecrj9c ("Bharat", Gujarati) : IN // India ભારત // xn--s9brj9c ("Bharat", Gurmukhi) : IN // India ਭਾਰਤ // xn--45brj9c ("Bharat", Bengali) : IN // India ভারত // xn--xkc2dl3a5ee0h ("India", Tamil) : IN // India இந்தியா // xn--mgba3a4f16a ("Iran", Persian) : IR ایران // xn--mgba3a4fra ("Iran", Arabic) : IR ايران // xn--mgbtx2b ("Iraq", Arabic) : IQ // Communications and Media Commission عراق // xn--mgbayh7gpa ("al-Ordon", Arabic) : JO // National Information Technology Center (NITC) // Royal Scientific Society, Al-Jubeiha الاردن // xn--3e0b707e ("Republic of Korea", Hangul) : KR 한국 // xn--80ao21a ("Kaz", Kazakh) : KZ қаз // xn--q7ce6a ("Lao", Lao) : LA ລາວ // xn--fzc2c9e2c ("Lanka", Sinhalese-Sinhala) : LK // https://nic.lk ලංකා // xn--xkc2al3hye2a ("Ilangai", Tamil) : LK // https://nic.lk இலங்கை // xn--mgbc0a9azcg ("Morocco/al-Maghrib", Arabic) : MA المغرب // xn--d1alf ("mkd", Macedonian) : MK // MARnet мкд // xn--l1acc ("mon", Mongolian) : MN мон // xn--mix891f ("Macao", Chinese, Traditional) : MO // MONIC / HNET Asia (Registry Operator for .mo) 澳門 // xn--mix082f ("Macao", Chinese, Simplified) : MO 澳门 // xn--mgbx4cd0ab ("Malaysia", Malay) : MY مليسيا // xn--mgb9awbf ("Oman", Arabic) : OM عمان // xn--mgbai9azgqp6j ("Pakistan", Urdu/Arabic) : PK پاکستان // xn--mgbai9a5eva00b ("Pakistan", Urdu/Arabic, variant) : PK پاكستان // xn--ygbi2ammx ("Falasteen", Arabic) : PS // The Palestinian National Internet Naming Authority (PNINA) // http://www.pnina.ps فلسطين // xn--90a3ac ("srb", Cyrillic) : RS // https://www.rnids.rs/en/domains/national-domains срб пр.срб орг.срб обр.срб од.срб упр.срб ак.срб // xn--p1ai ("rf", Russian-Cyrillic) : RU // https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf // Submitted by George Georgievsky рф // xn--wgbl6a ("Qatar", Arabic) : QA // http://www.ict.gov.qa/ قطر // xn--mgberp4a5d4ar ("AlSaudiah", Arabic) : SA // http://www.nic.net.sa/ السعودية // xn--mgberp4a5d4a87g ("AlSaudiah", Arabic, variant) : SA السعودیة // xn--mgbqly7c0a67fbc ("AlSaudiah", Arabic, variant) : SA السعودیۃ // xn--mgbqly7cvafr ("AlSaudiah", Arabic, variant) : SA السعوديه // xn--mgbpl2fh ("sudan", Arabic) : SD // Operated by .sd registry سودان // xn--yfro4i67o Singapore ("Singapore", Chinese) : SG 新加坡 // xn--clchc0ea0b2g2a9gcd ("Singapore", Tamil) : SG சிங்கப்பூர் // xn--ogbpf8fl ("Syria", Arabic) : SY سورية // xn--mgbtf8fl ("Syria", Arabic, variant) : SY سوريا // xn--o3cw4h ("Thai", Thai) : TH // http://www.thnic.co.th ไทย ศึกษา.ไทย ธุรกิจ.ไทย รัฐบาล.ไทย ทหาร.ไทย เน็ต.ไทย องค์กร.ไทย // xn--pgbs0dh ("Tunisia", Arabic) : TN // http://nic.tn تونس // xn--kpry57d ("Taiwan", Chinese, Traditional) : TW // http://www.twnic.net/english/dn/dn_07a.htm 台灣 // xn--kprw13d ("Taiwan", Chinese, Simplified) : TW // http://www.twnic.net/english/dn/dn_07a.htm 台湾 // xn--nnx388a ("Taiwan", Chinese, variant) : TW 臺灣 // xn--j1amh ("ukr", Cyrillic) : UA укр // xn--mgb2ddes ("AlYemen", Arabic) : YE اليمن // xxx : http://icmregistry.com xxx // ye : http://www.y.net.ye/services/domain_name.htm ye com.ye edu.ye gov.ye net.ye mil.ye org.ye // za : https://www.zadna.org.za/content/page/domain-information/ ac.za agric.za alt.za co.za edu.za gov.za grondar.za law.za mil.za net.za ngo.za nic.za nis.za nom.za org.za school.za tm.za web.za // zm : https://zicta.zm/ // Submitted by registry zm ac.zm biz.zm co.zm com.zm edu.zm gov.zm info.zm mil.zm net.zm org.zm sch.zm // zw : https://www.potraz.gov.zw/ // Confirmed by registry 2017-01-25 zw ac.zw co.zw gov.zw mil.zw org.zw // newGTLDs // List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2024-03-06T15:14:58Z // This list is auto-generated, don't edit it manually. // aaa : American Automobile Association, Inc. // https://www.iana.org/domains/root/db/aaa.html aaa // aarp : AARP // https://www.iana.org/domains/root/db/aarp.html aarp // abb : ABB Ltd // https://www.iana.org/domains/root/db/abb.html abb // abbott : Abbott Laboratories, Inc. // https://www.iana.org/domains/root/db/abbott.html abbott // abbvie : AbbVie Inc. // https://www.iana.org/domains/root/db/abbvie.html abbvie // abc : Disney Enterprises, Inc. // https://www.iana.org/domains/root/db/abc.html abc // able : Able Inc. // https://www.iana.org/domains/root/db/able.html able // abogado : Registry Services, LLC // https://www.iana.org/domains/root/db/abogado.html abogado // abudhabi : Abu Dhabi Systems and Information Centre // https://www.iana.org/domains/root/db/abudhabi.html abudhabi // academy : Binky Moon, LLC // https://www.iana.org/domains/root/db/academy.html academy // accenture : Accenture plc // https://www.iana.org/domains/root/db/accenture.html accenture // accountant : dot Accountant Limited // https://www.iana.org/domains/root/db/accountant.html accountant // accountants : Binky Moon, LLC // https://www.iana.org/domains/root/db/accountants.html accountants // aco : ACO Severin Ahlmann GmbH & Co. KG // https://www.iana.org/domains/root/db/aco.html aco // actor : Dog Beach, LLC // https://www.iana.org/domains/root/db/actor.html actor // ads : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/ads.html ads // adult : ICM Registry AD LLC // https://www.iana.org/domains/root/db/adult.html adult // aeg : Aktiebolaget Electrolux // https://www.iana.org/domains/root/db/aeg.html aeg // aetna : Aetna Life Insurance Company // https://www.iana.org/domains/root/db/aetna.html aetna // afl : Australian Football League // https://www.iana.org/domains/root/db/afl.html afl // africa : ZA Central Registry NPC trading as Registry.Africa // https://www.iana.org/domains/root/db/africa.html africa // agakhan : Fondation Aga Khan (Aga Khan Foundation) // https://www.iana.org/domains/root/db/agakhan.html agakhan // agency : Binky Moon, LLC // https://www.iana.org/domains/root/db/agency.html agency // aig : American International Group, Inc. // https://www.iana.org/domains/root/db/aig.html aig // airbus : Airbus S.A.S. // https://www.iana.org/domains/root/db/airbus.html airbus // airforce : Dog Beach, LLC // https://www.iana.org/domains/root/db/airforce.html airforce // airtel : Bharti Airtel Limited // https://www.iana.org/domains/root/db/airtel.html airtel // akdn : Fondation Aga Khan (Aga Khan Foundation) // https://www.iana.org/domains/root/db/akdn.html akdn // alibaba : Alibaba Group Holding Limited // https://www.iana.org/domains/root/db/alibaba.html alibaba // alipay : Alibaba Group Holding Limited // https://www.iana.org/domains/root/db/alipay.html alipay // allfinanz : Allfinanz Deutsche Vermögensberatung Aktiengesellschaft // https://www.iana.org/domains/root/db/allfinanz.html allfinanz // allstate : Allstate Fire and Casualty Insurance Company // https://www.iana.org/domains/root/db/allstate.html allstate // ally : Ally Financial Inc. // https://www.iana.org/domains/root/db/ally.html ally // alsace : Region Grand Est // https://www.iana.org/domains/root/db/alsace.html alsace // alstom : ALSTOM // https://www.iana.org/domains/root/db/alstom.html alstom // amazon : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/amazon.html amazon // americanexpress : American Express Travel Related Services Company, Inc. // https://www.iana.org/domains/root/db/americanexpress.html americanexpress // americanfamily : AmFam, Inc. // https://www.iana.org/domains/root/db/americanfamily.html americanfamily // amex : American Express Travel Related Services Company, Inc. // https://www.iana.org/domains/root/db/amex.html amex // amfam : AmFam, Inc. // https://www.iana.org/domains/root/db/amfam.html amfam // amica : Amica Mutual Insurance Company // https://www.iana.org/domains/root/db/amica.html amica // amsterdam : Gemeente Amsterdam // https://www.iana.org/domains/root/db/amsterdam.html amsterdam // analytics : Campus IP LLC // https://www.iana.org/domains/root/db/analytics.html analytics // android : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/android.html android // anquan : Beijing Qihu Keji Co., Ltd. // https://www.iana.org/domains/root/db/anquan.html anquan // anz : Australia and New Zealand Banking Group Limited // https://www.iana.org/domains/root/db/anz.html anz // aol : Oath Inc. // https://www.iana.org/domains/root/db/aol.html aol // apartments : Binky Moon, LLC // https://www.iana.org/domains/root/db/apartments.html apartments // app : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/app.html app // apple : Apple Inc. // https://www.iana.org/domains/root/db/apple.html apple // aquarelle : Aquarelle.com // https://www.iana.org/domains/root/db/aquarelle.html aquarelle // arab : League of Arab States // https://www.iana.org/domains/root/db/arab.html arab // aramco : Aramco Services Company // https://www.iana.org/domains/root/db/aramco.html aramco // archi : Identity Digital Limited // https://www.iana.org/domains/root/db/archi.html archi // army : Dog Beach, LLC // https://www.iana.org/domains/root/db/army.html army // art : UK Creative Ideas Limited // https://www.iana.org/domains/root/db/art.html art // arte : Association Relative à la Télévision Européenne G.E.I.E. // https://www.iana.org/domains/root/db/arte.html arte // asda : Wal-Mart Stores, Inc. // https://www.iana.org/domains/root/db/asda.html asda // associates : Binky Moon, LLC // https://www.iana.org/domains/root/db/associates.html associates // athleta : The Gap, Inc. // https://www.iana.org/domains/root/db/athleta.html athleta // attorney : Dog Beach, LLC // https://www.iana.org/domains/root/db/attorney.html attorney // auction : Dog Beach, LLC // https://www.iana.org/domains/root/db/auction.html auction // audi : AUDI Aktiengesellschaft // https://www.iana.org/domains/root/db/audi.html audi // audible : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/audible.html audible // audio : XYZ.COM LLC // https://www.iana.org/domains/root/db/audio.html audio // auspost : Australian Postal Corporation // https://www.iana.org/domains/root/db/auspost.html auspost // author : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/author.html author // auto : XYZ.COM LLC // https://www.iana.org/domains/root/db/auto.html auto // autos : XYZ.COM LLC // https://www.iana.org/domains/root/db/autos.html autos // avianca : Avianca Inc. // https://www.iana.org/domains/root/db/avianca.html avianca // aws : AWS Registry LLC // https://www.iana.org/domains/root/db/aws.html aws // axa : AXA Group Operations SAS // https://www.iana.org/domains/root/db/axa.html axa // azure : Microsoft Corporation // https://www.iana.org/domains/root/db/azure.html azure // baby : XYZ.COM LLC // https://www.iana.org/domains/root/db/baby.html baby // baidu : Baidu, Inc. // https://www.iana.org/domains/root/db/baidu.html baidu // banamex : Citigroup Inc. // https://www.iana.org/domains/root/db/banamex.html banamex // band : Dog Beach, LLC // https://www.iana.org/domains/root/db/band.html band // bank : fTLD Registry Services LLC // https://www.iana.org/domains/root/db/bank.html bank // bar : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable // https://www.iana.org/domains/root/db/bar.html bar // barcelona : Municipi de Barcelona // https://www.iana.org/domains/root/db/barcelona.html barcelona // barclaycard : Barclays Bank PLC // https://www.iana.org/domains/root/db/barclaycard.html barclaycard // barclays : Barclays Bank PLC // https://www.iana.org/domains/root/db/barclays.html barclays // barefoot : Gallo Vineyards, Inc. // https://www.iana.org/domains/root/db/barefoot.html barefoot // bargains : Binky Moon, LLC // https://www.iana.org/domains/root/db/bargains.html bargains // baseball : MLB Advanced Media DH, LLC // https://www.iana.org/domains/root/db/baseball.html baseball // basketball : Fédération Internationale de Basketball (FIBA) // https://www.iana.org/domains/root/db/basketball.html basketball // bauhaus : Werkhaus GmbH // https://www.iana.org/domains/root/db/bauhaus.html bauhaus // bayern : Bayern Connect GmbH // https://www.iana.org/domains/root/db/bayern.html bayern // bbc : British Broadcasting Corporation // https://www.iana.org/domains/root/db/bbc.html bbc // bbt : BB&T Corporation // https://www.iana.org/domains/root/db/bbt.html bbt // bbva : BANCO BILBAO VIZCAYA ARGENTARIA, S.A. // https://www.iana.org/domains/root/db/bbva.html bbva // bcg : The Boston Consulting Group, Inc. // https://www.iana.org/domains/root/db/bcg.html bcg // bcn : Municipi de Barcelona // https://www.iana.org/domains/root/db/bcn.html bcn // beats : Beats Electronics, LLC // https://www.iana.org/domains/root/db/beats.html beats // beauty : XYZ.COM LLC // https://www.iana.org/domains/root/db/beauty.html beauty // beer : Registry Services, LLC // https://www.iana.org/domains/root/db/beer.html beer // bentley : Bentley Motors Limited // https://www.iana.org/domains/root/db/bentley.html bentley // berlin : dotBERLIN GmbH & Co. KG // https://www.iana.org/domains/root/db/berlin.html berlin // best : BestTLD Pty Ltd // https://www.iana.org/domains/root/db/best.html best // bestbuy : BBY Solutions, Inc. // https://www.iana.org/domains/root/db/bestbuy.html bestbuy // bet : Identity Digital Limited // https://www.iana.org/domains/root/db/bet.html bet // bharti : Bharti Enterprises (Holding) Private Limited // https://www.iana.org/domains/root/db/bharti.html bharti // bible : American Bible Society // https://www.iana.org/domains/root/db/bible.html bible // bid : dot Bid Limited // https://www.iana.org/domains/root/db/bid.html bid // bike : Binky Moon, LLC // https://www.iana.org/domains/root/db/bike.html bike // bing : Microsoft Corporation // https://www.iana.org/domains/root/db/bing.html bing // bingo : Binky Moon, LLC // https://www.iana.org/domains/root/db/bingo.html bingo // bio : Identity Digital Limited // https://www.iana.org/domains/root/db/bio.html bio // black : Identity Digital Limited // https://www.iana.org/domains/root/db/black.html black // blackfriday : Registry Services, LLC // https://www.iana.org/domains/root/db/blackfriday.html blackfriday // blockbuster : Dish DBS Corporation // https://www.iana.org/domains/root/db/blockbuster.html blockbuster // blog : Knock Knock WHOIS There, LLC // https://www.iana.org/domains/root/db/blog.html blog // bloomberg : Bloomberg IP Holdings LLC // https://www.iana.org/domains/root/db/bloomberg.html bloomberg // blue : Identity Digital Limited // https://www.iana.org/domains/root/db/blue.html blue // bms : Bristol-Myers Squibb Company // https://www.iana.org/domains/root/db/bms.html bms // bmw : Bayerische Motoren Werke Aktiengesellschaft // https://www.iana.org/domains/root/db/bmw.html bmw // bnpparibas : BNP Paribas // https://www.iana.org/domains/root/db/bnpparibas.html bnpparibas // boats : XYZ.COM LLC // https://www.iana.org/domains/root/db/boats.html boats // boehringer : Boehringer Ingelheim International GmbH // https://www.iana.org/domains/root/db/boehringer.html boehringer // bofa : Bank of America Corporation // https://www.iana.org/domains/root/db/bofa.html bofa // bom : Núcleo de Informação e Coordenação do Ponto BR - NIC.br // https://www.iana.org/domains/root/db/bom.html bom // bond : ShortDot SA // https://www.iana.org/domains/root/db/bond.html bond // boo : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/boo.html boo // book : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/book.html book // booking : Booking.com B.V. // https://www.iana.org/domains/root/db/booking.html booking // bosch : Robert Bosch GMBH // https://www.iana.org/domains/root/db/bosch.html bosch // bostik : Bostik SA // https://www.iana.org/domains/root/db/bostik.html bostik // boston : Registry Services, LLC // https://www.iana.org/domains/root/db/boston.html boston // bot : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/bot.html bot // boutique : Binky Moon, LLC // https://www.iana.org/domains/root/db/boutique.html boutique // box : Intercap Registry Inc. // https://www.iana.org/domains/root/db/box.html box // bradesco : Banco Bradesco S.A. // https://www.iana.org/domains/root/db/bradesco.html bradesco // bridgestone : Bridgestone Corporation // https://www.iana.org/domains/root/db/bridgestone.html bridgestone // broadway : Celebrate Broadway, Inc. // https://www.iana.org/domains/root/db/broadway.html broadway // broker : Dog Beach, LLC // https://www.iana.org/domains/root/db/broker.html broker // brother : Brother Industries, Ltd. // https://www.iana.org/domains/root/db/brother.html brother // brussels : DNS.be vzw // https://www.iana.org/domains/root/db/brussels.html brussels // build : Plan Bee LLC // https://www.iana.org/domains/root/db/build.html build // builders : Binky Moon, LLC // https://www.iana.org/domains/root/db/builders.html builders // business : Binky Moon, LLC // https://www.iana.org/domains/root/db/business.html business // buy : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/buy.html buy // buzz : DOTSTRATEGY CO. // https://www.iana.org/domains/root/db/buzz.html buzz // bzh : Association www.bzh // https://www.iana.org/domains/root/db/bzh.html bzh // cab : Binky Moon, LLC // https://www.iana.org/domains/root/db/cab.html cab // cafe : Binky Moon, LLC // https://www.iana.org/domains/root/db/cafe.html cafe // cal : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/cal.html cal // call : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/call.html call // calvinklein : PVH gTLD Holdings LLC // https://www.iana.org/domains/root/db/calvinklein.html calvinklein // cam : Cam Connecting SARL // https://www.iana.org/domains/root/db/cam.html cam // camera : Binky Moon, LLC // https://www.iana.org/domains/root/db/camera.html camera // camp : Binky Moon, LLC // https://www.iana.org/domains/root/db/camp.html camp // canon : Canon Inc. // https://www.iana.org/domains/root/db/canon.html canon // capetown : ZA Central Registry NPC trading as ZA Central Registry // https://www.iana.org/domains/root/db/capetown.html capetown // capital : Binky Moon, LLC // https://www.iana.org/domains/root/db/capital.html capital // capitalone : Capital One Financial Corporation // https://www.iana.org/domains/root/db/capitalone.html capitalone // car : XYZ.COM LLC // https://www.iana.org/domains/root/db/car.html car // caravan : Caravan International, Inc. // https://www.iana.org/domains/root/db/caravan.html caravan // cards : Binky Moon, LLC // https://www.iana.org/domains/root/db/cards.html cards // care : Binky Moon, LLC // https://www.iana.org/domains/root/db/care.html care // career : dotCareer LLC // https://www.iana.org/domains/root/db/career.html career // careers : Binky Moon, LLC // https://www.iana.org/domains/root/db/careers.html careers // cars : XYZ.COM LLC // https://www.iana.org/domains/root/db/cars.html cars // casa : Registry Services, LLC // https://www.iana.org/domains/root/db/casa.html casa // case : Digity, LLC // https://www.iana.org/domains/root/db/case.html case // cash : Binky Moon, LLC // https://www.iana.org/domains/root/db/cash.html cash // casino : Binky Moon, LLC // https://www.iana.org/domains/root/db/casino.html casino // catering : Binky Moon, LLC // https://www.iana.org/domains/root/db/catering.html catering // catholic : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) // https://www.iana.org/domains/root/db/catholic.html catholic // cba : COMMONWEALTH BANK OF AUSTRALIA // https://www.iana.org/domains/root/db/cba.html cba // cbn : The Christian Broadcasting Network, Inc. // https://www.iana.org/domains/root/db/cbn.html cbn // cbre : CBRE, Inc. // https://www.iana.org/domains/root/db/cbre.html cbre // center : Binky Moon, LLC // https://www.iana.org/domains/root/db/center.html center // ceo : XYZ.COM LLC // https://www.iana.org/domains/root/db/ceo.html ceo // cern : European Organization for Nuclear Research ("CERN") // https://www.iana.org/domains/root/db/cern.html cern // cfa : CFA Institute // https://www.iana.org/domains/root/db/cfa.html cfa // cfd : ShortDot SA // https://www.iana.org/domains/root/db/cfd.html cfd // chanel : Chanel International B.V. // https://www.iana.org/domains/root/db/chanel.html chanel // channel : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/channel.html channel // charity : Public Interest Registry // https://www.iana.org/domains/root/db/charity.html charity // chase : JPMorgan Chase Bank, National Association // https://www.iana.org/domains/root/db/chase.html chase // chat : Binky Moon, LLC // https://www.iana.org/domains/root/db/chat.html chat // cheap : Binky Moon, LLC // https://www.iana.org/domains/root/db/cheap.html cheap // chintai : CHINTAI Corporation // https://www.iana.org/domains/root/db/chintai.html chintai // christmas : XYZ.COM LLC // https://www.iana.org/domains/root/db/christmas.html christmas // chrome : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/chrome.html chrome // church : Binky Moon, LLC // https://www.iana.org/domains/root/db/church.html church // cipriani : Hotel Cipriani Srl // https://www.iana.org/domains/root/db/cipriani.html cipriani // circle : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/circle.html circle // cisco : Cisco Technology, Inc. // https://www.iana.org/domains/root/db/cisco.html cisco // citadel : Citadel Domain LLC // https://www.iana.org/domains/root/db/citadel.html citadel // citi : Citigroup Inc. // https://www.iana.org/domains/root/db/citi.html citi // citic : CITIC Group Corporation // https://www.iana.org/domains/root/db/citic.html citic // city : Binky Moon, LLC // https://www.iana.org/domains/root/db/city.html city // claims : Binky Moon, LLC // https://www.iana.org/domains/root/db/claims.html claims // cleaning : Binky Moon, LLC // https://www.iana.org/domains/root/db/cleaning.html cleaning // click : Internet Naming Company LLC // https://www.iana.org/domains/root/db/click.html click // clinic : Binky Moon, LLC // https://www.iana.org/domains/root/db/clinic.html clinic // clinique : The Estée Lauder Companies Inc. // https://www.iana.org/domains/root/db/clinique.html clinique // clothing : Binky Moon, LLC // https://www.iana.org/domains/root/db/clothing.html clothing // cloud : Aruba PEC S.p.A. // https://www.iana.org/domains/root/db/cloud.html cloud // club : Registry Services, LLC // https://www.iana.org/domains/root/db/club.html club // clubmed : Club Méditerranée S.A. // https://www.iana.org/domains/root/db/clubmed.html clubmed // coach : Binky Moon, LLC // https://www.iana.org/domains/root/db/coach.html coach // codes : Binky Moon, LLC // https://www.iana.org/domains/root/db/codes.html codes // coffee : Binky Moon, LLC // https://www.iana.org/domains/root/db/coffee.html coffee // college : XYZ.COM LLC // https://www.iana.org/domains/root/db/college.html college // cologne : dotKoeln GmbH // https://www.iana.org/domains/root/db/cologne.html cologne // commbank : COMMONWEALTH BANK OF AUSTRALIA // https://www.iana.org/domains/root/db/commbank.html commbank // community : Binky Moon, LLC // https://www.iana.org/domains/root/db/community.html community // company : Binky Moon, LLC // https://www.iana.org/domains/root/db/company.html company // compare : Registry Services, LLC // https://www.iana.org/domains/root/db/compare.html compare // computer : Binky Moon, LLC // https://www.iana.org/domains/root/db/computer.html computer // comsec : VeriSign, Inc. // https://www.iana.org/domains/root/db/comsec.html comsec // condos : Binky Moon, LLC // https://www.iana.org/domains/root/db/condos.html condos // construction : Binky Moon, LLC // https://www.iana.org/domains/root/db/construction.html construction // consulting : Dog Beach, LLC // https://www.iana.org/domains/root/db/consulting.html consulting // contact : Dog Beach, LLC // https://www.iana.org/domains/root/db/contact.html contact // contractors : Binky Moon, LLC // https://www.iana.org/domains/root/db/contractors.html contractors // cooking : Registry Services, LLC // https://www.iana.org/domains/root/db/cooking.html cooking // cool : Binky Moon, LLC // https://www.iana.org/domains/root/db/cool.html cool // corsica : Collectivité de Corse // https://www.iana.org/domains/root/db/corsica.html corsica // country : Internet Naming Company LLC // https://www.iana.org/domains/root/db/country.html country // coupon : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/coupon.html coupon // coupons : Binky Moon, LLC // https://www.iana.org/domains/root/db/coupons.html coupons // courses : Registry Services, LLC // https://www.iana.org/domains/root/db/courses.html courses // cpa : American Institute of Certified Public Accountants // https://www.iana.org/domains/root/db/cpa.html cpa // credit : Binky Moon, LLC // https://www.iana.org/domains/root/db/credit.html credit // creditcard : Binky Moon, LLC // https://www.iana.org/domains/root/db/creditcard.html creditcard // creditunion : DotCooperation LLC // https://www.iana.org/domains/root/db/creditunion.html creditunion // cricket : dot Cricket Limited // https://www.iana.org/domains/root/db/cricket.html cricket // crown : Crown Equipment Corporation // https://www.iana.org/domains/root/db/crown.html crown // crs : Federated Co-operatives Limited // https://www.iana.org/domains/root/db/crs.html crs // cruise : Viking River Cruises (Bermuda) Ltd. // https://www.iana.org/domains/root/db/cruise.html cruise // cruises : Binky Moon, LLC // https://www.iana.org/domains/root/db/cruises.html cruises // cuisinella : SCHMIDT GROUPE S.A.S. // https://www.iana.org/domains/root/db/cuisinella.html cuisinella // cymru : Nominet UK // https://www.iana.org/domains/root/db/cymru.html cymru // cyou : ShortDot SA // https://www.iana.org/domains/root/db/cyou.html cyou // dabur : Dabur India Limited // https://www.iana.org/domains/root/db/dabur.html dabur // dad : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/dad.html dad // dance : Dog Beach, LLC // https://www.iana.org/domains/root/db/dance.html dance // data : Dish DBS Corporation // https://www.iana.org/domains/root/db/data.html data // date : dot Date Limited // https://www.iana.org/domains/root/db/date.html date // dating : Binky Moon, LLC // https://www.iana.org/domains/root/db/dating.html dating // datsun : NISSAN MOTOR CO., LTD. // https://www.iana.org/domains/root/db/datsun.html datsun // day : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/day.html day // dclk : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/dclk.html dclk // dds : Registry Services, LLC // https://www.iana.org/domains/root/db/dds.html dds // deal : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/deal.html deal // dealer : Intercap Registry Inc. // https://www.iana.org/domains/root/db/dealer.html dealer // deals : Binky Moon, LLC // https://www.iana.org/domains/root/db/deals.html deals // degree : Dog Beach, LLC // https://www.iana.org/domains/root/db/degree.html degree // delivery : Binky Moon, LLC // https://www.iana.org/domains/root/db/delivery.html delivery // dell : Dell Inc. // https://www.iana.org/domains/root/db/dell.html dell // deloitte : Deloitte Touche Tohmatsu // https://www.iana.org/domains/root/db/deloitte.html deloitte // delta : Delta Air Lines, Inc. // https://www.iana.org/domains/root/db/delta.html delta // democrat : Dog Beach, LLC // https://www.iana.org/domains/root/db/democrat.html democrat // dental : Binky Moon, LLC // https://www.iana.org/domains/root/db/dental.html dental // dentist : Dog Beach, LLC // https://www.iana.org/domains/root/db/dentist.html dentist // desi // https://www.iana.org/domains/root/db/desi.html desi // design : Registry Services, LLC // https://www.iana.org/domains/root/db/design.html design // dev : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/dev.html dev // dhl : Deutsche Post AG // https://www.iana.org/domains/root/db/dhl.html dhl // diamonds : Binky Moon, LLC // https://www.iana.org/domains/root/db/diamonds.html diamonds // diet : XYZ.COM LLC // https://www.iana.org/domains/root/db/diet.html diet // digital : Binky Moon, LLC // https://www.iana.org/domains/root/db/digital.html digital // direct : Binky Moon, LLC // https://www.iana.org/domains/root/db/direct.html direct // directory : Binky Moon, LLC // https://www.iana.org/domains/root/db/directory.html directory // discount : Binky Moon, LLC // https://www.iana.org/domains/root/db/discount.html discount // discover : Discover Financial Services // https://www.iana.org/domains/root/db/discover.html discover // dish : Dish DBS Corporation // https://www.iana.org/domains/root/db/dish.html dish // diy : Internet Naming Company LLC // https://www.iana.org/domains/root/db/diy.html diy // dnp : Dai Nippon Printing Co., Ltd. // https://www.iana.org/domains/root/db/dnp.html dnp // docs : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/docs.html docs // doctor : Binky Moon, LLC // https://www.iana.org/domains/root/db/doctor.html doctor // dog : Binky Moon, LLC // https://www.iana.org/domains/root/db/dog.html dog // domains : Binky Moon, LLC // https://www.iana.org/domains/root/db/domains.html domains // dot : Dish DBS Corporation // https://www.iana.org/domains/root/db/dot.html dot // download : dot Support Limited // https://www.iana.org/domains/root/db/download.html download // drive : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/drive.html drive // dtv : Dish DBS Corporation // https://www.iana.org/domains/root/db/dtv.html dtv // dubai : Dubai Smart Government Department // https://www.iana.org/domains/root/db/dubai.html dubai // dunlop : The Goodyear Tire & Rubber Company // https://www.iana.org/domains/root/db/dunlop.html dunlop // dupont : DuPont Specialty Products USA, LLC // https://www.iana.org/domains/root/db/dupont.html dupont // durban : ZA Central Registry NPC trading as ZA Central Registry // https://www.iana.org/domains/root/db/durban.html durban // dvag : Deutsche Vermögensberatung Aktiengesellschaft DVAG // https://www.iana.org/domains/root/db/dvag.html dvag // dvr : DISH Technologies L.L.C. // https://www.iana.org/domains/root/db/dvr.html dvr // earth : Interlink Systems Innovation Institute K.K. // https://www.iana.org/domains/root/db/earth.html earth // eat : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/eat.html eat // eco : Big Room Inc. // https://www.iana.org/domains/root/db/eco.html eco // edeka : EDEKA Verband kaufmännischer Genossenschaften e.V. // https://www.iana.org/domains/root/db/edeka.html edeka // education : Binky Moon, LLC // https://www.iana.org/domains/root/db/education.html education // email : Binky Moon, LLC // https://www.iana.org/domains/root/db/email.html email // emerck : Merck KGaA // https://www.iana.org/domains/root/db/emerck.html emerck // energy : Binky Moon, LLC // https://www.iana.org/domains/root/db/energy.html energy // engineer : Dog Beach, LLC // https://www.iana.org/domains/root/db/engineer.html engineer // engineering : Binky Moon, LLC // https://www.iana.org/domains/root/db/engineering.html engineering // enterprises : Binky Moon, LLC // https://www.iana.org/domains/root/db/enterprises.html enterprises // epson : Seiko Epson Corporation // https://www.iana.org/domains/root/db/epson.html epson // equipment : Binky Moon, LLC // https://www.iana.org/domains/root/db/equipment.html equipment // ericsson : Telefonaktiebolaget L M Ericsson // https://www.iana.org/domains/root/db/ericsson.html ericsson // erni : ERNI Group Holding AG // https://www.iana.org/domains/root/db/erni.html erni // esq : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/esq.html esq // estate : Binky Moon, LLC // https://www.iana.org/domains/root/db/estate.html estate // eurovision : European Broadcasting Union (EBU) // https://www.iana.org/domains/root/db/eurovision.html eurovision // eus : Puntueus Fundazioa // https://www.iana.org/domains/root/db/eus.html eus // events : Binky Moon, LLC // https://www.iana.org/domains/root/db/events.html events // exchange : Binky Moon, LLC // https://www.iana.org/domains/root/db/exchange.html exchange // expert : Binky Moon, LLC // https://www.iana.org/domains/root/db/expert.html expert // exposed : Binky Moon, LLC // https://www.iana.org/domains/root/db/exposed.html exposed // express : Binky Moon, LLC // https://www.iana.org/domains/root/db/express.html express // extraspace : Extra Space Storage LLC // https://www.iana.org/domains/root/db/extraspace.html extraspace // fage : Fage International S.A. // https://www.iana.org/domains/root/db/fage.html fage // fail : Binky Moon, LLC // https://www.iana.org/domains/root/db/fail.html fail // fairwinds : FairWinds Partners, LLC // https://www.iana.org/domains/root/db/fairwinds.html fairwinds // faith : dot Faith Limited // https://www.iana.org/domains/root/db/faith.html faith // family : Dog Beach, LLC // https://www.iana.org/domains/root/db/family.html family // fan : Dog Beach, LLC // https://www.iana.org/domains/root/db/fan.html fan // fans : ZDNS International Limited // https://www.iana.org/domains/root/db/fans.html fans // farm : Binky Moon, LLC // https://www.iana.org/domains/root/db/farm.html farm // farmers : Farmers Insurance Exchange // https://www.iana.org/domains/root/db/farmers.html farmers // fashion : Registry Services, LLC // https://www.iana.org/domains/root/db/fashion.html fashion // fast : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/fast.html fast // fedex : Federal Express Corporation // https://www.iana.org/domains/root/db/fedex.html fedex // feedback : Top Level Spectrum, Inc. // https://www.iana.org/domains/root/db/feedback.html feedback // ferrari : Fiat Chrysler Automobiles N.V. // https://www.iana.org/domains/root/db/ferrari.html ferrari // ferrero : Ferrero Trading Lux S.A. // https://www.iana.org/domains/root/db/ferrero.html ferrero // fidelity : Fidelity Brokerage Services LLC // https://www.iana.org/domains/root/db/fidelity.html fidelity // fido : Rogers Communications Canada Inc. // https://www.iana.org/domains/root/db/fido.html fido // film : Motion Picture Domain Registry Pty Ltd // https://www.iana.org/domains/root/db/film.html film // final : Núcleo de Informação e Coordenação do Ponto BR - NIC.br // https://www.iana.org/domains/root/db/final.html final // finance : Binky Moon, LLC // https://www.iana.org/domains/root/db/finance.html finance // financial : Binky Moon, LLC // https://www.iana.org/domains/root/db/financial.html financial // fire : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/fire.html fire // firestone : Bridgestone Licensing Services, Inc // https://www.iana.org/domains/root/db/firestone.html firestone // firmdale : Firmdale Holdings Limited // https://www.iana.org/domains/root/db/firmdale.html firmdale // fish : Binky Moon, LLC // https://www.iana.org/domains/root/db/fish.html fish // fishing : Registry Services, LLC // https://www.iana.org/domains/root/db/fishing.html fishing // fit : Registry Services, LLC // https://www.iana.org/domains/root/db/fit.html fit // fitness : Binky Moon, LLC // https://www.iana.org/domains/root/db/fitness.html fitness // flickr : Flickr, Inc. // https://www.iana.org/domains/root/db/flickr.html flickr // flights : Binky Moon, LLC // https://www.iana.org/domains/root/db/flights.html flights // flir : FLIR Systems, Inc. // https://www.iana.org/domains/root/db/flir.html flir // florist : Binky Moon, LLC // https://www.iana.org/domains/root/db/florist.html florist // flowers : XYZ.COM LLC // https://www.iana.org/domains/root/db/flowers.html flowers // fly : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/fly.html fly // foo : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/foo.html foo // food : Internet Naming Company LLC // https://www.iana.org/domains/root/db/food.html food // football : Binky Moon, LLC // https://www.iana.org/domains/root/db/football.html football // ford : Ford Motor Company // https://www.iana.org/domains/root/db/ford.html ford // forex : Dog Beach, LLC // https://www.iana.org/domains/root/db/forex.html forex // forsale : Dog Beach, LLC // https://www.iana.org/domains/root/db/forsale.html forsale // forum : Fegistry, LLC // https://www.iana.org/domains/root/db/forum.html forum // foundation : Public Interest Registry // https://www.iana.org/domains/root/db/foundation.html foundation // fox : FOX Registry, LLC // https://www.iana.org/domains/root/db/fox.html fox // free : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/free.html free // fresenius : Fresenius Immobilien-Verwaltungs-GmbH // https://www.iana.org/domains/root/db/fresenius.html fresenius // frl : FRLregistry B.V. // https://www.iana.org/domains/root/db/frl.html frl // frogans : OP3FT // https://www.iana.org/domains/root/db/frogans.html frogans // frontier : Frontier Communications Corporation // https://www.iana.org/domains/root/db/frontier.html frontier // ftr : Frontier Communications Corporation // https://www.iana.org/domains/root/db/ftr.html ftr // fujitsu : Fujitsu Limited // https://www.iana.org/domains/root/db/fujitsu.html fujitsu // fun : Radix Technologies Inc. // https://www.iana.org/domains/root/db/fun.html fun // fund : Binky Moon, LLC // https://www.iana.org/domains/root/db/fund.html fund // furniture : Binky Moon, LLC // https://www.iana.org/domains/root/db/furniture.html furniture // futbol : Dog Beach, LLC // https://www.iana.org/domains/root/db/futbol.html futbol // fyi : Binky Moon, LLC // https://www.iana.org/domains/root/db/fyi.html fyi // gal : Asociación puntoGAL // https://www.iana.org/domains/root/db/gal.html gal // gallery : Binky Moon, LLC // https://www.iana.org/domains/root/db/gallery.html gallery // gallo : Gallo Vineyards, Inc. // https://www.iana.org/domains/root/db/gallo.html gallo // gallup : Gallup, Inc. // https://www.iana.org/domains/root/db/gallup.html gallup // game : XYZ.COM LLC // https://www.iana.org/domains/root/db/game.html game // games : Dog Beach, LLC // https://www.iana.org/domains/root/db/games.html games // gap : The Gap, Inc. // https://www.iana.org/domains/root/db/gap.html gap // garden : Registry Services, LLC // https://www.iana.org/domains/root/db/garden.html garden // gay : Registry Services, LLC // https://www.iana.org/domains/root/db/gay.html gay // gbiz : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/gbiz.html gbiz // gdn : Joint Stock Company "Navigation-information systems" // https://www.iana.org/domains/root/db/gdn.html gdn // gea : GEA Group Aktiengesellschaft // https://www.iana.org/domains/root/db/gea.html gea // gent : Easyhost BV // https://www.iana.org/domains/root/db/gent.html gent // genting : Resorts World Inc Pte. Ltd. // https://www.iana.org/domains/root/db/genting.html genting // george : Wal-Mart Stores, Inc. // https://www.iana.org/domains/root/db/george.html george // ggee : GMO Internet, Inc. // https://www.iana.org/domains/root/db/ggee.html ggee // gift : DotGift, LLC // https://www.iana.org/domains/root/db/gift.html gift // gifts : Binky Moon, LLC // https://www.iana.org/domains/root/db/gifts.html gifts // gives : Public Interest Registry // https://www.iana.org/domains/root/db/gives.html gives // giving : Public Interest Registry // https://www.iana.org/domains/root/db/giving.html giving // glass : Binky Moon, LLC // https://www.iana.org/domains/root/db/glass.html glass // gle : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/gle.html gle // global : Identity Digital Limited // https://www.iana.org/domains/root/db/global.html global // globo : Globo Comunicação e Participações S.A // https://www.iana.org/domains/root/db/globo.html globo // gmail : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/gmail.html gmail // gmbh : Binky Moon, LLC // https://www.iana.org/domains/root/db/gmbh.html gmbh // gmo : GMO Internet, Inc. // https://www.iana.org/domains/root/db/gmo.html gmo // gmx : 1&1 Mail & Media GmbH // https://www.iana.org/domains/root/db/gmx.html gmx // godaddy : Go Daddy East, LLC // https://www.iana.org/domains/root/db/godaddy.html godaddy // gold : Binky Moon, LLC // https://www.iana.org/domains/root/db/gold.html gold // goldpoint : YODOBASHI CAMERA CO.,LTD. // https://www.iana.org/domains/root/db/goldpoint.html goldpoint // golf : Binky Moon, LLC // https://www.iana.org/domains/root/db/golf.html golf // goo : NTT DOCOMO, INC. // https://www.iana.org/domains/root/db/goo.html goo // goodyear : The Goodyear Tire & Rubber Company // https://www.iana.org/domains/root/db/goodyear.html goodyear // goog : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/goog.html goog // google : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/google.html google // gop : Republican State Leadership Committee, Inc. // https://www.iana.org/domains/root/db/gop.html gop // got : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/got.html got // grainger : Grainger Registry Services, LLC // https://www.iana.org/domains/root/db/grainger.html grainger // graphics : Binky Moon, LLC // https://www.iana.org/domains/root/db/graphics.html graphics // gratis : Binky Moon, LLC // https://www.iana.org/domains/root/db/gratis.html gratis // green : Identity Digital Limited // https://www.iana.org/domains/root/db/green.html green // gripe : Binky Moon, LLC // https://www.iana.org/domains/root/db/gripe.html gripe // grocery : Wal-Mart Stores, Inc. // https://www.iana.org/domains/root/db/grocery.html grocery // group : Binky Moon, LLC // https://www.iana.org/domains/root/db/group.html group // gucci : Guccio Gucci S.p.a. // https://www.iana.org/domains/root/db/gucci.html gucci // guge : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/guge.html guge // guide : Binky Moon, LLC // https://www.iana.org/domains/root/db/guide.html guide // guitars : XYZ.COM LLC // https://www.iana.org/domains/root/db/guitars.html guitars // guru : Binky Moon, LLC // https://www.iana.org/domains/root/db/guru.html guru // hair : XYZ.COM LLC // https://www.iana.org/domains/root/db/hair.html hair // hamburg : Hamburg Top-Level-Domain GmbH // https://www.iana.org/domains/root/db/hamburg.html hamburg // hangout : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/hangout.html hangout // haus : Dog Beach, LLC // https://www.iana.org/domains/root/db/haus.html haus // hbo : HBO Registry Services, Inc. // https://www.iana.org/domains/root/db/hbo.html hbo // hdfc : HOUSING DEVELOPMENT FINANCE CORPORATION LIMITED // https://www.iana.org/domains/root/db/hdfc.html hdfc // hdfcbank : HDFC Bank Limited // https://www.iana.org/domains/root/db/hdfcbank.html hdfcbank // health : Registry Services, LLC // https://www.iana.org/domains/root/db/health.html health // healthcare : Binky Moon, LLC // https://www.iana.org/domains/root/db/healthcare.html healthcare // help : Innovation service Limited // https://www.iana.org/domains/root/db/help.html help // helsinki : City of Helsinki // https://www.iana.org/domains/root/db/helsinki.html helsinki // here : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/here.html here // hermes : HERMES INTERNATIONAL // https://www.iana.org/domains/root/db/hermes.html hermes // hiphop : Dot Hip Hop, LLC // https://www.iana.org/domains/root/db/hiphop.html hiphop // hisamitsu : Hisamitsu Pharmaceutical Co.,Inc. // https://www.iana.org/domains/root/db/hisamitsu.html hisamitsu // hitachi : Hitachi, Ltd. // https://www.iana.org/domains/root/db/hitachi.html hitachi // hiv : Internet Naming Company LLC // https://www.iana.org/domains/root/db/hiv.html hiv // hkt : PCCW-HKT DataCom Services Limited // https://www.iana.org/domains/root/db/hkt.html hkt // hockey : Binky Moon, LLC // https://www.iana.org/domains/root/db/hockey.html hockey // holdings : Binky Moon, LLC // https://www.iana.org/domains/root/db/holdings.html holdings // holiday : Binky Moon, LLC // https://www.iana.org/domains/root/db/holiday.html holiday // homedepot : Home Depot Product Authority, LLC // https://www.iana.org/domains/root/db/homedepot.html homedepot // homegoods : The TJX Companies, Inc. // https://www.iana.org/domains/root/db/homegoods.html homegoods // homes : XYZ.COM LLC // https://www.iana.org/domains/root/db/homes.html homes // homesense : The TJX Companies, Inc. // https://www.iana.org/domains/root/db/homesense.html homesense // honda : Honda Motor Co., Ltd. // https://www.iana.org/domains/root/db/honda.html honda // horse : Registry Services, LLC // https://www.iana.org/domains/root/db/horse.html horse // hospital : Binky Moon, LLC // https://www.iana.org/domains/root/db/hospital.html hospital // host : Radix Technologies Inc. // https://www.iana.org/domains/root/db/host.html host // hosting : XYZ.COM LLC // https://www.iana.org/domains/root/db/hosting.html hosting // hot : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/hot.html hot // hotels : Booking.com B.V. // https://www.iana.org/domains/root/db/hotels.html hotels // hotmail : Microsoft Corporation // https://www.iana.org/domains/root/db/hotmail.html hotmail // house : Binky Moon, LLC // https://www.iana.org/domains/root/db/house.html house // how : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/how.html how // hsbc : HSBC Global Services (UK) Limited // https://www.iana.org/domains/root/db/hsbc.html hsbc // hughes : Hughes Satellite Systems Corporation // https://www.iana.org/domains/root/db/hughes.html hughes // hyatt : Hyatt GTLD, L.L.C. // https://www.iana.org/domains/root/db/hyatt.html hyatt // hyundai : Hyundai Motor Company // https://www.iana.org/domains/root/db/hyundai.html hyundai // ibm : International Business Machines Corporation // https://www.iana.org/domains/root/db/ibm.html ibm // icbc : Industrial and Commercial Bank of China Limited // https://www.iana.org/domains/root/db/icbc.html icbc // ice : IntercontinentalExchange, Inc. // https://www.iana.org/domains/root/db/ice.html ice // icu : ShortDot SA // https://www.iana.org/domains/root/db/icu.html icu // ieee : IEEE Global LLC // https://www.iana.org/domains/root/db/ieee.html ieee // ifm : ifm electronic gmbh // https://www.iana.org/domains/root/db/ifm.html ifm // ikano : Ikano S.A. // https://www.iana.org/domains/root/db/ikano.html ikano // imamat : Fondation Aga Khan (Aga Khan Foundation) // https://www.iana.org/domains/root/db/imamat.html imamat // imdb : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/imdb.html imdb // immo : Binky Moon, LLC // https://www.iana.org/domains/root/db/immo.html immo // immobilien : Dog Beach, LLC // https://www.iana.org/domains/root/db/immobilien.html immobilien // inc : Intercap Registry Inc. // https://www.iana.org/domains/root/db/inc.html inc // industries : Binky Moon, LLC // https://www.iana.org/domains/root/db/industries.html industries // infiniti : NISSAN MOTOR CO., LTD. // https://www.iana.org/domains/root/db/infiniti.html infiniti // ing : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/ing.html ing // ink : Registry Services, LLC // https://www.iana.org/domains/root/db/ink.html ink // institute : Binky Moon, LLC // https://www.iana.org/domains/root/db/institute.html institute // insurance : fTLD Registry Services LLC // https://www.iana.org/domains/root/db/insurance.html insurance // insure : Binky Moon, LLC // https://www.iana.org/domains/root/db/insure.html insure // international : Binky Moon, LLC // https://www.iana.org/domains/root/db/international.html international // intuit : Intuit Administrative Services, Inc. // https://www.iana.org/domains/root/db/intuit.html intuit // investments : Binky Moon, LLC // https://www.iana.org/domains/root/db/investments.html investments // ipiranga : Ipiranga Produtos de Petroleo S.A. // https://www.iana.org/domains/root/db/ipiranga.html ipiranga // irish : Binky Moon, LLC // https://www.iana.org/domains/root/db/irish.html irish // ismaili : Fondation Aga Khan (Aga Khan Foundation) // https://www.iana.org/domains/root/db/ismaili.html ismaili // ist : Istanbul Metropolitan Municipality // https://www.iana.org/domains/root/db/ist.html ist // istanbul : Istanbul Metropolitan Municipality // https://www.iana.org/domains/root/db/istanbul.html istanbul // itau : Itau Unibanco Holding S.A. // https://www.iana.org/domains/root/db/itau.html itau // itv : ITV Services Limited // https://www.iana.org/domains/root/db/itv.html itv // jaguar : Jaguar Land Rover Ltd // https://www.iana.org/domains/root/db/jaguar.html jaguar // java : Oracle Corporation // https://www.iana.org/domains/root/db/java.html java // jcb : JCB Co., Ltd. // https://www.iana.org/domains/root/db/jcb.html jcb // jeep : FCA US LLC. // https://www.iana.org/domains/root/db/jeep.html jeep // jetzt : Binky Moon, LLC // https://www.iana.org/domains/root/db/jetzt.html jetzt // jewelry : Binky Moon, LLC // https://www.iana.org/domains/root/db/jewelry.html jewelry // jio : Reliance Industries Limited // https://www.iana.org/domains/root/db/jio.html jio // jll : Jones Lang LaSalle Incorporated // https://www.iana.org/domains/root/db/jll.html jll // jmp : Matrix IP LLC // https://www.iana.org/domains/root/db/jmp.html jmp // jnj : Johnson & Johnson Services, Inc. // https://www.iana.org/domains/root/db/jnj.html jnj // joburg : ZA Central Registry NPC trading as ZA Central Registry // https://www.iana.org/domains/root/db/joburg.html joburg // jot : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/jot.html jot // joy : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/joy.html joy // jpmorgan : JPMorgan Chase Bank, National Association // https://www.iana.org/domains/root/db/jpmorgan.html jpmorgan // jprs : Japan Registry Services Co., Ltd. // https://www.iana.org/domains/root/db/jprs.html jprs // juegos : Dog Beach, LLC // https://www.iana.org/domains/root/db/juegos.html juegos // juniper : JUNIPER NETWORKS, INC. // https://www.iana.org/domains/root/db/juniper.html juniper // kaufen : Dog Beach, LLC // https://www.iana.org/domains/root/db/kaufen.html kaufen // kddi : KDDI CORPORATION // https://www.iana.org/domains/root/db/kddi.html kddi // kerryhotels : Kerry Trading Co. Limited // https://www.iana.org/domains/root/db/kerryhotels.html kerryhotels // kerrylogistics : Kerry Trading Co. Limited // https://www.iana.org/domains/root/db/kerrylogistics.html kerrylogistics // kerryproperties : Kerry Trading Co. Limited // https://www.iana.org/domains/root/db/kerryproperties.html kerryproperties // kfh : Kuwait Finance House // https://www.iana.org/domains/root/db/kfh.html kfh // kia : KIA MOTORS CORPORATION // https://www.iana.org/domains/root/db/kia.html kia // kids : DotKids Foundation Limited // https://www.iana.org/domains/root/db/kids.html kids // kim : Identity Digital Limited // https://www.iana.org/domains/root/db/kim.html kim // kindle : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/kindle.html kindle // kitchen : Binky Moon, LLC // https://www.iana.org/domains/root/db/kitchen.html kitchen // kiwi : DOT KIWI LIMITED // https://www.iana.org/domains/root/db/kiwi.html kiwi // koeln : dotKoeln GmbH // https://www.iana.org/domains/root/db/koeln.html koeln // komatsu : Komatsu Ltd. // https://www.iana.org/domains/root/db/komatsu.html komatsu // kosher : Kosher Marketing Assets LLC // https://www.iana.org/domains/root/db/kosher.html kosher // kpmg : KPMG International Cooperative (KPMG International Genossenschaft) // https://www.iana.org/domains/root/db/kpmg.html kpmg // kpn : Koninklijke KPN N.V. // https://www.iana.org/domains/root/db/kpn.html kpn // krd : KRG Department of Information Technology // https://www.iana.org/domains/root/db/krd.html krd // kred : KredTLD Pty Ltd // https://www.iana.org/domains/root/db/kred.html kred // kuokgroup : Kerry Trading Co. Limited // https://www.iana.org/domains/root/db/kuokgroup.html kuokgroup // kyoto : Academic Institution: Kyoto Jyoho Gakuen // https://www.iana.org/domains/root/db/kyoto.html kyoto // lacaixa : Fundación Bancaria Caixa d’Estalvis i Pensions de Barcelona, “la Caixa” // https://www.iana.org/domains/root/db/lacaixa.html lacaixa // lamborghini : Automobili Lamborghini S.p.A. // https://www.iana.org/domains/root/db/lamborghini.html lamborghini // lamer : The Estée Lauder Companies Inc. // https://www.iana.org/domains/root/db/lamer.html lamer // lancaster : LANCASTER // https://www.iana.org/domains/root/db/lancaster.html lancaster // land : Binky Moon, LLC // https://www.iana.org/domains/root/db/land.html land // landrover : Jaguar Land Rover Ltd // https://www.iana.org/domains/root/db/landrover.html landrover // lanxess : LANXESS Corporation // https://www.iana.org/domains/root/db/lanxess.html lanxess // lasalle : Jones Lang LaSalle Incorporated // https://www.iana.org/domains/root/db/lasalle.html lasalle // lat : XYZ.COM LLC // https://www.iana.org/domains/root/db/lat.html lat // latino : Dish DBS Corporation // https://www.iana.org/domains/root/db/latino.html latino // latrobe : La Trobe University // https://www.iana.org/domains/root/db/latrobe.html latrobe // law : Registry Services, LLC // https://www.iana.org/domains/root/db/law.html law // lawyer : Dog Beach, LLC // https://www.iana.org/domains/root/db/lawyer.html lawyer // lds : IRI Domain Management, LLC // https://www.iana.org/domains/root/db/lds.html lds // lease : Binky Moon, LLC // https://www.iana.org/domains/root/db/lease.html lease // leclerc : A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc // https://www.iana.org/domains/root/db/leclerc.html leclerc // lefrak : LeFrak Organization, Inc. // https://www.iana.org/domains/root/db/lefrak.html lefrak // legal : Binky Moon, LLC // https://www.iana.org/domains/root/db/legal.html legal // lego : LEGO Juris A/S // https://www.iana.org/domains/root/db/lego.html lego // lexus : TOYOTA MOTOR CORPORATION // https://www.iana.org/domains/root/db/lexus.html lexus // lgbt : Identity Digital Limited // https://www.iana.org/domains/root/db/lgbt.html lgbt // lidl : Schwarz Domains und Services GmbH & Co. KG // https://www.iana.org/domains/root/db/lidl.html lidl // life : Binky Moon, LLC // https://www.iana.org/domains/root/db/life.html life // lifeinsurance : American Council of Life Insurers // https://www.iana.org/domains/root/db/lifeinsurance.html lifeinsurance // lifestyle : Internet Naming Company LLC // https://www.iana.org/domains/root/db/lifestyle.html lifestyle // lighting : Binky Moon, LLC // https://www.iana.org/domains/root/db/lighting.html lighting // like : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/like.html like // lilly : Eli Lilly and Company // https://www.iana.org/domains/root/db/lilly.html lilly // limited : Binky Moon, LLC // https://www.iana.org/domains/root/db/limited.html limited // limo : Binky Moon, LLC // https://www.iana.org/domains/root/db/limo.html limo // lincoln : Ford Motor Company // https://www.iana.org/domains/root/db/lincoln.html lincoln // link : Nova Registry Ltd // https://www.iana.org/domains/root/db/link.html link // lipsy : Lipsy Ltd // https://www.iana.org/domains/root/db/lipsy.html lipsy // live : Dog Beach, LLC // https://www.iana.org/domains/root/db/live.html live // living : Internet Naming Company LLC // https://www.iana.org/domains/root/db/living.html living // llc : Identity Digital Limited // https://www.iana.org/domains/root/db/llc.html llc // llp : Intercap Registry Inc. // https://www.iana.org/domains/root/db/llp.html llp // loan : dot Loan Limited // https://www.iana.org/domains/root/db/loan.html loan // loans : Binky Moon, LLC // https://www.iana.org/domains/root/db/loans.html loans // locker : Orange Domains LLC // https://www.iana.org/domains/root/db/locker.html locker // locus : Locus Analytics LLC // https://www.iana.org/domains/root/db/locus.html locus // lol : XYZ.COM LLC // https://www.iana.org/domains/root/db/lol.html lol // london : Dot London Domains Limited // https://www.iana.org/domains/root/db/london.html london // lotte : Lotte Holdings Co., Ltd. // https://www.iana.org/domains/root/db/lotte.html lotte // lotto : Identity Digital Limited // https://www.iana.org/domains/root/db/lotto.html lotto // love : Merchant Law Group LLP // https://www.iana.org/domains/root/db/love.html love // lpl : LPL Holdings, Inc. // https://www.iana.org/domains/root/db/lpl.html lpl // lplfinancial : LPL Holdings, Inc. // https://www.iana.org/domains/root/db/lplfinancial.html lplfinancial // ltd : Binky Moon, LLC // https://www.iana.org/domains/root/db/ltd.html ltd // ltda : InterNetX, Corp // https://www.iana.org/domains/root/db/ltda.html ltda // lundbeck : H. Lundbeck A/S // https://www.iana.org/domains/root/db/lundbeck.html lundbeck // luxe : Registry Services, LLC // https://www.iana.org/domains/root/db/luxe.html luxe // luxury : Luxury Partners, LLC // https://www.iana.org/domains/root/db/luxury.html luxury // madrid : Comunidad de Madrid // https://www.iana.org/domains/root/db/madrid.html madrid // maif : Mutuelle Assurance Instituteur France (MAIF) // https://www.iana.org/domains/root/db/maif.html maif // maison : Binky Moon, LLC // https://www.iana.org/domains/root/db/maison.html maison // makeup : XYZ.COM LLC // https://www.iana.org/domains/root/db/makeup.html makeup // man : MAN SE // https://www.iana.org/domains/root/db/man.html man // management : Binky Moon, LLC // https://www.iana.org/domains/root/db/management.html management // mango : PUNTO FA S.L. // https://www.iana.org/domains/root/db/mango.html mango // map : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/map.html map // market : Dog Beach, LLC // https://www.iana.org/domains/root/db/market.html market // marketing : Binky Moon, LLC // https://www.iana.org/domains/root/db/marketing.html marketing // markets : Dog Beach, LLC // https://www.iana.org/domains/root/db/markets.html markets // marriott : Marriott Worldwide Corporation // https://www.iana.org/domains/root/db/marriott.html marriott // marshalls : The TJX Companies, Inc. // https://www.iana.org/domains/root/db/marshalls.html marshalls // mattel : Mattel Sites, Inc. // https://www.iana.org/domains/root/db/mattel.html mattel // mba : Binky Moon, LLC // https://www.iana.org/domains/root/db/mba.html mba // mckinsey : McKinsey Holdings, Inc. // https://www.iana.org/domains/root/db/mckinsey.html mckinsey // med : Medistry LLC // https://www.iana.org/domains/root/db/med.html med // media : Binky Moon, LLC // https://www.iana.org/domains/root/db/media.html media // meet : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/meet.html meet // melbourne : The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation // https://www.iana.org/domains/root/db/melbourne.html melbourne // meme : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/meme.html meme // memorial : Dog Beach, LLC // https://www.iana.org/domains/root/db/memorial.html memorial // men : Exclusive Registry Limited // https://www.iana.org/domains/root/db/men.html men // menu : Dot Menu Registry, LLC // https://www.iana.org/domains/root/db/menu.html menu // merckmsd : MSD Registry Holdings, Inc. // https://www.iana.org/domains/root/db/merckmsd.html merckmsd // miami : Registry Services, LLC // https://www.iana.org/domains/root/db/miami.html miami // microsoft : Microsoft Corporation // https://www.iana.org/domains/root/db/microsoft.html microsoft // mini : Bayerische Motoren Werke Aktiengesellschaft // https://www.iana.org/domains/root/db/mini.html mini // mint : Intuit Administrative Services, Inc. // https://www.iana.org/domains/root/db/mint.html mint // mit : Massachusetts Institute of Technology // https://www.iana.org/domains/root/db/mit.html mit // mitsubishi : Mitsubishi Corporation // https://www.iana.org/domains/root/db/mitsubishi.html mitsubishi // mlb : MLB Advanced Media DH, LLC // https://www.iana.org/domains/root/db/mlb.html mlb // mls : The Canadian Real Estate Association // https://www.iana.org/domains/root/db/mls.html mls // mma : MMA IARD // https://www.iana.org/domains/root/db/mma.html mma // mobile : Dish DBS Corporation // https://www.iana.org/domains/root/db/mobile.html mobile // moda : Dog Beach, LLC // https://www.iana.org/domains/root/db/moda.html moda // moe : Interlink Systems Innovation Institute K.K. // https://www.iana.org/domains/root/db/moe.html moe // moi : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/moi.html moi // mom : XYZ.COM LLC // https://www.iana.org/domains/root/db/mom.html mom // monash : Monash University // https://www.iana.org/domains/root/db/monash.html monash // money : Binky Moon, LLC // https://www.iana.org/domains/root/db/money.html money // monster : XYZ.COM LLC // https://www.iana.org/domains/root/db/monster.html monster // mormon : IRI Domain Management, LLC // https://www.iana.org/domains/root/db/mormon.html mormon // mortgage : Dog Beach, LLC // https://www.iana.org/domains/root/db/mortgage.html mortgage // moscow : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) // https://www.iana.org/domains/root/db/moscow.html moscow // moto : Motorola Trademark Holdings, LLC // https://www.iana.org/domains/root/db/moto.html moto // motorcycles : XYZ.COM LLC // https://www.iana.org/domains/root/db/motorcycles.html motorcycles // mov : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/mov.html mov // movie : Binky Moon, LLC // https://www.iana.org/domains/root/db/movie.html movie // msd : MSD Registry Holdings, Inc. // https://www.iana.org/domains/root/db/msd.html msd // mtn : MTN Dubai Limited // https://www.iana.org/domains/root/db/mtn.html mtn // mtr : MTR Corporation Limited // https://www.iana.org/domains/root/db/mtr.html mtr // music : DotMusic Limited // https://www.iana.org/domains/root/db/music.html music // nab : National Australia Bank Limited // https://www.iana.org/domains/root/db/nab.html nab // nagoya : GMO Registry, Inc. // https://www.iana.org/domains/root/db/nagoya.html nagoya // natura : NATURA COSMÉTICOS S.A. // https://www.iana.org/domains/root/db/natura.html natura // navy : Dog Beach, LLC // https://www.iana.org/domains/root/db/navy.html navy // nba : NBA REGISTRY, LLC // https://www.iana.org/domains/root/db/nba.html nba // nec : NEC Corporation // https://www.iana.org/domains/root/db/nec.html nec // netbank : COMMONWEALTH BANK OF AUSTRALIA // https://www.iana.org/domains/root/db/netbank.html netbank // netflix : Netflix, Inc. // https://www.iana.org/domains/root/db/netflix.html netflix // network : Binky Moon, LLC // https://www.iana.org/domains/root/db/network.html network // neustar : NeuStar, Inc. // https://www.iana.org/domains/root/db/neustar.html neustar // new : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/new.html new // news : Dog Beach, LLC // https://www.iana.org/domains/root/db/news.html news // next : Next plc // https://www.iana.org/domains/root/db/next.html next // nextdirect : Next plc // https://www.iana.org/domains/root/db/nextdirect.html nextdirect // nexus : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/nexus.html nexus // nfl : NFL Reg Ops LLC // https://www.iana.org/domains/root/db/nfl.html nfl // ngo : Public Interest Registry // https://www.iana.org/domains/root/db/ngo.html ngo // nhk : Japan Broadcasting Corporation (NHK) // https://www.iana.org/domains/root/db/nhk.html nhk // nico : DWANGO Co., Ltd. // https://www.iana.org/domains/root/db/nico.html nico // nike : NIKE, Inc. // https://www.iana.org/domains/root/db/nike.html nike // nikon : NIKON CORPORATION // https://www.iana.org/domains/root/db/nikon.html nikon // ninja : Dog Beach, LLC // https://www.iana.org/domains/root/db/ninja.html ninja // nissan : NISSAN MOTOR CO., LTD. // https://www.iana.org/domains/root/db/nissan.html nissan // nissay : Nippon Life Insurance Company // https://www.iana.org/domains/root/db/nissay.html nissay // nokia : Nokia Corporation // https://www.iana.org/domains/root/db/nokia.html nokia // norton : NortonLifeLock Inc. // https://www.iana.org/domains/root/db/norton.html norton // now : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/now.html now // nowruz : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. // https://www.iana.org/domains/root/db/nowruz.html nowruz // nowtv : Starbucks (HK) Limited // https://www.iana.org/domains/root/db/nowtv.html nowtv // nra : NRA Holdings Company, INC. // https://www.iana.org/domains/root/db/nra.html nra // nrw : Minds + Machines GmbH // https://www.iana.org/domains/root/db/nrw.html nrw // ntt : NIPPON TELEGRAPH AND TELEPHONE CORPORATION // https://www.iana.org/domains/root/db/ntt.html ntt // nyc : The City of New York by and through the New York City Department of Information Technology & Telecommunications // https://www.iana.org/domains/root/db/nyc.html nyc // obi : OBI Group Holding SE & Co. KGaA // https://www.iana.org/domains/root/db/obi.html obi // observer : Fegistry, LLC // https://www.iana.org/domains/root/db/observer.html observer // office : Microsoft Corporation // https://www.iana.org/domains/root/db/office.html office // okinawa : BRregistry, Inc. // https://www.iana.org/domains/root/db/okinawa.html okinawa // olayan : Competrol (Luxembourg) Sarl // https://www.iana.org/domains/root/db/olayan.html olayan // olayangroup : Competrol (Luxembourg) Sarl // https://www.iana.org/domains/root/db/olayangroup.html olayangroup // ollo : Dish DBS Corporation // https://www.iana.org/domains/root/db/ollo.html ollo // omega : The Swatch Group Ltd // https://www.iana.org/domains/root/db/omega.html omega // one : One.com A/S // https://www.iana.org/domains/root/db/one.html one // ong : Public Interest Registry // https://www.iana.org/domains/root/db/ong.html ong // onl : iRegistry GmbH // https://www.iana.org/domains/root/db/onl.html onl // online : Radix Technologies Inc. // https://www.iana.org/domains/root/db/online.html online // ooo : INFIBEAM AVENUES LIMITED // https://www.iana.org/domains/root/db/ooo.html ooo // open : American Express Travel Related Services Company, Inc. // https://www.iana.org/domains/root/db/open.html open // oracle : Oracle Corporation // https://www.iana.org/domains/root/db/oracle.html oracle // orange : Orange Brand Services Limited // https://www.iana.org/domains/root/db/orange.html orange // organic : Identity Digital Limited // https://www.iana.org/domains/root/db/organic.html organic // origins : The Estée Lauder Companies Inc. // https://www.iana.org/domains/root/db/origins.html origins // osaka : Osaka Registry Co., Ltd. // https://www.iana.org/domains/root/db/osaka.html osaka // otsuka : Otsuka Holdings Co., Ltd. // https://www.iana.org/domains/root/db/otsuka.html otsuka // ott : Dish DBS Corporation // https://www.iana.org/domains/root/db/ott.html ott // ovh : MédiaBC // https://www.iana.org/domains/root/db/ovh.html ovh // page : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/page.html page // panasonic : Panasonic Holdings Corporation // https://www.iana.org/domains/root/db/panasonic.html panasonic // paris : City of Paris // https://www.iana.org/domains/root/db/paris.html paris // pars : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. // https://www.iana.org/domains/root/db/pars.html pars // partners : Binky Moon, LLC // https://www.iana.org/domains/root/db/partners.html partners // parts : Binky Moon, LLC // https://www.iana.org/domains/root/db/parts.html parts // party : Blue Sky Registry Limited // https://www.iana.org/domains/root/db/party.html party // pay : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/pay.html pay // pccw : PCCW Enterprises Limited // https://www.iana.org/domains/root/db/pccw.html pccw // pet : Identity Digital Limited // https://www.iana.org/domains/root/db/pet.html pet // pfizer : Pfizer Inc. // https://www.iana.org/domains/root/db/pfizer.html pfizer // pharmacy : National Association of Boards of Pharmacy // https://www.iana.org/domains/root/db/pharmacy.html pharmacy // phd : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/phd.html phd // philips : Koninklijke Philips N.V. // https://www.iana.org/domains/root/db/philips.html philips // phone : Dish DBS Corporation // https://www.iana.org/domains/root/db/phone.html phone // photo : Registry Services, LLC // https://www.iana.org/domains/root/db/photo.html photo // photography : Binky Moon, LLC // https://www.iana.org/domains/root/db/photography.html photography // photos : Binky Moon, LLC // https://www.iana.org/domains/root/db/photos.html photos // physio : PhysBiz Pty Ltd // https://www.iana.org/domains/root/db/physio.html physio // pics : XYZ.COM LLC // https://www.iana.org/domains/root/db/pics.html pics // pictet : Pictet Europe S.A. // https://www.iana.org/domains/root/db/pictet.html pictet // pictures : Binky Moon, LLC // https://www.iana.org/domains/root/db/pictures.html pictures // pid : Top Level Spectrum, Inc. // https://www.iana.org/domains/root/db/pid.html pid // pin : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/pin.html pin // ping : Ping Registry Provider, Inc. // https://www.iana.org/domains/root/db/ping.html ping // pink : Identity Digital Limited // https://www.iana.org/domains/root/db/pink.html pink // pioneer : Pioneer Corporation // https://www.iana.org/domains/root/db/pioneer.html pioneer // pizza : Binky Moon, LLC // https://www.iana.org/domains/root/db/pizza.html pizza // place : Binky Moon, LLC // https://www.iana.org/domains/root/db/place.html place // play : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/play.html play // playstation : Sony Interactive Entertainment Inc. // https://www.iana.org/domains/root/db/playstation.html playstation // plumbing : Binky Moon, LLC // https://www.iana.org/domains/root/db/plumbing.html plumbing // plus : Binky Moon, LLC // https://www.iana.org/domains/root/db/plus.html plus // pnc : PNC Domain Co., LLC // https://www.iana.org/domains/root/db/pnc.html pnc // pohl : Deutsche Vermögensberatung Aktiengesellschaft DVAG // https://www.iana.org/domains/root/db/pohl.html pohl // poker : Identity Digital Limited // https://www.iana.org/domains/root/db/poker.html poker // politie : Politie Nederland // https://www.iana.org/domains/root/db/politie.html politie // porn : ICM Registry PN LLC // https://www.iana.org/domains/root/db/porn.html porn // pramerica : Prudential Financial, Inc. // https://www.iana.org/domains/root/db/pramerica.html pramerica // praxi : Praxi S.p.A. // https://www.iana.org/domains/root/db/praxi.html praxi // press : Radix Technologies Inc. // https://www.iana.org/domains/root/db/press.html press // prime : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/prime.html prime // prod : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/prod.html prod // productions : Binky Moon, LLC // https://www.iana.org/domains/root/db/productions.html productions // prof : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/prof.html prof // progressive : Progressive Casualty Insurance Company // https://www.iana.org/domains/root/db/progressive.html progressive // promo : Identity Digital Limited // https://www.iana.org/domains/root/db/promo.html promo // properties : Binky Moon, LLC // https://www.iana.org/domains/root/db/properties.html properties // property : Digital Property Infrastructure Limited // https://www.iana.org/domains/root/db/property.html property // protection : XYZ.COM LLC // https://www.iana.org/domains/root/db/protection.html protection // pru : Prudential Financial, Inc. // https://www.iana.org/domains/root/db/pru.html pru // prudential : Prudential Financial, Inc. // https://www.iana.org/domains/root/db/prudential.html prudential // pub : Dog Beach, LLC // https://www.iana.org/domains/root/db/pub.html pub // pwc : PricewaterhouseCoopers LLP // https://www.iana.org/domains/root/db/pwc.html pwc // qpon : dotQPON LLC // https://www.iana.org/domains/root/db/qpon.html qpon // quebec : PointQuébec Inc // https://www.iana.org/domains/root/db/quebec.html quebec // quest : XYZ.COM LLC // https://www.iana.org/domains/root/db/quest.html quest // racing : Premier Registry Limited // https://www.iana.org/domains/root/db/racing.html racing // radio : European Broadcasting Union (EBU) // https://www.iana.org/domains/root/db/radio.html radio // read : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/read.html read // realestate : dotRealEstate LLC // https://www.iana.org/domains/root/db/realestate.html realestate // realtor : Real Estate Domains LLC // https://www.iana.org/domains/root/db/realtor.html realtor // realty : Internet Naming Company LLC // https://www.iana.org/domains/root/db/realty.html realty // recipes : Binky Moon, LLC // https://www.iana.org/domains/root/db/recipes.html recipes // red : Identity Digital Limited // https://www.iana.org/domains/root/db/red.html red // redstone : Redstone Haute Couture Co., Ltd. // https://www.iana.org/domains/root/db/redstone.html redstone // redumbrella : Travelers TLD, LLC // https://www.iana.org/domains/root/db/redumbrella.html redumbrella // rehab : Dog Beach, LLC // https://www.iana.org/domains/root/db/rehab.html rehab // reise : Binky Moon, LLC // https://www.iana.org/domains/root/db/reise.html reise // reisen : Binky Moon, LLC // https://www.iana.org/domains/root/db/reisen.html reisen // reit : National Association of Real Estate Investment Trusts, Inc. // https://www.iana.org/domains/root/db/reit.html reit // reliance : Reliance Industries Limited // https://www.iana.org/domains/root/db/reliance.html reliance // ren : ZDNS International Limited // https://www.iana.org/domains/root/db/ren.html ren // rent : XYZ.COM LLC // https://www.iana.org/domains/root/db/rent.html rent // rentals : Binky Moon, LLC // https://www.iana.org/domains/root/db/rentals.html rentals // repair : Binky Moon, LLC // https://www.iana.org/domains/root/db/repair.html repair // report : Binky Moon, LLC // https://www.iana.org/domains/root/db/report.html report // republican : Dog Beach, LLC // https://www.iana.org/domains/root/db/republican.html republican // rest : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable // https://www.iana.org/domains/root/db/rest.html rest // restaurant : Binky Moon, LLC // https://www.iana.org/domains/root/db/restaurant.html restaurant // review : dot Review Limited // https://www.iana.org/domains/root/db/review.html review // reviews : Dog Beach, LLC // https://www.iana.org/domains/root/db/reviews.html reviews // rexroth : Robert Bosch GMBH // https://www.iana.org/domains/root/db/rexroth.html rexroth // rich : iRegistry GmbH // https://www.iana.org/domains/root/db/rich.html rich // richardli : Pacific Century Asset Management (HK) Limited // https://www.iana.org/domains/root/db/richardli.html richardli // ricoh : Ricoh Company, Ltd. // https://www.iana.org/domains/root/db/ricoh.html ricoh // ril : Reliance Industries Limited // https://www.iana.org/domains/root/db/ril.html ril // rio : Empresa Municipal de Informática SA - IPLANRIO // https://www.iana.org/domains/root/db/rio.html rio // rip : Dog Beach, LLC // https://www.iana.org/domains/root/db/rip.html rip // rocks : Dog Beach, LLC // https://www.iana.org/domains/root/db/rocks.html rocks // rodeo : Registry Services, LLC // https://www.iana.org/domains/root/db/rodeo.html rodeo // rogers : Rogers Communications Canada Inc. // https://www.iana.org/domains/root/db/rogers.html rogers // room : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/room.html room // rsvp : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/rsvp.html rsvp // rugby : World Rugby Strategic Developments Limited // https://www.iana.org/domains/root/db/rugby.html rugby // ruhr : dotSaarland GmbH // https://www.iana.org/domains/root/db/ruhr.html ruhr // run : Binky Moon, LLC // https://www.iana.org/domains/root/db/run.html run // rwe : RWE AG // https://www.iana.org/domains/root/db/rwe.html rwe // ryukyu : BRregistry, Inc. // https://www.iana.org/domains/root/db/ryukyu.html ryukyu // saarland : dotSaarland GmbH // https://www.iana.org/domains/root/db/saarland.html saarland // safe : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/safe.html safe // safety : Safety Registry Services, LLC. // https://www.iana.org/domains/root/db/safety.html safety // sakura : SAKURA Internet Inc. // https://www.iana.org/domains/root/db/sakura.html sakura // sale : Dog Beach, LLC // https://www.iana.org/domains/root/db/sale.html sale // salon : Binky Moon, LLC // https://www.iana.org/domains/root/db/salon.html salon // samsclub : Wal-Mart Stores, Inc. // https://www.iana.org/domains/root/db/samsclub.html samsclub // samsung : SAMSUNG SDS CO., LTD // https://www.iana.org/domains/root/db/samsung.html samsung // sandvik : Sandvik AB // https://www.iana.org/domains/root/db/sandvik.html sandvik // sandvikcoromant : Sandvik AB // https://www.iana.org/domains/root/db/sandvikcoromant.html sandvikcoromant // sanofi : Sanofi // https://www.iana.org/domains/root/db/sanofi.html sanofi // sap : SAP AG // https://www.iana.org/domains/root/db/sap.html sap // sarl : Binky Moon, LLC // https://www.iana.org/domains/root/db/sarl.html sarl // sas : Research IP LLC // https://www.iana.org/domains/root/db/sas.html sas // save : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/save.html save // saxo : Saxo Bank A/S // https://www.iana.org/domains/root/db/saxo.html saxo // sbi : STATE BANK OF INDIA // https://www.iana.org/domains/root/db/sbi.html sbi // sbs : ShortDot SA // https://www.iana.org/domains/root/db/sbs.html sbs // scb : The Siam Commercial Bank Public Company Limited ("SCB") // https://www.iana.org/domains/root/db/scb.html scb // schaeffler : Schaeffler Technologies AG & Co. KG // https://www.iana.org/domains/root/db/schaeffler.html schaeffler // schmidt : SCHMIDT GROUPE S.A.S. // https://www.iana.org/domains/root/db/schmidt.html schmidt // scholarships : Scholarships.com, LLC // https://www.iana.org/domains/root/db/scholarships.html scholarships // school : Binky Moon, LLC // https://www.iana.org/domains/root/db/school.html school // schule : Binky Moon, LLC // https://www.iana.org/domains/root/db/schule.html schule // schwarz : Schwarz Domains und Services GmbH & Co. KG // https://www.iana.org/domains/root/db/schwarz.html schwarz // science : dot Science Limited // https://www.iana.org/domains/root/db/science.html science // scot : Dot Scot Registry Limited // https://www.iana.org/domains/root/db/scot.html scot // search : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/search.html search // seat : SEAT, S.A. (Sociedad Unipersonal) // https://www.iana.org/domains/root/db/seat.html seat // secure : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/secure.html secure // security : XYZ.COM LLC // https://www.iana.org/domains/root/db/security.html security // seek : Seek Limited // https://www.iana.org/domains/root/db/seek.html seek // select : Registry Services, LLC // https://www.iana.org/domains/root/db/select.html select // sener : Sener Ingeniería y Sistemas, S.A. // https://www.iana.org/domains/root/db/sener.html sener // services : Binky Moon, LLC // https://www.iana.org/domains/root/db/services.html services // seven : Seven West Media Ltd // https://www.iana.org/domains/root/db/seven.html seven // sew : SEW-EURODRIVE GmbH & Co KG // https://www.iana.org/domains/root/db/sew.html sew // sex : ICM Registry SX LLC // https://www.iana.org/domains/root/db/sex.html sex // sexy : Internet Naming Company LLC // https://www.iana.org/domains/root/db/sexy.html sexy // sfr : Societe Francaise du Radiotelephone - SFR // https://www.iana.org/domains/root/db/sfr.html sfr // shangrila : Shangri‐La International Hotel Management Limited // https://www.iana.org/domains/root/db/shangrila.html shangrila // sharp : Sharp Corporation // https://www.iana.org/domains/root/db/sharp.html sharp // shaw : Shaw Cablesystems G.P. // https://www.iana.org/domains/root/db/shaw.html shaw // shell : Shell Information Technology International Inc // https://www.iana.org/domains/root/db/shell.html shell // shia : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. // https://www.iana.org/domains/root/db/shia.html shia // shiksha : Identity Digital Limited // https://www.iana.org/domains/root/db/shiksha.html shiksha // shoes : Binky Moon, LLC // https://www.iana.org/domains/root/db/shoes.html shoes // shop : GMO Registry, Inc. // https://www.iana.org/domains/root/db/shop.html shop // shopping : Binky Moon, LLC // https://www.iana.org/domains/root/db/shopping.html shopping // shouji : Beijing Qihu Keji Co., Ltd. // https://www.iana.org/domains/root/db/shouji.html shouji // show : Binky Moon, LLC // https://www.iana.org/domains/root/db/show.html show // silk : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/silk.html silk // sina : Sina Corporation // https://www.iana.org/domains/root/db/sina.html sina // singles : Binky Moon, LLC // https://www.iana.org/domains/root/db/singles.html singles // site : Radix Technologies Inc. // https://www.iana.org/domains/root/db/site.html site // ski : Identity Digital Limited // https://www.iana.org/domains/root/db/ski.html ski // skin : XYZ.COM LLC // https://www.iana.org/domains/root/db/skin.html skin // sky : Sky International AG // https://www.iana.org/domains/root/db/sky.html sky // skype : Microsoft Corporation // https://www.iana.org/domains/root/db/skype.html skype // sling : DISH Technologies L.L.C. // https://www.iana.org/domains/root/db/sling.html sling // smart : Smart Communications, Inc. (SMART) // https://www.iana.org/domains/root/db/smart.html smart // smile : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/smile.html smile // sncf : Société Nationale SNCF // https://www.iana.org/domains/root/db/sncf.html sncf // soccer : Binky Moon, LLC // https://www.iana.org/domains/root/db/soccer.html soccer // social : Dog Beach, LLC // https://www.iana.org/domains/root/db/social.html social // softbank : SoftBank Group Corp. // https://www.iana.org/domains/root/db/softbank.html softbank // software : Dog Beach, LLC // https://www.iana.org/domains/root/db/software.html software // sohu : Sohu.com Limited // https://www.iana.org/domains/root/db/sohu.html sohu // solar : Binky Moon, LLC // https://www.iana.org/domains/root/db/solar.html solar // solutions : Binky Moon, LLC // https://www.iana.org/domains/root/db/solutions.html solutions // song : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/song.html song // sony : Sony Corporation // https://www.iana.org/domains/root/db/sony.html sony // soy : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/soy.html soy // spa : Asia Spa and Wellness Promotion Council Limited // https://www.iana.org/domains/root/db/spa.html spa // space : Radix Technologies Inc. // https://www.iana.org/domains/root/db/space.html space // sport : SportAccord // https://www.iana.org/domains/root/db/sport.html sport // spot : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/spot.html spot // srl : InterNetX, Corp // https://www.iana.org/domains/root/db/srl.html srl // stada : STADA Arzneimittel AG // https://www.iana.org/domains/root/db/stada.html stada // staples : Staples, Inc. // https://www.iana.org/domains/root/db/staples.html staples // star : Star India Private Limited // https://www.iana.org/domains/root/db/star.html star // statebank : STATE BANK OF INDIA // https://www.iana.org/domains/root/db/statebank.html statebank // statefarm : State Farm Mutual Automobile Insurance Company // https://www.iana.org/domains/root/db/statefarm.html statefarm // stc : Saudi Telecom Company // https://www.iana.org/domains/root/db/stc.html stc // stcgroup : Saudi Telecom Company // https://www.iana.org/domains/root/db/stcgroup.html stcgroup // stockholm : Stockholms kommun // https://www.iana.org/domains/root/db/stockholm.html stockholm // storage : XYZ.COM LLC // https://www.iana.org/domains/root/db/storage.html storage // store : Radix Technologies Inc. // https://www.iana.org/domains/root/db/store.html store // stream : dot Stream Limited // https://www.iana.org/domains/root/db/stream.html stream // studio : Dog Beach, LLC // https://www.iana.org/domains/root/db/studio.html studio // study : Registry Services, LLC // https://www.iana.org/domains/root/db/study.html study // style : Binky Moon, LLC // https://www.iana.org/domains/root/db/style.html style // sucks : Vox Populi Registry Ltd. // https://www.iana.org/domains/root/db/sucks.html sucks // supplies : Binky Moon, LLC // https://www.iana.org/domains/root/db/supplies.html supplies // supply : Binky Moon, LLC // https://www.iana.org/domains/root/db/supply.html supply // support : Binky Moon, LLC // https://www.iana.org/domains/root/db/support.html support // surf : Registry Services, LLC // https://www.iana.org/domains/root/db/surf.html surf // surgery : Binky Moon, LLC // https://www.iana.org/domains/root/db/surgery.html surgery // suzuki : SUZUKI MOTOR CORPORATION // https://www.iana.org/domains/root/db/suzuki.html suzuki // swatch : The Swatch Group Ltd // https://www.iana.org/domains/root/db/swatch.html swatch // swiss : Swiss Confederation // https://www.iana.org/domains/root/db/swiss.html swiss // sydney : State of New South Wales, Department of Premier and Cabinet // https://www.iana.org/domains/root/db/sydney.html sydney // systems : Binky Moon, LLC // https://www.iana.org/domains/root/db/systems.html systems // tab : Tabcorp Holdings Limited // https://www.iana.org/domains/root/db/tab.html tab // taipei : Taipei City Government // https://www.iana.org/domains/root/db/taipei.html taipei // talk : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/talk.html talk // taobao : Alibaba Group Holding Limited // https://www.iana.org/domains/root/db/taobao.html taobao // target : Target Domain Holdings, LLC // https://www.iana.org/domains/root/db/target.html target // tatamotors : Tata Motors Ltd // https://www.iana.org/domains/root/db/tatamotors.html tatamotors // tatar : Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic" // https://www.iana.org/domains/root/db/tatar.html tatar // tattoo : Registry Services, LLC // https://www.iana.org/domains/root/db/tattoo.html tattoo // tax : Binky Moon, LLC // https://www.iana.org/domains/root/db/tax.html tax // taxi : Binky Moon, LLC // https://www.iana.org/domains/root/db/taxi.html taxi // tci : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. // https://www.iana.org/domains/root/db/tci.html tci // tdk : TDK Corporation // https://www.iana.org/domains/root/db/tdk.html tdk // team : Binky Moon, LLC // https://www.iana.org/domains/root/db/team.html team // tech : Radix Technologies Inc. // https://www.iana.org/domains/root/db/tech.html tech // technology : Binky Moon, LLC // https://www.iana.org/domains/root/db/technology.html technology // temasek : Temasek Holdings (Private) Limited // https://www.iana.org/domains/root/db/temasek.html temasek // tennis : Binky Moon, LLC // https://www.iana.org/domains/root/db/tennis.html tennis // teva : Teva Pharmaceutical Industries Limited // https://www.iana.org/domains/root/db/teva.html teva // thd : Home Depot Product Authority, LLC // https://www.iana.org/domains/root/db/thd.html thd // theater : Binky Moon, LLC // https://www.iana.org/domains/root/db/theater.html theater // theatre : XYZ.COM LLC // https://www.iana.org/domains/root/db/theatre.html theatre // tiaa : Teachers Insurance and Annuity Association of America // https://www.iana.org/domains/root/db/tiaa.html tiaa // tickets : XYZ.COM LLC // https://www.iana.org/domains/root/db/tickets.html tickets // tienda : Binky Moon, LLC // https://www.iana.org/domains/root/db/tienda.html tienda // tips : Binky Moon, LLC // https://www.iana.org/domains/root/db/tips.html tips // tires : Binky Moon, LLC // https://www.iana.org/domains/root/db/tires.html tires // tirol : punkt Tirol GmbH // https://www.iana.org/domains/root/db/tirol.html tirol // tjmaxx : The TJX Companies, Inc. // https://www.iana.org/domains/root/db/tjmaxx.html tjmaxx // tjx : The TJX Companies, Inc. // https://www.iana.org/domains/root/db/tjx.html tjx // tkmaxx : The TJX Companies, Inc. // https://www.iana.org/domains/root/db/tkmaxx.html tkmaxx // tmall : Alibaba Group Holding Limited // https://www.iana.org/domains/root/db/tmall.html tmall // today : Binky Moon, LLC // https://www.iana.org/domains/root/db/today.html today // tokyo : GMO Registry, Inc. // https://www.iana.org/domains/root/db/tokyo.html tokyo // tools : Binky Moon, LLC // https://www.iana.org/domains/root/db/tools.html tools // top : .TOP Registry // https://www.iana.org/domains/root/db/top.html top // toray : Toray Industries, Inc. // https://www.iana.org/domains/root/db/toray.html toray // toshiba : TOSHIBA Corporation // https://www.iana.org/domains/root/db/toshiba.html toshiba // total : TotalEnergies SE // https://www.iana.org/domains/root/db/total.html total // tours : Binky Moon, LLC // https://www.iana.org/domains/root/db/tours.html tours // town : Binky Moon, LLC // https://www.iana.org/domains/root/db/town.html town // toyota : TOYOTA MOTOR CORPORATION // https://www.iana.org/domains/root/db/toyota.html toyota // toys : Binky Moon, LLC // https://www.iana.org/domains/root/db/toys.html toys // trade : Elite Registry Limited // https://www.iana.org/domains/root/db/trade.html trade // trading : Dog Beach, LLC // https://www.iana.org/domains/root/db/trading.html trading // training : Binky Moon, LLC // https://www.iana.org/domains/root/db/training.html training // travel : Dog Beach, LLC // https://www.iana.org/domains/root/db/travel.html travel // travelers : Travelers TLD, LLC // https://www.iana.org/domains/root/db/travelers.html travelers // travelersinsurance : Travelers TLD, LLC // https://www.iana.org/domains/root/db/travelersinsurance.html travelersinsurance // trust : Internet Naming Company LLC // https://www.iana.org/domains/root/db/trust.html trust // trv : Travelers TLD, LLC // https://www.iana.org/domains/root/db/trv.html trv // tube : Latin American Telecom LLC // https://www.iana.org/domains/root/db/tube.html tube // tui : TUI AG // https://www.iana.org/domains/root/db/tui.html tui // tunes : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/tunes.html tunes // tushu : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/tushu.html tushu // tvs : T V SUNDRAM IYENGAR & SONS LIMITED // https://www.iana.org/domains/root/db/tvs.html tvs // ubank : National Australia Bank Limited // https://www.iana.org/domains/root/db/ubank.html ubank // ubs : UBS AG // https://www.iana.org/domains/root/db/ubs.html ubs // unicom : China United Network Communications Corporation Limited // https://www.iana.org/domains/root/db/unicom.html unicom // university : Binky Moon, LLC // https://www.iana.org/domains/root/db/university.html university // uno : Radix Technologies Inc. // https://www.iana.org/domains/root/db/uno.html uno // uol : UBN INTERNET LTDA. // https://www.iana.org/domains/root/db/uol.html uol // ups : UPS Market Driver, Inc. // https://www.iana.org/domains/root/db/ups.html ups // vacations : Binky Moon, LLC // https://www.iana.org/domains/root/db/vacations.html vacations // vana : Internet Naming Company LLC // https://www.iana.org/domains/root/db/vana.html vana // vanguard : The Vanguard Group, Inc. // https://www.iana.org/domains/root/db/vanguard.html vanguard // vegas : Dot Vegas, Inc. // https://www.iana.org/domains/root/db/vegas.html vegas // ventures : Binky Moon, LLC // https://www.iana.org/domains/root/db/ventures.html ventures // verisign : VeriSign, Inc. // https://www.iana.org/domains/root/db/verisign.html verisign // versicherung : tldbox GmbH // https://www.iana.org/domains/root/db/versicherung.html versicherung // vet : Dog Beach, LLC // https://www.iana.org/domains/root/db/vet.html vet // viajes : Binky Moon, LLC // https://www.iana.org/domains/root/db/viajes.html viajes // video : Dog Beach, LLC // https://www.iana.org/domains/root/db/video.html video // vig : VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe // https://www.iana.org/domains/root/db/vig.html vig // viking : Viking River Cruises (Bermuda) Ltd. // https://www.iana.org/domains/root/db/viking.html viking // villas : Binky Moon, LLC // https://www.iana.org/domains/root/db/villas.html villas // vin : Binky Moon, LLC // https://www.iana.org/domains/root/db/vin.html vin // vip : Registry Services, LLC // https://www.iana.org/domains/root/db/vip.html vip // virgin : Virgin Enterprises Limited // https://www.iana.org/domains/root/db/virgin.html virgin // visa : Visa Worldwide Pte. Limited // https://www.iana.org/domains/root/db/visa.html visa // vision : Binky Moon, LLC // https://www.iana.org/domains/root/db/vision.html vision // viva : Saudi Telecom Company // https://www.iana.org/domains/root/db/viva.html viva // vivo : Telefonica Brasil S.A. // https://www.iana.org/domains/root/db/vivo.html vivo // vlaanderen : DNS.be vzw // https://www.iana.org/domains/root/db/vlaanderen.html vlaanderen // vodka : Registry Services, LLC // https://www.iana.org/domains/root/db/vodka.html vodka // volvo : Volvo Holding Sverige Aktiebolag // https://www.iana.org/domains/root/db/volvo.html volvo // vote : Monolith Registry LLC // https://www.iana.org/domains/root/db/vote.html vote // voting : Valuetainment Corp. // https://www.iana.org/domains/root/db/voting.html voting // voto : Monolith Registry LLC // https://www.iana.org/domains/root/db/voto.html voto // voyage : Binky Moon, LLC // https://www.iana.org/domains/root/db/voyage.html voyage // wales : Nominet UK // https://www.iana.org/domains/root/db/wales.html wales // walmart : Wal-Mart Stores, Inc. // https://www.iana.org/domains/root/db/walmart.html walmart // walter : Sandvik AB // https://www.iana.org/domains/root/db/walter.html walter // wang : Zodiac Wang Limited // https://www.iana.org/domains/root/db/wang.html wang // wanggou : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/wanggou.html wanggou // watch : Binky Moon, LLC // https://www.iana.org/domains/root/db/watch.html watch // watches : Identity Digital Limited // https://www.iana.org/domains/root/db/watches.html watches // weather : International Business Machines Corporation // https://www.iana.org/domains/root/db/weather.html weather // weatherchannel : International Business Machines Corporation // https://www.iana.org/domains/root/db/weatherchannel.html weatherchannel // webcam : dot Webcam Limited // https://www.iana.org/domains/root/db/webcam.html webcam // weber : Saint-Gobain Weber SA // https://www.iana.org/domains/root/db/weber.html weber // website : Radix Technologies Inc. // https://www.iana.org/domains/root/db/website.html website // wed // https://www.iana.org/domains/root/db/wed.html wed // wedding : Registry Services, LLC // https://www.iana.org/domains/root/db/wedding.html wedding // weibo : Sina Corporation // https://www.iana.org/domains/root/db/weibo.html weibo // weir : Weir Group IP Limited // https://www.iana.org/domains/root/db/weir.html weir // whoswho : Who's Who Registry // https://www.iana.org/domains/root/db/whoswho.html whoswho // wien : punkt.wien GmbH // https://www.iana.org/domains/root/db/wien.html wien // wiki : Registry Services, LLC // https://www.iana.org/domains/root/db/wiki.html wiki // williamhill : William Hill Organization Limited // https://www.iana.org/domains/root/db/williamhill.html williamhill // win : First Registry Limited // https://www.iana.org/domains/root/db/win.html win // windows : Microsoft Corporation // https://www.iana.org/domains/root/db/windows.html windows // wine : Binky Moon, LLC // https://www.iana.org/domains/root/db/wine.html wine // winners : The TJX Companies, Inc. // https://www.iana.org/domains/root/db/winners.html winners // wme : William Morris Endeavor Entertainment, LLC // https://www.iana.org/domains/root/db/wme.html wme // wolterskluwer : Wolters Kluwer N.V. // https://www.iana.org/domains/root/db/wolterskluwer.html wolterskluwer // woodside : Woodside Petroleum Limited // https://www.iana.org/domains/root/db/woodside.html woodside // work : Registry Services, LLC // https://www.iana.org/domains/root/db/work.html work // works : Binky Moon, LLC // https://www.iana.org/domains/root/db/works.html works // world : Binky Moon, LLC // https://www.iana.org/domains/root/db/world.html world // wow : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/wow.html wow // wtc : World Trade Centers Association, Inc. // https://www.iana.org/domains/root/db/wtc.html wtc // wtf : Binky Moon, LLC // https://www.iana.org/domains/root/db/wtf.html wtf // xbox : Microsoft Corporation // https://www.iana.org/domains/root/db/xbox.html xbox // xerox : Xerox DNHC LLC // https://www.iana.org/domains/root/db/xerox.html xerox // xihuan : Beijing Qihu Keji Co., Ltd. // https://www.iana.org/domains/root/db/xihuan.html xihuan // xin : Elegant Leader Limited // https://www.iana.org/domains/root/db/xin.html xin // xn--11b4c3d : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--11b4c3d.html कॉम // xn--1ck2e1b : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--1ck2e1b.html セール // xn--1qqw23a : Guangzhou YU Wei Information Technology Co., Ltd. // https://www.iana.org/domains/root/db/xn--1qqw23a.html 佛山 // xn--30rr7y : Excellent First Limited // https://www.iana.org/domains/root/db/xn--30rr7y.html 慈善 // xn--3bst00m : Eagle Horizon Limited // https://www.iana.org/domains/root/db/xn--3bst00m.html 集团 // xn--3ds443g : TLD REGISTRY LIMITED OY // https://www.iana.org/domains/root/db/xn--3ds443g.html 在线 // xn--3pxu8k : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--3pxu8k.html 点看 // xn--42c2d9a : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--42c2d9a.html คอม // xn--45q11c : Zodiac Gemini Ltd // https://www.iana.org/domains/root/db/xn--45q11c.html 八卦 // xn--4gbrim : Helium TLDs Ltd // https://www.iana.org/domains/root/db/xn--4gbrim.html موقع // xn--55qw42g : China Organizational Name Administration Center // https://www.iana.org/domains/root/db/xn--55qw42g.html 公益 // xn--55qx5d : China Internet Network Information Center (CNNIC) // https://www.iana.org/domains/root/db/xn--55qx5d.html 公司 // xn--5su34j936bgsg : Shangri‐La International Hotel Management Limited // https://www.iana.org/domains/root/db/xn--5su34j936bgsg.html 香格里拉 // xn--5tzm5g : Global Website TLD Asia Limited // https://www.iana.org/domains/root/db/xn--5tzm5g.html 网站 // xn--6frz82g : Identity Digital Limited // https://www.iana.org/domains/root/db/xn--6frz82g.html 移动 // xn--6qq986b3xl : Tycoon Treasure Limited // https://www.iana.org/domains/root/db/xn--6qq986b3xl.html 我爱你 // xn--80adxhks : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) // https://www.iana.org/domains/root/db/xn--80adxhks.html москва // xn--80aqecdr1a : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) // https://www.iana.org/domains/root/db/xn--80aqecdr1a.html католик // xn--80asehdb : CORE Association // https://www.iana.org/domains/root/db/xn--80asehdb.html онлайн // xn--80aswg : CORE Association // https://www.iana.org/domains/root/db/xn--80aswg.html сайт // xn--8y0a063a : China United Network Communications Corporation Limited // https://www.iana.org/domains/root/db/xn--8y0a063a.html 联通 // xn--9dbq2a : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--9dbq2a.html קום // xn--9et52u : RISE VICTORY LIMITED // https://www.iana.org/domains/root/db/xn--9et52u.html 时尚 // xn--9krt00a : Sina Corporation // https://www.iana.org/domains/root/db/xn--9krt00a.html 微博 // xn--b4w605ferd : Temasek Holdings (Private) Limited // https://www.iana.org/domains/root/db/xn--b4w605ferd.html 淡马锡 // xn--bck1b9a5dre4c : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--bck1b9a5dre4c.html ファッション // xn--c1avg : Public Interest Registry // https://www.iana.org/domains/root/db/xn--c1avg.html орг // xn--c2br7g : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--c2br7g.html नेट // xn--cck2b3b : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--cck2b3b.html ストア // xn--cckwcxetd : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--cckwcxetd.html アマゾン // xn--cg4bki : SAMSUNG SDS CO., LTD // https://www.iana.org/domains/root/db/xn--cg4bki.html 삼성 // xn--czr694b : Internet DotTrademark Organisation Limited // https://www.iana.org/domains/root/db/xn--czr694b.html 商标 // xn--czrs0t : Binky Moon, LLC // https://www.iana.org/domains/root/db/xn--czrs0t.html 商店 // xn--czru2d : Zodiac Aquarius Limited // https://www.iana.org/domains/root/db/xn--czru2d.html 商城 // xn--d1acj3b : The Foundation for Network Initiatives “The Smart Internet” // https://www.iana.org/domains/root/db/xn--d1acj3b.html дети // xn--eckvdtc9d : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--eckvdtc9d.html ポイント // xn--efvy88h : Guangzhou YU Wei Information Technology Co., Ltd. // https://www.iana.org/domains/root/db/xn--efvy88h.html 新闻 // xn--fct429k : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--fct429k.html 家電 // xn--fhbei : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--fhbei.html كوم // xn--fiq228c5hs : TLD REGISTRY LIMITED OY // https://www.iana.org/domains/root/db/xn--fiq228c5hs.html 中文网 // xn--fiq64b : CITIC Group Corporation // https://www.iana.org/domains/root/db/xn--fiq64b.html 中信 // xn--fjq720a : Binky Moon, LLC // https://www.iana.org/domains/root/db/xn--fjq720a.html 娱乐 // xn--flw351e : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/xn--flw351e.html 谷歌 // xn--fzys8d69uvgm : PCCW Enterprises Limited // https://www.iana.org/domains/root/db/xn--fzys8d69uvgm.html 電訊盈科 // xn--g2xx48c : Nawang Heli(Xiamen) Network Service Co., LTD. // https://www.iana.org/domains/root/db/xn--g2xx48c.html 购物 // xn--gckr3f0f : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--gckr3f0f.html クラウド // xn--gk3at1e : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--gk3at1e.html 通販 // xn--hxt814e : Zodiac Taurus Limited // https://www.iana.org/domains/root/db/xn--hxt814e.html 网店 // xn--i1b6b1a6a2e : Public Interest Registry // https://www.iana.org/domains/root/db/xn--i1b6b1a6a2e.html संगठन // xn--imr513n : Internet DotTrademark Organisation Limited // https://www.iana.org/domains/root/db/xn--imr513n.html 餐厅 // xn--io0a7i : China Internet Network Information Center (CNNIC) // https://www.iana.org/domains/root/db/xn--io0a7i.html 网络 // xn--j1aef : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--j1aef.html ком // xn--jlq480n2rg : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--jlq480n2rg.html 亚马逊 // xn--jvr189m : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--jvr189m.html 食品 // xn--kcrx77d1x4a : Koninklijke Philips N.V. // https://www.iana.org/domains/root/db/xn--kcrx77d1x4a.html 飞利浦 // xn--kput3i : Beijing RITT-Net Technology Development Co., Ltd // https://www.iana.org/domains/root/db/xn--kput3i.html 手机 // xn--mgba3a3ejt : Aramco Services Company // https://www.iana.org/domains/root/db/xn--mgba3a3ejt.html ارامكو // xn--mgba7c0bbn0a : Competrol (Luxembourg) Sarl // https://www.iana.org/domains/root/db/xn--mgba7c0bbn0a.html العليان // xn--mgbab2bd : CORE Association // https://www.iana.org/domains/root/db/xn--mgbab2bd.html بازار // xn--mgbca7dzdo : Abu Dhabi Systems and Information Centre // https://www.iana.org/domains/root/db/xn--mgbca7dzdo.html ابوظبي // xn--mgbi4ecexp : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) // https://www.iana.org/domains/root/db/xn--mgbi4ecexp.html كاثوليك // xn--mgbt3dhd : Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. // https://www.iana.org/domains/root/db/xn--mgbt3dhd.html همراه // xn--mk1bu44c : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--mk1bu44c.html 닷컴 // xn--mxtq1m : Net-Chinese Co., Ltd. // https://www.iana.org/domains/root/db/xn--mxtq1m.html 政府 // xn--ngbc5azd : International Domain Registry Pty. Ltd. // https://www.iana.org/domains/root/db/xn--ngbc5azd.html شبكة // xn--ngbe9e0a : Kuwait Finance House // https://www.iana.org/domains/root/db/xn--ngbe9e0a.html بيتك // xn--ngbrx : League of Arab States // https://www.iana.org/domains/root/db/xn--ngbrx.html عرب // xn--nqv7f : Public Interest Registry // https://www.iana.org/domains/root/db/xn--nqv7f.html 机构 // xn--nqv7fs00ema : Public Interest Registry // https://www.iana.org/domains/root/db/xn--nqv7fs00ema.html 组织机构 // xn--nyqy26a : Stable Tone Limited // https://www.iana.org/domains/root/db/xn--nyqy26a.html 健康 // xn--otu796d : Jiang Yu Liang Cai Technology Company Limited // https://www.iana.org/domains/root/db/xn--otu796d.html 招聘 // xn--p1acf : Rusnames Limited // https://www.iana.org/domains/root/db/xn--p1acf.html рус // xn--pssy2u : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--pssy2u.html 大拿 // xn--q9jyb4c : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/xn--q9jyb4c.html みんな // xn--qcka1pmc : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/xn--qcka1pmc.html グーグル // xn--rhqv96g : Stable Tone Limited // https://www.iana.org/domains/root/db/xn--rhqv96g.html 世界 // xn--rovu88b : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/xn--rovu88b.html 書籍 // xn--ses554g : KNET Co., Ltd. // https://www.iana.org/domains/root/db/xn--ses554g.html 网址 // xn--t60b56a : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--t60b56a.html 닷넷 // xn--tckwe : VeriSign Sarl // https://www.iana.org/domains/root/db/xn--tckwe.html コム // xn--tiq49xqyj : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication) // https://www.iana.org/domains/root/db/xn--tiq49xqyj.html 天主教 // xn--unup4y : Binky Moon, LLC // https://www.iana.org/domains/root/db/xn--unup4y.html 游戏 // xn--vermgensberater-ctb : Deutsche Vermögensberatung Aktiengesellschaft DVAG // https://www.iana.org/domains/root/db/xn--vermgensberater-ctb.html vermögensberater // xn--vermgensberatung-pwb : Deutsche Vermögensberatung Aktiengesellschaft DVAG // https://www.iana.org/domains/root/db/xn--vermgensberatung-pwb.html vermögensberatung // xn--vhquv : Binky Moon, LLC // https://www.iana.org/domains/root/db/xn--vhquv.html 企业 // xn--vuq861b : Beijing Tele-info Technology Co., Ltd. // https://www.iana.org/domains/root/db/xn--vuq861b.html 信息 // xn--w4r85el8fhu5dnra : Kerry Trading Co. Limited // https://www.iana.org/domains/root/db/xn--w4r85el8fhu5dnra.html 嘉里大酒店 // xn--w4rs40l : Kerry Trading Co. Limited // https://www.iana.org/domains/root/db/xn--w4rs40l.html 嘉里 // xn--xhq521b : Guangzhou YU Wei Information Technology Co., Ltd. // https://www.iana.org/domains/root/db/xn--xhq521b.html 广东 // xn--zfr164b : China Organizational Name Administration Center // https://www.iana.org/domains/root/db/xn--zfr164b.html 政务 // xyz : XYZ.COM LLC // https://www.iana.org/domains/root/db/xyz.html xyz // yachts : XYZ.COM LLC // https://www.iana.org/domains/root/db/yachts.html yachts // yahoo : Oath Inc. // https://www.iana.org/domains/root/db/yahoo.html yahoo // yamaxun : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/yamaxun.html yamaxun // yandex : Yandex Europe B.V. // https://www.iana.org/domains/root/db/yandex.html yandex // yodobashi : YODOBASHI CAMERA CO.,LTD. // https://www.iana.org/domains/root/db/yodobashi.html yodobashi // yoga : Registry Services, LLC // https://www.iana.org/domains/root/db/yoga.html yoga // yokohama : GMO Registry, Inc. // https://www.iana.org/domains/root/db/yokohama.html yokohama // you : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/you.html you // youtube : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/youtube.html youtube // yun : Beijing Qihu Keji Co., Ltd. // https://www.iana.org/domains/root/db/yun.html yun // zappos : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/zappos.html zappos // zara : Industria de Diseño Textil, S.A. (INDITEX, S.A.) // https://www.iana.org/domains/root/db/zara.html zara // zero : Amazon Registry Services, Inc. // https://www.iana.org/domains/root/db/zero.html zero // zip : Charleston Road Registry Inc. // https://www.iana.org/domains/root/db/zip.html zip // zone : Binky Moon, LLC // https://www.iana.org/domains/root/db/zone.html zone // zuerich : Kanton Zürich (Canton of Zurich) // https://www.iana.org/domains/root/db/zuerich.html zuerich // ===END ICANN DOMAINS=== // ===BEGIN PRIVATE DOMAINS=== // (Note: these are in alphabetical order by company name) // 12CHARS: https://12chars.com // Submitted by Kenny Niehage 12chars.dev 12chars.it 12chars.pro // 1GB LLC : https://www.1gb.ua/ // Submitted by 1GB LLC cc.ua inf.ua ltd.ua // 611coin : https://611project.org/ 611.to // A2 Hosting // Submitted by Tyler Hall a2hosted.com cpserver.com // Aaron Marais' Gitlab pages: https://lab.aaronleem.co.za // Submitted by Aaron Marais graphox.us // accesso Technology Group, plc. : https://accesso.com/ // Submitted by accesso Team *.devcdnaccesso.com // Acorn Labs : https://acorn.io // Submitted by Craig Jellick *.on-acorn.io // ActiveTrail: https://www.activetrail.biz/ // Submitted by Ofer Kalaora activetrail.biz // Adaptable.io : https://adaptable.io // Submitted by Mark Terrel adaptable.app // Adobe : https://www.adobe.com/ // Submitted by Ian Boston and Lars Trieloff adobeaemcloud.com *.dev.adobeaemcloud.com aem.live hlx.live adobeaemcloud.net aem.page hlx.page hlx3.page // Adobe Developer Platform : https://developer.adobe.com // Submitted by Jesse MacFadyen adobeio-static.net adobeioruntime.net // Agnat sp. z o.o. : https://domena.pl // Submitted by Przemyslaw Plewa beep.pl // Airkit : https://www.airkit.com/ // Submitted by Grant Cooksey airkitapps.com airkitapps-au.com airkitapps.eu // Aiven: https://aiven.io/ // Submitted by Etienne Stalmans aivencloud.com // Akamai : https://www.akamai.com/ // Submitted by Akamai Team akadns.net akamai.net akamai-staging.net akamaiedge.net akamaiedge-staging.net akamaihd.net akamaihd-staging.net akamaiorigin.net akamaiorigin-staging.net akamaized.net akamaized-staging.net edgekey.net edgekey-staging.net edgesuite.net edgesuite-staging.net // alboto.ca : http://alboto.ca // Submitted by Anton Avramov barsy.ca // Alces Software Ltd : http://alces-software.com // Submitted by Mark J. Titorenko *.compute.estate *.alces.network // all-inkl.com : https://all-inkl.com // Submitted by Werner Kaltofen kasserver.com // Altervista: https://www.altervista.org // Submitted by Carlo Cannas altervista.org // alwaysdata : https://www.alwaysdata.com // Submitted by Cyril alwaysdata.net // Amaze Software : https://amaze.co // Submitted by Domain Admin myamaze.net // Amazon : https://www.amazon.com/ // Submitted by AWS Security // Subsections of Amazon/subsidiaries will appear until "concludes" tag // Amazon API Gateway // Submitted by AWS Security // Reference: 9e37648f-a66c-4655-9ab1-5981f8737197 execute-api.cn-north-1.amazonaws.com.cn execute-api.cn-northwest-1.amazonaws.com.cn execute-api.af-south-1.amazonaws.com execute-api.ap-east-1.amazonaws.com execute-api.ap-northeast-1.amazonaws.com execute-api.ap-northeast-2.amazonaws.com execute-api.ap-northeast-3.amazonaws.com execute-api.ap-south-1.amazonaws.com execute-api.ap-south-2.amazonaws.com execute-api.ap-southeast-1.amazonaws.com execute-api.ap-southeast-2.amazonaws.com execute-api.ap-southeast-3.amazonaws.com execute-api.ap-southeast-4.amazonaws.com execute-api.ca-central-1.amazonaws.com execute-api.ca-west-1.amazonaws.com execute-api.eu-central-1.amazonaws.com execute-api.eu-central-2.amazonaws.com execute-api.eu-north-1.amazonaws.com execute-api.eu-south-1.amazonaws.com execute-api.eu-south-2.amazonaws.com execute-api.eu-west-1.amazonaws.com execute-api.eu-west-2.amazonaws.com execute-api.eu-west-3.amazonaws.com execute-api.il-central-1.amazonaws.com execute-api.me-central-1.amazonaws.com execute-api.me-south-1.amazonaws.com execute-api.sa-east-1.amazonaws.com execute-api.us-east-1.amazonaws.com execute-api.us-east-2.amazonaws.com execute-api.us-gov-east-1.amazonaws.com execute-api.us-gov-west-1.amazonaws.com execute-api.us-west-1.amazonaws.com execute-api.us-west-2.amazonaws.com // Amazon CloudFront // Submitted by Donavan Miller // Reference: 54144616-fd49-4435-8535-19c6a601bdb3 cloudfront.net // Amazon Cognito // Submitted by AWS Security // Reference: 7bee1013-f456-47df-bfe8-03c78d946d61 auth.af-south-1.amazoncognito.com auth.ap-northeast-1.amazoncognito.com auth.ap-northeast-2.amazoncognito.com auth.ap-northeast-3.amazoncognito.com auth.ap-south-1.amazoncognito.com auth.ap-southeast-1.amazoncognito.com auth.ap-southeast-2.amazoncognito.com auth.ap-southeast-3.amazoncognito.com auth.ca-central-1.amazoncognito.com auth.eu-central-1.amazoncognito.com auth.eu-north-1.amazoncognito.com auth.eu-south-1.amazoncognito.com auth.eu-west-1.amazoncognito.com auth.eu-west-2.amazoncognito.com auth.eu-west-3.amazoncognito.com auth.il-central-1.amazoncognito.com auth.me-south-1.amazoncognito.com auth.sa-east-1.amazoncognito.com auth.us-east-1.amazoncognito.com auth-fips.us-east-1.amazoncognito.com auth.us-east-2.amazoncognito.com auth-fips.us-east-2.amazoncognito.com auth-fips.us-gov-west-1.amazoncognito.com auth.us-west-1.amazoncognito.com auth-fips.us-west-1.amazoncognito.com auth.us-west-2.amazoncognito.com auth-fips.us-west-2.amazoncognito.com // Amazon EC2 // Submitted by Luke Wells // Reference: 4c38fa71-58ac-4768-99e5-689c1767e537 *.compute.amazonaws.com *.compute-1.amazonaws.com *.compute.amazonaws.com.cn us-east-1.amazonaws.com // Amazon EMR // Submitted by AWS Security // Reference: 597f3f8e-9283-4e48-8e32-7ee25a1ff6ab emrappui-prod.cn-north-1.amazonaws.com.cn emrnotebooks-prod.cn-north-1.amazonaws.com.cn emrstudio-prod.cn-north-1.amazonaws.com.cn emrappui-prod.cn-northwest-1.amazonaws.com.cn emrnotebooks-prod.cn-northwest-1.amazonaws.com.cn emrstudio-prod.cn-northwest-1.amazonaws.com.cn emrappui-prod.af-south-1.amazonaws.com emrnotebooks-prod.af-south-1.amazonaws.com emrstudio-prod.af-south-1.amazonaws.com emrappui-prod.ap-east-1.amazonaws.com emrnotebooks-prod.ap-east-1.amazonaws.com emrstudio-prod.ap-east-1.amazonaws.com emrappui-prod.ap-northeast-1.amazonaws.com emrnotebooks-prod.ap-northeast-1.amazonaws.com emrstudio-prod.ap-northeast-1.amazonaws.com emrappui-prod.ap-northeast-2.amazonaws.com emrnotebooks-prod.ap-northeast-2.amazonaws.com emrstudio-prod.ap-northeast-2.amazonaws.com emrappui-prod.ap-northeast-3.amazonaws.com emrnotebooks-prod.ap-northeast-3.amazonaws.com emrstudio-prod.ap-northeast-3.amazonaws.com emrappui-prod.ap-south-1.amazonaws.com emrnotebooks-prod.ap-south-1.amazonaws.com emrstudio-prod.ap-south-1.amazonaws.com emrappui-prod.ap-southeast-1.amazonaws.com emrnotebooks-prod.ap-southeast-1.amazonaws.com emrstudio-prod.ap-southeast-1.amazonaws.com emrappui-prod.ap-southeast-2.amazonaws.com emrnotebooks-prod.ap-southeast-2.amazonaws.com emrstudio-prod.ap-southeast-2.amazonaws.com emrappui-prod.ap-southeast-3.amazonaws.com emrnotebooks-prod.ap-southeast-3.amazonaws.com emrstudio-prod.ap-southeast-3.amazonaws.com emrappui-prod.ca-central-1.amazonaws.com emrnotebooks-prod.ca-central-1.amazonaws.com emrstudio-prod.ca-central-1.amazonaws.com emrappui-prod.eu-central-1.amazonaws.com emrnotebooks-prod.eu-central-1.amazonaws.com emrstudio-prod.eu-central-1.amazonaws.com emrappui-prod.eu-north-1.amazonaws.com emrnotebooks-prod.eu-north-1.amazonaws.com emrstudio-prod.eu-north-1.amazonaws.com emrappui-prod.eu-south-1.amazonaws.com emrnotebooks-prod.eu-south-1.amazonaws.com emrstudio-prod.eu-south-1.amazonaws.com emrappui-prod.eu-west-1.amazonaws.com emrnotebooks-prod.eu-west-1.amazonaws.com emrstudio-prod.eu-west-1.amazonaws.com emrappui-prod.eu-west-2.amazonaws.com emrnotebooks-prod.eu-west-2.amazonaws.com emrstudio-prod.eu-west-2.amazonaws.com emrappui-prod.eu-west-3.amazonaws.com emrnotebooks-prod.eu-west-3.amazonaws.com emrstudio-prod.eu-west-3.amazonaws.com emrappui-prod.me-central-1.amazonaws.com emrnotebooks-prod.me-central-1.amazonaws.com emrstudio-prod.me-central-1.amazonaws.com emrappui-prod.me-south-1.amazonaws.com emrnotebooks-prod.me-south-1.amazonaws.com emrstudio-prod.me-south-1.amazonaws.com emrappui-prod.sa-east-1.amazonaws.com emrnotebooks-prod.sa-east-1.amazonaws.com emrstudio-prod.sa-east-1.amazonaws.com emrappui-prod.us-east-1.amazonaws.com emrnotebooks-prod.us-east-1.amazonaws.com emrstudio-prod.us-east-1.amazonaws.com emrappui-prod.us-east-2.amazonaws.com emrnotebooks-prod.us-east-2.amazonaws.com emrstudio-prod.us-east-2.amazonaws.com emrappui-prod.us-gov-east-1.amazonaws.com emrnotebooks-prod.us-gov-east-1.amazonaws.com emrstudio-prod.us-gov-east-1.amazonaws.com emrappui-prod.us-gov-west-1.amazonaws.com emrnotebooks-prod.us-gov-west-1.amazonaws.com emrstudio-prod.us-gov-west-1.amazonaws.com emrappui-prod.us-west-1.amazonaws.com emrnotebooks-prod.us-west-1.amazonaws.com emrstudio-prod.us-west-1.amazonaws.com emrappui-prod.us-west-2.amazonaws.com emrnotebooks-prod.us-west-2.amazonaws.com emrstudio-prod.us-west-2.amazonaws.com // Amazon Managed Workflows for Apache Airflow // Submitted by AWS Security // Reference: 4ab55e6f-90c0-4a8d-b6a0-52ca5dbb1c2e *.cn-north-1.airflow.amazonaws.com.cn *.cn-northwest-1.airflow.amazonaws.com.cn *.ap-northeast-1.airflow.amazonaws.com *.ap-northeast-2.airflow.amazonaws.com *.ap-south-1.airflow.amazonaws.com *.ap-southeast-1.airflow.amazonaws.com *.ap-southeast-2.airflow.amazonaws.com *.ca-central-1.airflow.amazonaws.com *.eu-central-1.airflow.amazonaws.com *.eu-north-1.airflow.amazonaws.com *.eu-west-1.airflow.amazonaws.com *.eu-west-2.airflow.amazonaws.com *.eu-west-3.airflow.amazonaws.com *.sa-east-1.airflow.amazonaws.com *.us-east-1.airflow.amazonaws.com *.us-east-2.airflow.amazonaws.com *.us-west-2.airflow.amazonaws.com // Amazon S3 // Submitted by AWS Security // Reference: cd5c8b3a-67b7-4b40-9236-c87ce81a3d10 s3.dualstack.cn-north-1.amazonaws.com.cn s3-accesspoint.dualstack.cn-north-1.amazonaws.com.cn s3-website.dualstack.cn-north-1.amazonaws.com.cn s3.cn-north-1.amazonaws.com.cn s3-accesspoint.cn-north-1.amazonaws.com.cn s3-deprecated.cn-north-1.amazonaws.com.cn s3-object-lambda.cn-north-1.amazonaws.com.cn s3-website.cn-north-1.amazonaws.com.cn s3.dualstack.cn-northwest-1.amazonaws.com.cn s3-accesspoint.dualstack.cn-northwest-1.amazonaws.com.cn s3.cn-northwest-1.amazonaws.com.cn s3-accesspoint.cn-northwest-1.amazonaws.com.cn s3-object-lambda.cn-northwest-1.amazonaws.com.cn s3-website.cn-northwest-1.amazonaws.com.cn s3.dualstack.af-south-1.amazonaws.com s3-accesspoint.dualstack.af-south-1.amazonaws.com s3-website.dualstack.af-south-1.amazonaws.com s3.af-south-1.amazonaws.com s3-accesspoint.af-south-1.amazonaws.com s3-object-lambda.af-south-1.amazonaws.com s3-website.af-south-1.amazonaws.com s3.dualstack.ap-east-1.amazonaws.com s3-accesspoint.dualstack.ap-east-1.amazonaws.com s3.ap-east-1.amazonaws.com s3-accesspoint.ap-east-1.amazonaws.com s3-object-lambda.ap-east-1.amazonaws.com s3-website.ap-east-1.amazonaws.com s3.dualstack.ap-northeast-1.amazonaws.com s3-accesspoint.dualstack.ap-northeast-1.amazonaws.com s3-website.dualstack.ap-northeast-1.amazonaws.com s3.ap-northeast-1.amazonaws.com s3-accesspoint.ap-northeast-1.amazonaws.com s3-object-lambda.ap-northeast-1.amazonaws.com s3-website.ap-northeast-1.amazonaws.com s3.dualstack.ap-northeast-2.amazonaws.com s3-accesspoint.dualstack.ap-northeast-2.amazonaws.com s3-website.dualstack.ap-northeast-2.amazonaws.com s3.ap-northeast-2.amazonaws.com s3-accesspoint.ap-northeast-2.amazonaws.com s3-object-lambda.ap-northeast-2.amazonaws.com s3-website.ap-northeast-2.amazonaws.com s3.dualstack.ap-northeast-3.amazonaws.com s3-accesspoint.dualstack.ap-northeast-3.amazonaws.com s3-website.dualstack.ap-northeast-3.amazonaws.com s3.ap-northeast-3.amazonaws.com s3-accesspoint.ap-northeast-3.amazonaws.com s3-object-lambda.ap-northeast-3.amazonaws.com s3-website.ap-northeast-3.amazonaws.com s3.dualstack.ap-south-1.amazonaws.com s3-accesspoint.dualstack.ap-south-1.amazonaws.com s3-website.dualstack.ap-south-1.amazonaws.com s3.ap-south-1.amazonaws.com s3-accesspoint.ap-south-1.amazonaws.com s3-object-lambda.ap-south-1.amazonaws.com s3-website.ap-south-1.amazonaws.com s3.dualstack.ap-south-2.amazonaws.com s3-accesspoint.dualstack.ap-south-2.amazonaws.com s3.ap-south-2.amazonaws.com s3-accesspoint.ap-south-2.amazonaws.com s3-object-lambda.ap-south-2.amazonaws.com s3-website.ap-south-2.amazonaws.com s3.dualstack.ap-southeast-1.amazonaws.com s3-accesspoint.dualstack.ap-southeast-1.amazonaws.com s3-website.dualstack.ap-southeast-1.amazonaws.com s3.ap-southeast-1.amazonaws.com s3-accesspoint.ap-southeast-1.amazonaws.com s3-object-lambda.ap-southeast-1.amazonaws.com s3-website.ap-southeast-1.amazonaws.com s3.dualstack.ap-southeast-2.amazonaws.com s3-accesspoint.dualstack.ap-southeast-2.amazonaws.com s3-website.dualstack.ap-southeast-2.amazonaws.com s3.ap-southeast-2.amazonaws.com s3-accesspoint.ap-southeast-2.amazonaws.com s3-object-lambda.ap-southeast-2.amazonaws.com s3-website.ap-southeast-2.amazonaws.com s3.dualstack.ap-southeast-3.amazonaws.com s3-accesspoint.dualstack.ap-southeast-3.amazonaws.com s3.ap-southeast-3.amazonaws.com s3-accesspoint.ap-southeast-3.amazonaws.com s3-object-lambda.ap-southeast-3.amazonaws.com s3-website.ap-southeast-3.amazonaws.com s3.dualstack.ap-southeast-4.amazonaws.com s3-accesspoint.dualstack.ap-southeast-4.amazonaws.com s3.ap-southeast-4.amazonaws.com s3-accesspoint.ap-southeast-4.amazonaws.com s3-object-lambda.ap-southeast-4.amazonaws.com s3-website.ap-southeast-4.amazonaws.com s3.dualstack.ca-central-1.amazonaws.com s3-accesspoint.dualstack.ca-central-1.amazonaws.com s3-accesspoint-fips.dualstack.ca-central-1.amazonaws.com s3-fips.dualstack.ca-central-1.amazonaws.com s3-website.dualstack.ca-central-1.amazonaws.com s3.ca-central-1.amazonaws.com s3-accesspoint.ca-central-1.amazonaws.com s3-accesspoint-fips.ca-central-1.amazonaws.com s3-fips.ca-central-1.amazonaws.com s3-object-lambda.ca-central-1.amazonaws.com s3-website.ca-central-1.amazonaws.com s3.dualstack.ca-west-1.amazonaws.com s3-accesspoint.dualstack.ca-west-1.amazonaws.com s3-accesspoint-fips.dualstack.ca-west-1.amazonaws.com s3-fips.dualstack.ca-west-1.amazonaws.com s3-website.dualstack.ca-west-1.amazonaws.com s3.ca-west-1.amazonaws.com s3-accesspoint.ca-west-1.amazonaws.com s3-accesspoint-fips.ca-west-1.amazonaws.com s3-fips.ca-west-1.amazonaws.com s3-website.ca-west-1.amazonaws.com s3.dualstack.eu-central-1.amazonaws.com s3-accesspoint.dualstack.eu-central-1.amazonaws.com s3-website.dualstack.eu-central-1.amazonaws.com s3.eu-central-1.amazonaws.com s3-accesspoint.eu-central-1.amazonaws.com s3-object-lambda.eu-central-1.amazonaws.com s3-website.eu-central-1.amazonaws.com s3.dualstack.eu-central-2.amazonaws.com s3-accesspoint.dualstack.eu-central-2.amazonaws.com s3.eu-central-2.amazonaws.com s3-accesspoint.eu-central-2.amazonaws.com s3-object-lambda.eu-central-2.amazonaws.com s3-website.eu-central-2.amazonaws.com s3.dualstack.eu-north-1.amazonaws.com s3-accesspoint.dualstack.eu-north-1.amazonaws.com s3.eu-north-1.amazonaws.com s3-accesspoint.eu-north-1.amazonaws.com s3-object-lambda.eu-north-1.amazonaws.com s3-website.eu-north-1.amazonaws.com s3.dualstack.eu-south-1.amazonaws.com s3-accesspoint.dualstack.eu-south-1.amazonaws.com s3-website.dualstack.eu-south-1.amazonaws.com s3.eu-south-1.amazonaws.com s3-accesspoint.eu-south-1.amazonaws.com s3-object-lambda.eu-south-1.amazonaws.com s3-website.eu-south-1.amazonaws.com s3.dualstack.eu-south-2.amazonaws.com s3-accesspoint.dualstack.eu-south-2.amazonaws.com s3.eu-south-2.amazonaws.com s3-accesspoint.eu-south-2.amazonaws.com s3-object-lambda.eu-south-2.amazonaws.com s3-website.eu-south-2.amazonaws.com s3.dualstack.eu-west-1.amazonaws.com s3-accesspoint.dualstack.eu-west-1.amazonaws.com s3-website.dualstack.eu-west-1.amazonaws.com s3.eu-west-1.amazonaws.com s3-accesspoint.eu-west-1.amazonaws.com s3-deprecated.eu-west-1.amazonaws.com s3-object-lambda.eu-west-1.amazonaws.com s3-website.eu-west-1.amazonaws.com s3.dualstack.eu-west-2.amazonaws.com s3-accesspoint.dualstack.eu-west-2.amazonaws.com s3.eu-west-2.amazonaws.com s3-accesspoint.eu-west-2.amazonaws.com s3-object-lambda.eu-west-2.amazonaws.com s3-website.eu-west-2.amazonaws.com s3.dualstack.eu-west-3.amazonaws.com s3-accesspoint.dualstack.eu-west-3.amazonaws.com s3-website.dualstack.eu-west-3.amazonaws.com s3.eu-west-3.amazonaws.com s3-accesspoint.eu-west-3.amazonaws.com s3-object-lambda.eu-west-3.amazonaws.com s3-website.eu-west-3.amazonaws.com s3.dualstack.il-central-1.amazonaws.com s3-accesspoint.dualstack.il-central-1.amazonaws.com s3.il-central-1.amazonaws.com s3-accesspoint.il-central-1.amazonaws.com s3-object-lambda.il-central-1.amazonaws.com s3-website.il-central-1.amazonaws.com s3.dualstack.me-central-1.amazonaws.com s3-accesspoint.dualstack.me-central-1.amazonaws.com s3.me-central-1.amazonaws.com s3-accesspoint.me-central-1.amazonaws.com s3-object-lambda.me-central-1.amazonaws.com s3-website.me-central-1.amazonaws.com s3.dualstack.me-south-1.amazonaws.com s3-accesspoint.dualstack.me-south-1.amazonaws.com s3.me-south-1.amazonaws.com s3-accesspoint.me-south-1.amazonaws.com s3-object-lambda.me-south-1.amazonaws.com s3-website.me-south-1.amazonaws.com s3.amazonaws.com s3-1.amazonaws.com s3-ap-east-1.amazonaws.com s3-ap-northeast-1.amazonaws.com s3-ap-northeast-2.amazonaws.com s3-ap-northeast-3.amazonaws.com s3-ap-south-1.amazonaws.com s3-ap-southeast-1.amazonaws.com s3-ap-southeast-2.amazonaws.com s3-ca-central-1.amazonaws.com s3-eu-central-1.amazonaws.com s3-eu-north-1.amazonaws.com s3-eu-west-1.amazonaws.com s3-eu-west-2.amazonaws.com s3-eu-west-3.amazonaws.com s3-external-1.amazonaws.com s3-fips-us-gov-east-1.amazonaws.com s3-fips-us-gov-west-1.amazonaws.com mrap.accesspoint.s3-global.amazonaws.com s3-me-south-1.amazonaws.com s3-sa-east-1.amazonaws.com s3-us-east-2.amazonaws.com s3-us-gov-east-1.amazonaws.com s3-us-gov-west-1.amazonaws.com s3-us-west-1.amazonaws.com s3-us-west-2.amazonaws.com s3-website-ap-northeast-1.amazonaws.com s3-website-ap-southeast-1.amazonaws.com s3-website-ap-southeast-2.amazonaws.com s3-website-eu-west-1.amazonaws.com s3-website-sa-east-1.amazonaws.com s3-website-us-east-1.amazonaws.com s3-website-us-gov-west-1.amazonaws.com s3-website-us-west-1.amazonaws.com s3-website-us-west-2.amazonaws.com s3.dualstack.sa-east-1.amazonaws.com s3-accesspoint.dualstack.sa-east-1.amazonaws.com s3-website.dualstack.sa-east-1.amazonaws.com s3.sa-east-1.amazonaws.com s3-accesspoint.sa-east-1.amazonaws.com s3-object-lambda.sa-east-1.amazonaws.com s3-website.sa-east-1.amazonaws.com s3.dualstack.us-east-1.amazonaws.com s3-accesspoint.dualstack.us-east-1.amazonaws.com s3-accesspoint-fips.dualstack.us-east-1.amazonaws.com s3-fips.dualstack.us-east-1.amazonaws.com s3-website.dualstack.us-east-1.amazonaws.com s3.us-east-1.amazonaws.com s3-accesspoint.us-east-1.amazonaws.com s3-accesspoint-fips.us-east-1.amazonaws.com s3-deprecated.us-east-1.amazonaws.com s3-fips.us-east-1.amazonaws.com s3-object-lambda.us-east-1.amazonaws.com s3-website.us-east-1.amazonaws.com s3.dualstack.us-east-2.amazonaws.com s3-accesspoint.dualstack.us-east-2.amazonaws.com s3-accesspoint-fips.dualstack.us-east-2.amazonaws.com s3-fips.dualstack.us-east-2.amazonaws.com s3.us-east-2.amazonaws.com s3-accesspoint.us-east-2.amazonaws.com s3-accesspoint-fips.us-east-2.amazonaws.com s3-deprecated.us-east-2.amazonaws.com s3-fips.us-east-2.amazonaws.com s3-object-lambda.us-east-2.amazonaws.com s3-website.us-east-2.amazonaws.com s3.dualstack.us-gov-east-1.amazonaws.com s3-accesspoint.dualstack.us-gov-east-1.amazonaws.com s3-accesspoint-fips.dualstack.us-gov-east-1.amazonaws.com s3-fips.dualstack.us-gov-east-1.amazonaws.com s3.us-gov-east-1.amazonaws.com s3-accesspoint.us-gov-east-1.amazonaws.com s3-accesspoint-fips.us-gov-east-1.amazonaws.com s3-fips.us-gov-east-1.amazonaws.com s3-object-lambda.us-gov-east-1.amazonaws.com s3-website.us-gov-east-1.amazonaws.com s3.dualstack.us-gov-west-1.amazonaws.com s3-accesspoint.dualstack.us-gov-west-1.amazonaws.com s3-accesspoint-fips.dualstack.us-gov-west-1.amazonaws.com s3-fips.dualstack.us-gov-west-1.amazonaws.com s3.us-gov-west-1.amazonaws.com s3-accesspoint.us-gov-west-1.amazonaws.com s3-accesspoint-fips.us-gov-west-1.amazonaws.com s3-fips.us-gov-west-1.amazonaws.com s3-object-lambda.us-gov-west-1.amazonaws.com s3-website.us-gov-west-1.amazonaws.com s3.dualstack.us-west-1.amazonaws.com s3-accesspoint.dualstack.us-west-1.amazonaws.com s3-accesspoint-fips.dualstack.us-west-1.amazonaws.com s3-fips.dualstack.us-west-1.amazonaws.com s3-website.dualstack.us-west-1.amazonaws.com s3.us-west-1.amazonaws.com s3-accesspoint.us-west-1.amazonaws.com s3-accesspoint-fips.us-west-1.amazonaws.com s3-fips.us-west-1.amazonaws.com s3-object-lambda.us-west-1.amazonaws.com s3-website.us-west-1.amazonaws.com s3.dualstack.us-west-2.amazonaws.com s3-accesspoint.dualstack.us-west-2.amazonaws.com s3-accesspoint-fips.dualstack.us-west-2.amazonaws.com s3-fips.dualstack.us-west-2.amazonaws.com s3-website.dualstack.us-west-2.amazonaws.com s3.us-west-2.amazonaws.com s3-accesspoint.us-west-2.amazonaws.com s3-accesspoint-fips.us-west-2.amazonaws.com s3-deprecated.us-west-2.amazonaws.com s3-fips.us-west-2.amazonaws.com s3-object-lambda.us-west-2.amazonaws.com s3-website.us-west-2.amazonaws.com // Amazon SageMaker Notebook Instances // Submitted by AWS Security // Reference: ce8ae0b1-0070-496d-be88-37c31837af9d notebook.af-south-1.sagemaker.aws notebook.ap-east-1.sagemaker.aws notebook.ap-northeast-1.sagemaker.aws notebook.ap-northeast-2.sagemaker.aws notebook.ap-northeast-3.sagemaker.aws notebook.ap-south-1.sagemaker.aws notebook.ap-south-2.sagemaker.aws notebook.ap-southeast-1.sagemaker.aws notebook.ap-southeast-2.sagemaker.aws notebook.ap-southeast-3.sagemaker.aws notebook.ap-southeast-4.sagemaker.aws notebook.ca-central-1.sagemaker.aws notebook-fips.ca-central-1.sagemaker.aws notebook.ca-west-1.sagemaker.aws notebook-fips.ca-west-1.sagemaker.aws notebook.eu-central-1.sagemaker.aws notebook.eu-central-2.sagemaker.aws notebook.eu-north-1.sagemaker.aws notebook.eu-south-1.sagemaker.aws notebook.eu-south-2.sagemaker.aws notebook.eu-west-1.sagemaker.aws notebook.eu-west-2.sagemaker.aws notebook.eu-west-3.sagemaker.aws notebook.il-central-1.sagemaker.aws notebook.me-central-1.sagemaker.aws notebook.me-south-1.sagemaker.aws notebook.sa-east-1.sagemaker.aws notebook.us-east-1.sagemaker.aws notebook-fips.us-east-1.sagemaker.aws notebook.us-east-2.sagemaker.aws notebook-fips.us-east-2.sagemaker.aws notebook.us-gov-east-1.sagemaker.aws notebook-fips.us-gov-east-1.sagemaker.aws notebook.us-gov-west-1.sagemaker.aws notebook-fips.us-gov-west-1.sagemaker.aws notebook.us-west-1.sagemaker.aws notebook.us-west-2.sagemaker.aws notebook-fips.us-west-2.sagemaker.aws notebook.cn-north-1.sagemaker.com.cn notebook.cn-northwest-1.sagemaker.com.cn // Amazon SageMaker Studio // Submitted by AWS Security // Reference: 057ee397-6bf8-4f20-b807-d7bc145ac980 studio.af-south-1.sagemaker.aws studio.ap-east-1.sagemaker.aws studio.ap-northeast-1.sagemaker.aws studio.ap-northeast-2.sagemaker.aws studio.ap-northeast-3.sagemaker.aws studio.ap-south-1.sagemaker.aws studio.ap-southeast-1.sagemaker.aws studio.ap-southeast-2.sagemaker.aws studio.ap-southeast-3.sagemaker.aws studio.ca-central-1.sagemaker.aws studio.eu-central-1.sagemaker.aws studio.eu-north-1.sagemaker.aws studio.eu-south-1.sagemaker.aws studio.eu-west-1.sagemaker.aws studio.eu-west-2.sagemaker.aws studio.eu-west-3.sagemaker.aws studio.il-central-1.sagemaker.aws studio.me-central-1.sagemaker.aws studio.me-south-1.sagemaker.aws studio.sa-east-1.sagemaker.aws studio.us-east-1.sagemaker.aws studio.us-east-2.sagemaker.aws studio.us-gov-east-1.sagemaker.aws studio-fips.us-gov-east-1.sagemaker.aws studio.us-gov-west-1.sagemaker.aws studio-fips.us-gov-west-1.sagemaker.aws studio.us-west-1.sagemaker.aws studio.us-west-2.sagemaker.aws studio.cn-north-1.sagemaker.com.cn studio.cn-northwest-1.sagemaker.com.cn // Analytics on AWS // Submitted by AWS Security // Reference: 955f9f40-a495-4e73-ae85-67b77ac9cadd analytics-gateway.ap-northeast-1.amazonaws.com analytics-gateway.ap-northeast-2.amazonaws.com analytics-gateway.ap-south-1.amazonaws.com analytics-gateway.ap-southeast-1.amazonaws.com analytics-gateway.ap-southeast-2.amazonaws.com analytics-gateway.eu-central-1.amazonaws.com analytics-gateway.eu-west-1.amazonaws.com analytics-gateway.us-east-1.amazonaws.com analytics-gateway.us-east-2.amazonaws.com analytics-gateway.us-west-2.amazonaws.com // AWS Amplify // Submitted by AWS Security // Reference: 5ecce854-c033-4fc4-a755-1a9916d9a9bb *.amplifyapp.com // AWS App Runner // Submitted by AWS Security // Reference: 6828c008-ba5d-442f-ade5-48da4e7c2316 *.awsapprunner.com // AWS Cloud9 // Submitted by: AWS Security // Reference: 30717f72-4007-4f0f-8ed4-864c6f2efec9 webview-assets.aws-cloud9.af-south-1.amazonaws.com vfs.cloud9.af-south-1.amazonaws.com webview-assets.cloud9.af-south-1.amazonaws.com webview-assets.aws-cloud9.ap-east-1.amazonaws.com vfs.cloud9.ap-east-1.amazonaws.com webview-assets.cloud9.ap-east-1.amazonaws.com webview-assets.aws-cloud9.ap-northeast-1.amazonaws.com vfs.cloud9.ap-northeast-1.amazonaws.com webview-assets.cloud9.ap-northeast-1.amazonaws.com webview-assets.aws-cloud9.ap-northeast-2.amazonaws.com vfs.cloud9.ap-northeast-2.amazonaws.com webview-assets.cloud9.ap-northeast-2.amazonaws.com webview-assets.aws-cloud9.ap-northeast-3.amazonaws.com vfs.cloud9.ap-northeast-3.amazonaws.com webview-assets.cloud9.ap-northeast-3.amazonaws.com webview-assets.aws-cloud9.ap-south-1.amazonaws.com vfs.cloud9.ap-south-1.amazonaws.com webview-assets.cloud9.ap-south-1.amazonaws.com webview-assets.aws-cloud9.ap-southeast-1.amazonaws.com vfs.cloud9.ap-southeast-1.amazonaws.com webview-assets.cloud9.ap-southeast-1.amazonaws.com webview-assets.aws-cloud9.ap-southeast-2.amazonaws.com vfs.cloud9.ap-southeast-2.amazonaws.com webview-assets.cloud9.ap-southeast-2.amazonaws.com webview-assets.aws-cloud9.ca-central-1.amazonaws.com vfs.cloud9.ca-central-1.amazonaws.com webview-assets.cloud9.ca-central-1.amazonaws.com webview-assets.aws-cloud9.eu-central-1.amazonaws.com vfs.cloud9.eu-central-1.amazonaws.com webview-assets.cloud9.eu-central-1.amazonaws.com webview-assets.aws-cloud9.eu-north-1.amazonaws.com vfs.cloud9.eu-north-1.amazonaws.com webview-assets.cloud9.eu-north-1.amazonaws.com webview-assets.aws-cloud9.eu-south-1.amazonaws.com vfs.cloud9.eu-south-1.amazonaws.com webview-assets.cloud9.eu-south-1.amazonaws.com webview-assets.aws-cloud9.eu-west-1.amazonaws.com vfs.cloud9.eu-west-1.amazonaws.com webview-assets.cloud9.eu-west-1.amazonaws.com webview-assets.aws-cloud9.eu-west-2.amazonaws.com vfs.cloud9.eu-west-2.amazonaws.com webview-assets.cloud9.eu-west-2.amazonaws.com webview-assets.aws-cloud9.eu-west-3.amazonaws.com vfs.cloud9.eu-west-3.amazonaws.com webview-assets.cloud9.eu-west-3.amazonaws.com webview-assets.aws-cloud9.il-central-1.amazonaws.com vfs.cloud9.il-central-1.amazonaws.com webview-assets.aws-cloud9.me-south-1.amazonaws.com vfs.cloud9.me-south-1.amazonaws.com webview-assets.cloud9.me-south-1.amazonaws.com webview-assets.aws-cloud9.sa-east-1.amazonaws.com vfs.cloud9.sa-east-1.amazonaws.com webview-assets.cloud9.sa-east-1.amazonaws.com webview-assets.aws-cloud9.us-east-1.amazonaws.com vfs.cloud9.us-east-1.amazonaws.com webview-assets.cloud9.us-east-1.amazonaws.com webview-assets.aws-cloud9.us-east-2.amazonaws.com vfs.cloud9.us-east-2.amazonaws.com webview-assets.cloud9.us-east-2.amazonaws.com webview-assets.aws-cloud9.us-west-1.amazonaws.com vfs.cloud9.us-west-1.amazonaws.com webview-assets.cloud9.us-west-1.amazonaws.com webview-assets.aws-cloud9.us-west-2.amazonaws.com vfs.cloud9.us-west-2.amazonaws.com webview-assets.cloud9.us-west-2.amazonaws.com // AWS Elastic Beanstalk // Submitted by AWS Security // Reference: bb5a965c-dec3-4967-aa22-e306ad064797 cn-north-1.eb.amazonaws.com.cn cn-northwest-1.eb.amazonaws.com.cn elasticbeanstalk.com af-south-1.elasticbeanstalk.com ap-east-1.elasticbeanstalk.com ap-northeast-1.elasticbeanstalk.com ap-northeast-2.elasticbeanstalk.com ap-northeast-3.elasticbeanstalk.com ap-south-1.elasticbeanstalk.com ap-southeast-1.elasticbeanstalk.com ap-southeast-2.elasticbeanstalk.com ap-southeast-3.elasticbeanstalk.com ca-central-1.elasticbeanstalk.com eu-central-1.elasticbeanstalk.com eu-north-1.elasticbeanstalk.com eu-south-1.elasticbeanstalk.com eu-west-1.elasticbeanstalk.com eu-west-2.elasticbeanstalk.com eu-west-3.elasticbeanstalk.com il-central-1.elasticbeanstalk.com me-south-1.elasticbeanstalk.com sa-east-1.elasticbeanstalk.com us-east-1.elasticbeanstalk.com us-east-2.elasticbeanstalk.com us-gov-east-1.elasticbeanstalk.com us-gov-west-1.elasticbeanstalk.com us-west-1.elasticbeanstalk.com us-west-2.elasticbeanstalk.com // (AWS) Elastic Load Balancing // Submitted by Luke Wells // Reference: 12a3d528-1bac-4433-a359-a395867ffed2 *.elb.amazonaws.com.cn *.elb.amazonaws.com // AWS Global Accelerator // Submitted by Daniel Massaguer // Reference: d916759d-a08b-4241-b536-4db887383a6a awsglobalaccelerator.com // AWS re:Post Private // Submitted by AWS Security // Reference: 83385945-225f-416e-9aa0-ad0632bfdcee *.private.repost.aws // eero // Submitted by Yue Kang // Reference: 264afe70-f62c-4c02-8ab9-b5281ed24461 eero.online eero-stage.online // concludes Amazon // Amune : https://amune.org/ // Submitted by Team Amune t3l3p0rt.net tele.amune.org // Apigee : https://apigee.com/ // Submitted by Apigee Security Team apigee.io // Apis Networks: https://apisnetworks.com // Submitted by Matt Saladna panel.dev // Apphud : https://apphud.com // Submitted by Alexander Selivanov siiites.com // Appspace : https://www.appspace.com // Submitted by Appspace Security Team appspacehosted.com appspaceusercontent.com // Appudo UG (haftungsbeschränkt) : https://www.appudo.com // Submitted by Alexander Hochbaum appudo.net // Aptible : https://www.aptible.com/ // Submitted by Thomas Orozco on-aptible.com // Aquapal : https://aquapal.net/ // Submitted by Aki Ueno f5.si // ASEINet : https://www.aseinet.com/ // Submitted by Asei SEKIGUCHI user.aseinet.ne.jp gv.vc d.gv.vc // Asociación Amigos de la Informática "Euskalamiga" : http://encounter.eus/ // Submitted by Hector Martin user.party.eus // Association potager.org : https://potager.org/ // Submitted by Lunar pimienta.org poivron.org potager.org sweetpepper.org // ASUSTOR Inc. : http://www.asustor.com // Submitted by Vincent Tseng myasustor.com // Atlassian : https://atlassian.com // Submitted by Sam Smyth cdn.prod.atlassian-dev.net // Authentick UG (haftungsbeschränkt) : https://authentick.net // Submitted by Lukas Reschke translated.page // Autocode : https://autocode.com // Submitted by Jacob Lee autocode.dev // AVM : https://avm.de // Submitted by Andreas Weise myfritz.net // AVStack Pte. Ltd. : https://avstack.io // Submitted by Jasper Hugo onavstack.net // AW AdvisorWebsites.com Software Inc : https://advisorwebsites.com // Submitted by James Kennedy *.awdev.ca *.advisor.ws // AZ.pl sp. z.o.o: https://az.pl // Submitted by Krzysztof Wolski ecommerce-shop.pl // b-data GmbH : https://www.b-data.io // Submitted by Olivier Benz b-data.io // backplane : https://www.backplane.io // Submitted by Anthony Voutas backplaneapp.io // Balena : https://www.balena.io // Submitted by Petros Angelatos balena-devices.com // University of Banja Luka : https://unibl.org // Domains for Republic of Srpska administrative entity. // Submitted by Marko Ivanovic rs.ba // Banzai Cloud // Submitted by Janos Matyas *.banzai.cloud app.banzaicloud.io *.backyards.banzaicloud.io // BASE, Inc. : https://binc.jp // Submitted by Yuya NAGASAWA base.ec official.ec buyshop.jp fashionstore.jp handcrafted.jp kawaiishop.jp supersale.jp theshop.jp shopselect.net base.shop // BeagleBoard.org Foundation : https://beagleboard.org // Submitted by Jason Kridner beagleboard.io // Beget Ltd // Submitted by Lev Nekrasov *.beget.app // Besties : https://besties.house // Submitted by Hazel Cora pages.gay // BetaInABox // Submitted by Adrian betainabox.com // BinaryLane : http://www.binarylane.com // Submitted by Nathan O'Sullivan bnr.la // Bitbucket : http://bitbucket.org // Submitted by Andy Ortlieb bitbucket.io // Blackbaud, Inc. : https://www.blackbaud.com // Submitted by Paul Crowder blackbaudcdn.net // Blatech : http://www.blatech.net // Submitted by Luke Bratch of.je // Blue Bite, LLC : https://bluebite.com // Submitted by Joshua Weiss bluebite.io // Boomla : https://boomla.com // Submitted by Tibor Halter boomla.net // Boutir : https://www.boutir.com // Submitted by Eric Ng Ka Ka boutir.com // Boxfuse : https://boxfuse.com // Submitted by Axel Fontaine boxfuse.io // bplaced : https://www.bplaced.net/ // Submitted by Miroslav Bozic square7.ch bplaced.com bplaced.de square7.de bplaced.net square7.net // Brave : https://brave.com // Submitted by Andrea Brancaleoni *.s.brave.io // Brendly : https://brendly.rs // Submitted by Dusan Radovanovic shop.brendly.rs // BrowserSafetyMark // Submitted by Dave Tharp browsersafetymark.io // Bytemark Hosting : https://www.bytemark.co.uk // Submitted by Paul Cammish uk0.bigv.io dh.bytemark.co.uk vm.bytemark.co.uk // Caf.js Labs LLC : https://www.cafjs.com // Submitted by Antonio Lain cafjs.com // callidomus : https://www.callidomus.com/ // Submitted by Marcus Popp mycd.eu // Canva Pty Ltd : https://canva.com/ // Submitted by Joel Aquilina canva-apps.cn *.my.canvasite.cn canva-apps.com *.my.canva.site // Carrd : https://carrd.co // Submitted by AJ drr.ac uwu.ai carrd.co crd.co ju.mp // CentralNic : http://www.centralnic.com/names/domains // Submitted by registry ae.org br.com cn.com com.de com.se de.com eu.com gb.net hu.net jp.net jpn.com mex.com ru.com sa.com se.net uk.com uk.net us.com za.bz za.com // No longer operated by CentralNic, these entries should be adopted and/or removed by current operators // Submitted by Gavin Brown ar.com hu.com kr.com no.com qc.com uy.com // Africa.com Web Solutions Ltd : https://registry.africa.com // Submitted by Gavin Brown africa.com // iDOT Services Limited : http://www.domain.gr.com // Submitted by Gavin Brown gr.com // Radix FZC : http://domains.in.net // Submitted by Gavin Brown in.net web.in // US REGISTRY LLC : http://us.org // Submitted by Gavin Brown us.org // co.com Registry, LLC : https://registry.co.com // Submitted by Gavin Brown co.com // Roar Domains LLC : https://roar.basketball/ // Submitted by Gavin Brown aus.basketball nz.basketball // BRS Media : https://brsmedia.com/ // Submitted by Gavin Brown radio.am radio.fm // c.la : http://www.c.la/ c.la // certmgr.org : https://certmgr.org // Submitted by B. Blechschmidt certmgr.org // Cityhost LLC : https://cityhost.ua // Submitted by Maksym Rivtin cx.ua // Civilized Discourse Construction Kit, Inc. : https://www.discourse.org/ // Submitted by Rishabh Nambiar & Michael Brown discourse.group discourse.team // Clever Cloud : https://www.clever-cloud.com/ // Submitted by Quentin Adam cleverapps.io // Clerk : https://www.clerk.dev // Submitted by Colin Sidoti clerk.app clerkstage.app *.lcl.dev *.lclstage.dev *.stg.dev *.stgstage.dev // ClickRising : https://clickrising.com/ // Submitted by Umut Gumeli clickrising.net // Cloud66 : https://www.cloud66.com/ // Submitted by Khash Sajadi c66.me cloud66.ws cloud66.zone // CloudAccess.net : https://www.cloudaccess.net/ // Submitted by Pawel Panek jdevcloud.com wpdevcloud.com cloudaccess.host freesite.host cloudaccess.net // cloudControl : https://www.cloudcontrol.com/ // Submitted by Tobias Wilken cloudcontrolled.com cloudcontrolapp.com // Cloudera, Inc. : https://www.cloudera.com/ // Submitted by Kedarnath Waikar *.cloudera.site // Cloudflare, Inc. : https://www.cloudflare.com/ // Submitted by Cloudflare Team cf-ipfs.com cloudflare-ipfs.com trycloudflare.com pages.dev r2.dev workers.dev // Clovyr : https://clovyr.io // Submitted by Patrick Nielsen wnext.app // co.ca : http://registry.co.ca/ co.ca // Co & Co : https://co-co.nl/ // Submitted by Govert Versluis *.otap.co // i-registry s.r.o. : http://www.i-registry.cz/ // Submitted by Martin Semrad co.cz // CDN77.com : http://www.cdn77.com // Submitted by Jan Krpes c.cdn77.org cdn77-ssl.net r.cdn77.net rsc.cdn77.org ssl.origin.cdn77-secure.org // Cloud DNS Ltd : http://www.cloudns.net // Submitted by Aleksander Hristov cloudns.asia cloudns.biz cloudns.club cloudns.cc cloudns.eu cloudns.in cloudns.info cloudns.org cloudns.pro cloudns.pw cloudns.us // CNPY : https://cnpy.gdn // Submitted by Angelo Gladding cnpy.gdn // Codeberg e. V. : https://codeberg.org // Submitted by Moritz Marquardt codeberg.page // CoDNS B.V. co.nl co.no // Combell.com : https://www.combell.com // Submitted by Thomas Wouters webhosting.be hosting-cluster.nl // Convex : https://convex.dev/ // Submitted by James Cowling convex.site // Coordination Center for TLD RU and XN--P1AI : https://cctld.ru/en/domains/domens_ru/reserved/ // Submitted by George Georgievsky ac.ru edu.ru gov.ru int.ru mil.ru test.ru // COSIMO GmbH : http://www.cosimo.de // Submitted by Rene Marticke dyn.cosidns.de dynamisches-dns.de dnsupdater.de internet-dns.de l-o-g-i-n.de dynamic-dns.info feste-ip.net knx-server.net static-access.net // cPanel L.L.C. : https://www.cpanel.net/ // Submitted by Dustin Scherer *.cprapid.com // Craynic, s.r.o. : http://www.craynic.com/ // Submitted by Ales Krajnik realm.cz // Crisp IM SAS : https://crisp.chat/ // Submitted by Baptiste Jamin on.crisp.email // Cryptonomic : https://cryptonomic.net/ // Submitted by Andrew Cady *.cryptonomic.net // Cupcake : https://cupcake.io/ // Submitted by Jonathan Rudenberg cupcake.is // Curv UG : https://curv-labs.de/ // Submitted by Marvin Wiesner curv.dev // Customer OCI - Oracle Dyn https://cloud.oracle.com/home https://dyn.com/dns/ // Submitted by Gregory Drake // Note: This is intended to also include customer-oci.com due to wildcards implicitly including the current label *.customer-oci.com *.oci.customer-oci.com *.ocp.customer-oci.com *.ocs.customer-oci.com // Cyclic Software : https://www.cyclic.sh // Submitted by Kam Lasater cyclic.app cyclic.cloud cyclic-app.com cyclic.co.in // cyon GmbH : https://www.cyon.ch/ // Submitted by Dominic Luechinger cyon.link cyon.site // Danger Science Group: https://dangerscience.com/ // Submitted by Skylar MacDonald fnwk.site folionetwork.site platform0.app // Daplie, Inc : https://daplie.com // Submitted by AJ ONeal daplie.me localhost.daplie.me // Datto, Inc. : https://www.datto.com/ // Submitted by Philipp Heckel dattolocal.com dattorelay.com dattoweb.com mydatto.com dattolocal.net mydatto.net // Dansk.net : http://www.dansk.net/ // Submitted by Anani Voule biz.dk co.dk firm.dk reg.dk store.dk // dappnode.io : https://dappnode.io/ // Submitted by Abel Boldu / DAppNode Team dyndns.dappnode.io // dapps.earth : https://dapps.earth/ // Submitted by Daniil Burdakov *.dapps.earth *.bzz.dapps.earth // Dark, Inc. : https://darklang.com // Submitted by Paul Biggar builtwithdark.com // DataDetect, LLC. : https://datadetect.com // Submitted by Andrew Banchich demo.datadetect.com instance.datadetect.com // Datawire, Inc : https://www.datawire.io // Submitted by Richard Li edgestack.me // DDNS5 : https://ddns5.com // Submitted by Cameron Elliott ddns5.com // Debian : https://www.debian.org/ // Submitted by Peter Palfrader / Debian Sysadmin Team debian.net // Deno Land Inc : https://deno.com/ // Submitted by Luca Casonato deno.dev deno-staging.dev // deSEC : https://desec.io/ // Submitted by Peter Thomassen dedyn.io // Deta: https://www.deta.sh/ // Submitted by Aavash Shrestha deta.app deta.dev // Diher Solutions : https://diher.solutions // Submitted by Didi Hermawan *.rss.my.id *.diher.solutions // Discord Inc : https://discord.com // Submitted by Sahn Lam discordsays.com discordsez.com // DNS Africa Ltd https://dns.business // Submitted by Calvin Browne jozi.biz // DNShome : https://www.dnshome.de/ // Submitted by Norbert Auler dnshome.de // DotArai : https://www.dotarai.com/ // Submitted by Atsadawat Netcharadsang online.th shop.th // DrayTek Corp. : https://www.draytek.com/ // Submitted by Paul Fang drayddns.com // DreamCommerce : https://shoper.pl/ // Submitted by Konrad Kotarba shoparena.pl // DreamHost : http://www.dreamhost.com/ // Submitted by Andrew Farmer dreamhosters.com // Drobo : http://www.drobo.com/ // Submitted by Ricardo Padilha mydrobo.com // Drud Holdings, LLC. : https://www.drud.com/ // Submitted by Kevin Bridges drud.io drud.us // DuckDNS : http://www.duckdns.org/ // Submitted by Richard Harper duckdns.org // Bip : https://bip.sh // Submitted by Joel Kennedy bip.sh // bitbridge.net : Submitted by Craig Welch, abeliidev@gmail.com bitbridge.net // dy.fi : http://dy.fi/ // Submitted by Heikki Hannikainen dy.fi tunk.org // DynDNS.com : http://www.dyndns.com/services/dns/dyndns/ dyndns-at-home.com dyndns-at-work.com dyndns-blog.com dyndns-free.com dyndns-home.com dyndns-ip.com dyndns-mail.com dyndns-office.com dyndns-pics.com dyndns-remote.com dyndns-server.com dyndns-web.com dyndns-wiki.com dyndns-work.com dyndns.biz dyndns.info dyndns.org dyndns.tv at-band-camp.net ath.cx barrel-of-knowledge.info barrell-of-knowledge.info better-than.tv blogdns.com blogdns.net blogdns.org blogsite.org boldlygoingnowhere.org broke-it.net buyshouses.net cechire.com dnsalias.com dnsalias.net dnsalias.org dnsdojo.com dnsdojo.net dnsdojo.org does-it.net doesntexist.com doesntexist.org dontexist.com dontexist.net dontexist.org doomdns.com doomdns.org dvrdns.org dyn-o-saur.com dynalias.com dynalias.net dynalias.org dynathome.net dyndns.ws endofinternet.net endofinternet.org endoftheinternet.org est-a-la-maison.com est-a-la-masion.com est-le-patron.com est-mon-blogueur.com for-better.biz for-more.biz for-our.info for-some.biz for-the.biz forgot.her.name forgot.his.name from-ak.com from-al.com from-ar.com from-az.net from-ca.com from-co.net from-ct.com from-dc.com from-de.com from-fl.com from-ga.com from-hi.com from-ia.com from-id.com from-il.com from-in.com from-ks.com from-ky.com from-la.net from-ma.com from-md.com from-me.org from-mi.com from-mn.com from-mo.com from-ms.com from-mt.com from-nc.com from-nd.com from-ne.com from-nh.com from-nj.com from-nm.com from-nv.com from-ny.net from-oh.com from-ok.com from-or.com from-pa.com from-pr.com from-ri.com from-sc.com from-sd.com from-tn.com from-tx.com from-ut.com from-va.com from-vt.com from-wa.com from-wi.com from-wv.com from-wy.com ftpaccess.cc fuettertdasnetz.de game-host.org game-server.cc getmyip.com gets-it.net go.dyndns.org gotdns.com gotdns.org groks-the.info groks-this.info ham-radio-op.net here-for-more.info hobby-site.com hobby-site.org home.dyndns.org homedns.org homeftp.net homeftp.org homeip.net homelinux.com homelinux.net homelinux.org homeunix.com homeunix.net homeunix.org iamallama.com in-the-band.net is-a-anarchist.com is-a-blogger.com is-a-bookkeeper.com is-a-bruinsfan.org is-a-bulls-fan.com is-a-candidate.org is-a-caterer.com is-a-celticsfan.org is-a-chef.com is-a-chef.net is-a-chef.org is-a-conservative.com is-a-cpa.com is-a-cubicle-slave.com is-a-democrat.com is-a-designer.com is-a-doctor.com is-a-financialadvisor.com is-a-geek.com is-a-geek.net is-a-geek.org is-a-green.com is-a-guru.com is-a-hard-worker.com is-a-hunter.com is-a-knight.org is-a-landscaper.com is-a-lawyer.com is-a-liberal.com is-a-libertarian.com is-a-linux-user.org is-a-llama.com is-a-musician.com is-a-nascarfan.com is-a-nurse.com is-a-painter.com is-a-patsfan.org is-a-personaltrainer.com is-a-photographer.com is-a-player.com is-a-republican.com is-a-rockstar.com is-a-socialist.com is-a-soxfan.org is-a-student.com is-a-teacher.com is-a-techie.com is-a-therapist.com is-an-accountant.com is-an-actor.com is-an-actress.com is-an-anarchist.com is-an-artist.com is-an-engineer.com is-an-entertainer.com is-by.us is-certified.com is-found.org is-gone.com is-into-anime.com is-into-cars.com is-into-cartoons.com is-into-games.com is-leet.com is-lost.org is-not-certified.com is-saved.org is-slick.com is-uberleet.com is-very-bad.org is-very-evil.org is-very-good.org is-very-nice.org is-very-sweet.org is-with-theband.com isa-geek.com isa-geek.net isa-geek.org isa-hockeynut.com issmarterthanyou.com isteingeek.de istmein.de kicks-ass.net kicks-ass.org knowsitall.info land-4-sale.us lebtimnetz.de leitungsen.de likes-pie.com likescandy.com merseine.nu mine.nu misconfused.org mypets.ws myphotos.cc neat-url.com office-on-the.net on-the-web.tv podzone.net podzone.org readmyblog.org saves-the-whales.com scrapper-site.net scrapping.cc selfip.biz selfip.com selfip.info selfip.net selfip.org sells-for-less.com sells-for-u.com sells-it.net sellsyourhome.org servebbs.com servebbs.net servebbs.org serveftp.net serveftp.org servegame.org shacknet.nu simple-url.com space-to-rent.com stuff-4-sale.org stuff-4-sale.us teaches-yoga.com thruhere.net traeumtgerade.de webhop.biz webhop.info webhop.net webhop.org worse-than.tv writesthisblog.com // ddnss.de : https://www.ddnss.de/ // Submitted by Robert Niedziela ddnss.de dyn.ddnss.de dyndns.ddnss.de dyndns1.de dyn-ip24.de home-webserver.de dyn.home-webserver.de myhome-server.de ddnss.org // Definima : http://www.definima.com/ // Submitted by Maxence Bitterli definima.net definima.io // DigitalOcean App Platform : https://www.digitalocean.com/products/app-platform/ // Submitted by Braxton Huggins ondigitalocean.app // DigitalOcean Spaces : https://www.digitalocean.com/products/spaces/ // Submitted by Robin H. Johnson *.digitaloceanspaces.com // dnstrace.pro : https://dnstrace.pro/ // Submitted by Chris Partridge bci.dnstrace.pro // Dynu.com : https://www.dynu.com/ // Submitted by Sue Ye ddnsfree.com ddnsgeek.com giize.com gleeze.com kozow.com loseyourip.com ooguy.com theworkpc.com casacam.net dynu.net accesscam.org camdvr.org freeddns.org mywire.org webredirect.org myddns.rocks blogsite.xyz // dynv6 : https://dynv6.com // Submitted by Dominik Menke dynv6.net // E4YOU spol. s.r.o. : https://e4you.cz/ // Submitted by Vladimir Dudr e4.cz // Easypanel : https://easypanel.io // Submitted by Andrei Canta easypanel.app easypanel.host // EasyWP : https://www.easywp.com // Submitted by *.ewp.live // Elementor : Elementor Ltd. // Submitted by Anton Barkan elementor.cloud elementor.cool // En root‽ : https://en-root.org // Submitted by Emmanuel Raviart en-root.fr // Enalean SAS: https://www.enalean.com // Submitted by Thomas Cottier mytuleap.com tuleap-partners.com // Encoretivity AB: https://encore.dev // Submitted by André Eriksson encr.app encoreapi.com // ECG Robotics, Inc: https://ecgrobotics.org // Submitted by onred.one staging.onred.one // encoway GmbH : https://www.encoway.de // Submitted by Marcel Daus eu.encoway.cloud // EU.org https://eu.org/ // Submitted by Pierre Beyssac eu.org al.eu.org asso.eu.org at.eu.org au.eu.org be.eu.org bg.eu.org ca.eu.org cd.eu.org ch.eu.org cn.eu.org cy.eu.org cz.eu.org de.eu.org dk.eu.org edu.eu.org ee.eu.org es.eu.org fi.eu.org fr.eu.org gr.eu.org hr.eu.org hu.eu.org ie.eu.org il.eu.org in.eu.org int.eu.org is.eu.org it.eu.org jp.eu.org kr.eu.org lt.eu.org lu.eu.org lv.eu.org mc.eu.org me.eu.org mk.eu.org mt.eu.org my.eu.org net.eu.org ng.eu.org nl.eu.org no.eu.org nz.eu.org paris.eu.org pl.eu.org pt.eu.org q-a.eu.org ro.eu.org ru.eu.org se.eu.org si.eu.org sk.eu.org tr.eu.org uk.eu.org us.eu.org // Eurobyte : https://eurobyte.ru // Submitted by Evgeniy Subbotin eurodir.ru // Evennode : http://www.evennode.com/ // Submitted by Michal Kralik eu-1.evennode.com eu-2.evennode.com eu-3.evennode.com eu-4.evennode.com us-1.evennode.com us-2.evennode.com us-3.evennode.com us-4.evennode.com // eDirect Corp. : https://hosting.url.com.tw/ // Submitted by C.S. chang twmail.cc twmail.net twmail.org mymailer.com.tw url.tw // Fabrica Technologies, Inc. : https://www.fabrica.dev/ // Submitted by Eric Jiang onfabrica.com // FAITID : https://faitid.org/ // Submitted by Maxim Alzoba // https://www.flexireg.net/stat_info ru.net adygeya.ru bashkiria.ru bir.ru cbg.ru com.ru dagestan.ru grozny.ru kalmykia.ru kustanai.ru marine.ru mordovia.ru msk.ru mytis.ru nalchik.ru nov.ru pyatigorsk.ru spb.ru vladikavkaz.ru vladimir.ru abkhazia.su adygeya.su aktyubinsk.su arkhangelsk.su armenia.su ashgabad.su azerbaijan.su balashov.su bashkiria.su bryansk.su bukhara.su chimkent.su dagestan.su east-kazakhstan.su exnet.su georgia.su grozny.su ivanovo.su jambyl.su kalmykia.su kaluga.su karacol.su karaganda.su karelia.su khakassia.su krasnodar.su kurgan.su kustanai.su lenug.su mangyshlak.su mordovia.su msk.su murmansk.su nalchik.su navoi.su north-kazakhstan.su nov.su obninsk.su penza.su pokrovsk.su sochi.su spb.su tashkent.su termez.su togliatti.su troitsk.su tselinograd.su tula.su tuva.su vladikavkaz.su vladimir.su vologda.su // Fancy Bits, LLC : http://getchannels.com // Submitted by Aman Gupta channelsdvr.net u.channelsdvr.net // Fastly Inc. : http://www.fastly.com/ // Submitted by Fastly Security edgecompute.app fastly-edge.com fastly-terrarium.com fastlylb.net map.fastlylb.net freetls.fastly.net map.fastly.net a.prod.fastly.net global.prod.fastly.net a.ssl.fastly.net b.ssl.fastly.net global.ssl.fastly.net // Fastmail : https://www.fastmail.com/ // Submitted by Marc Bradshaw *.user.fm // FASTVPS EESTI OU : https://fastvps.ru/ // Submitted by Likhachev Vasiliy fastvps-server.com fastvps.host myfast.host fastvps.site myfast.space // Fedora : https://fedoraproject.org/ // submitted by Patrick Uiterwijk fedorainfracloud.org fedorapeople.org cloud.fedoraproject.org app.os.fedoraproject.org app.os.stg.fedoraproject.org // FearWorks Media Ltd. : https://fearworksmedia.co.uk // submitted by Keith Fairley conn.uk copro.uk hosp.uk // Fermax : https://fermax.com/ // submitted by Koen Van Isterdael mydobiss.com // FH Muenster : https://www.fh-muenster.de // Submitted by Robin Naundorf fh-muenster.io // Filegear Inc. : https://www.filegear.com // Submitted by Jason Zhu filegear.me filegear-au.me filegear-de.me filegear-gb.me filegear-ie.me filegear-jp.me filegear-sg.me // Firebase, Inc. // Submitted by Chris Raynor firebaseapp.com // Firewebkit : https://www.firewebkit.com // Submitted by Majid Qureshi fireweb.app // FLAP : https://www.flap.cloud // Submitted by Louis Chemineau flap.id // FlashDrive : https://flashdrive.io // Submitted by Eric Chan onflashdrive.app fldrv.com // FlutterFlow : https://flutterflow.io // Submitted by Anton Emelyanov flutterflow.app // fly.io: https://fly.io // Submitted by Kurt Mackey fly.dev edgeapp.net shw.io // Flynn : https://flynn.io // Submitted by Jonathan Rudenberg flynnhosting.net // Forgerock : https://www.forgerock.com // Submitted by Roderick Parr forgeblocks.com id.forgerock.io // Framer : https://www.framer.com // Submitted by Koen Rouwhorst framer.app framercanvas.com framer.media framer.photos framer.website framer.wiki // Frusky MEDIA&PR : https://www.frusky.de // Submitted by Victor Pupynin *.frusky.de // RavPage : https://www.ravpage.co.il // Submitted by Roni Horowitz ravpage.co.il // Frederik Braun https://frederik-braun.com // Submitted by Frederik Braun 0e.vc // Freebox : http://www.freebox.fr // Submitted by Romain Fliedel freebox-os.com freeboxos.com fbx-os.fr fbxos.fr freebox-os.fr freeboxos.fr // freedesktop.org : https://www.freedesktop.org // Submitted by Daniel Stone freedesktop.org // freemyip.com : https://freemyip.com // Submitted by Cadence freemyip.com // FunkFeuer - Verein zur Förderung freier Netze : https://www.funkfeuer.at // Submitted by Daniel A. Maierhofer wien.funkfeuer.at // Futureweb GmbH : https://www.futureweb.at // Submitted by Andreas Schnederle-Wagner *.futurecms.at *.ex.futurecms.at *.in.futurecms.at futurehosting.at futuremailing.at *.ex.ortsinfo.at *.kunden.ortsinfo.at *.statics.cloud // GCom Internet : https://www.gcom.net.au // Submitted by Leo Julius aliases121.com // GDS : https://www.gov.uk/service-manual/technology/managing-domain-names // Submitted by Stephen Ford independent-commission.uk independent-inquest.uk independent-inquiry.uk independent-panel.uk independent-review.uk public-inquiry.uk royal-commission.uk campaign.gov.uk service.gov.uk // CDDO : https://www.gov.uk/guidance/get-an-api-domain-on-govuk // Submitted by Jamie Tanna api.gov.uk // Gehirn Inc. : https://www.gehirn.co.jp/ // Submitted by Kohei YOSHIDA gehirn.ne.jp usercontent.jp // Gentlent, Inc. : https://www.gentlent.com // Submitted by Tom Klein gentapps.com gentlentapis.com lab.ms cdn-edges.net // Ghost Foundation : https://ghost.org // Submitted by Matt Hanley ghost.io // GignoSystemJapan: http://gsj.bz // Submitted by GignoSystemJapan gsj.bz // GitHub, Inc. // Submitted by Patrick Toomey githubusercontent.com githubpreview.dev github.io // GitLab, Inc. // Submitted by Alex Hanselka gitlab.io // Gitplac.si - https://gitplac.si // Submitted by Aljaž Starc gitapp.si gitpage.si // Glitch, Inc : https://glitch.com // Submitted by Mads Hartmann glitch.me // Global NOG Alliance : https://nogalliance.org/ // Submitted by Sander Steffann nog.community // Globe Hosting SRL : https://www.globehosting.com/ // Submitted by Gavin Brown co.ro shop.ro // GMO Pepabo, Inc. : https://pepabo.com/ // Submitted by Hosting Div lolipop.io angry.jp babyblue.jp babymilk.jp backdrop.jp bambina.jp bitter.jp blush.jp boo.jp boy.jp boyfriend.jp but.jp candypop.jp capoo.jp catfood.jp cheap.jp chicappa.jp chillout.jp chips.jp chowder.jp chu.jp ciao.jp cocotte.jp coolblog.jp cranky.jp cutegirl.jp daa.jp deca.jp deci.jp digick.jp egoism.jp fakefur.jp fem.jp flier.jp floppy.jp fool.jp frenchkiss.jp girlfriend.jp girly.jp gloomy.jp gonna.jp greater.jp hacca.jp heavy.jp her.jp hiho.jp hippy.jp holy.jp hungry.jp icurus.jp itigo.jp jellybean.jp kikirara.jp kill.jp kilo.jp kuron.jp littlestar.jp lolipopmc.jp lolitapunk.jp lomo.jp lovepop.jp lovesick.jp main.jp mods.jp mond.jp mongolian.jp moo.jp namaste.jp nikita.jp nobushi.jp noor.jp oops.jp parallel.jp parasite.jp pecori.jp peewee.jp penne.jp pepper.jp perma.jp pigboat.jp pinoko.jp punyu.jp pupu.jp pussycat.jp pya.jp raindrop.jp readymade.jp sadist.jp schoolbus.jp secret.jp staba.jp stripper.jp sub.jp sunnyday.jp thick.jp tonkotsu.jp under.jp upper.jp velvet.jp verse.jp versus.jp vivian.jp watson.jp weblike.jp whitesnow.jp zombie.jp heteml.net // GOV.UK Platform as a Service : https://www.cloud.service.gov.uk/ // Submitted by Tom Whitwell cloudapps.digital london.cloudapps.digital // GOV.UK Pay : https://www.payments.service.gov.uk/ // Submitted by Richard Baker pymnt.uk // GlobeHosting, Inc. // Submitted by Zoltan Egresi ro.im // GoIP DNS Services : http://www.goip.de // Submitted by Christian Poulter goip.de // Google, Inc. // Submitted by Eduardo Vela *.run.app web.app *.0emm.com appspot.com *.r.appspot.com codespot.com googleapis.com googlecode.com pagespeedmobilizer.com publishproxy.com withgoogle.com withyoutube.com *.gateway.dev cloud.goog translate.goog *.usercontent.goog cloudfunctions.net blogspot.ae blogspot.al blogspot.am blogspot.ba blogspot.be blogspot.bg blogspot.bj blogspot.ca blogspot.cf blogspot.ch blogspot.cl blogspot.co.at blogspot.co.id blogspot.co.il blogspot.co.ke blogspot.co.nz blogspot.co.uk blogspot.co.za blogspot.com blogspot.com.ar blogspot.com.au blogspot.com.br blogspot.com.by blogspot.com.co blogspot.com.cy blogspot.com.ee blogspot.com.eg blogspot.com.es blogspot.com.mt blogspot.com.ng blogspot.com.tr blogspot.com.uy blogspot.cv blogspot.cz blogspot.de blogspot.dk blogspot.fi blogspot.fr blogspot.gr blogspot.hk blogspot.hr blogspot.hu blogspot.ie blogspot.in blogspot.is blogspot.it blogspot.jp blogspot.kr blogspot.li blogspot.lt blogspot.lu blogspot.md blogspot.mk blogspot.mr blogspot.mx blogspot.my blogspot.nl blogspot.no blogspot.pe blogspot.pt blogspot.qa blogspot.re blogspot.ro blogspot.rs blogspot.ru blogspot.se blogspot.sg blogspot.si blogspot.sk blogspot.sn blogspot.td blogspot.tw blogspot.ug blogspot.vn // Goupile : https://goupile.fr // Submitted by Niels Martignene goupile.fr // Government of the Netherlands: https://www.government.nl // Submitted by gov.nl // Group 53, LLC : https://www.group53.com // Submitted by Tyler Todd awsmppl.com // GünstigBestellen : https://günstigbestellen.de // Submitted by Furkan Akkoc günstigbestellen.de günstigliefern.de // Hakaran group: http://hakaran.cz // Submitted by Arseniy Sokolov fin.ci free.hr caa.li ua.rs conf.se // Handshake : https://handshake.org // Submitted by Mike Damm hs.zone hs.run // Hashbang : https://hashbang.sh hashbang.sh // Hasura : https://hasura.io // Submitted by Shahidh K Muhammed hasura.app hasura-app.io // Heilbronn University of Applied Sciences - Faculty Informatics (GitLab Pages): https://www.hs-heilbronn.de // Submitted by Richard Zowalla pages.it.hs-heilbronn.de // Hepforge : https://www.hepforge.org // Submitted by David Grellscheid hepforge.org // Heroku : https://www.heroku.com/ // Submitted by Tom Maher herokuapp.com herokussl.com // Hibernating Rhinos // Submitted by Oren Eini ravendb.cloud ravendb.community ravendb.me development.run ravendb.run // home.pl S.A.: https://home.pl // Submitted by Krzysztof Wolski homesklep.pl // Homebase : https://homebase.id/ // Submitted by Jason Babo *.kin.one *.id.pub *.kin.pub // Hong Kong Productivity Council: https://www.hkpc.org/ // Submitted by SECaaS Team secaas.hk // Hoplix : https://www.hoplix.com // Submitted by Danilo De Franco hoplix.shop // HOSTBIP REGISTRY : https://www.hostbip.com/ // Submitted by Atanunu Igbunuroghene orx.biz biz.gl col.ng firm.ng gen.ng ltd.ng ngo.ng edu.scot sch.so // HostFly : https://www.ie.ua // Submitted by Bohdan Dub ie.ua // HostyHosting (hostyhosting.com) hostyhosting.io // Häkkinen.fi // Submitted by Eero Häkkinen häkkinen.fi // Ici la Lune : http://www.icilalune.com/ // Submitted by Simon Morvan *.moonscale.io moonscale.net // iki.fi // Submitted by Hannu Aronsson iki.fi // iliad italia: https://www.iliad.it // Submitted by Marios Makassikis ibxos.it iliadboxos.it // Impertrix Solutions : // Submitted by Zhixiang Zhao impertrixcdn.com impertrix.com // Incsub, LLC: https://incsub.com/ // Submitted by Aaron Edwards smushcdn.com wphostedmail.com wpmucdn.com tempurl.host wpmudev.host // Individual Network Berlin e.V. : https://www.in-berlin.de/ // Submitted by Christian Seitz dyn-berlin.de in-berlin.de in-brb.de in-butter.de in-dsl.de in-dsl.net in-dsl.org in-vpn.de in-vpn.net in-vpn.org // info.at : http://www.info.at/ biz.at info.at // info.cx : http://info.cx // Submitted by Jacob Slater info.cx // Interlegis : http://www.interlegis.leg.br // Submitted by Gabriel Ferreira ac.leg.br al.leg.br am.leg.br ap.leg.br ba.leg.br ce.leg.br df.leg.br es.leg.br go.leg.br ma.leg.br mg.leg.br ms.leg.br mt.leg.br pa.leg.br pb.leg.br pe.leg.br pi.leg.br pr.leg.br rj.leg.br rn.leg.br ro.leg.br rr.leg.br rs.leg.br sc.leg.br se.leg.br sp.leg.br to.leg.br // intermetrics GmbH : https://pixolino.com/ // Submitted by Wolfgang Schwarz pixolino.com // Internet-Pro, LLP: https://netangels.ru/ // Submitted by Vasiliy Sheredeko na4u.ru // iopsys software solutions AB : https://iopsys.eu/ // Submitted by Roman Azarenko iopsys.se // IPiFony Systems, Inc. : https://www.ipifony.com/ // Submitted by Matthew Hardeman ipifony.net // IServ GmbH : https://iserv.de // Submitted by Mario Hoberg iservschule.de mein-iserv.de schulplattform.de schulserver.de test-iserv.de iserv.dev // I-O DATA DEVICE, INC. : http://www.iodata.com/ // Submitted by Yuji Minagawa iobb.net // Jelastic, Inc. : https://jelastic.com/ // Submitted by Ihor Kolodyuk mel.cloudlets.com.au cloud.interhostsolutions.be mycloud.by alp1.ae.flow.ch appengine.flow.ch es-1.axarnet.cloud diadem.cloud vip.jelastic.cloud jele.cloud it1.eur.aruba.jenv-aruba.cloud it1.jenv-aruba.cloud keliweb.cloud cs.keliweb.cloud oxa.cloud tn.oxa.cloud uk.oxa.cloud primetel.cloud uk.primetel.cloud ca.reclaim.cloud uk.reclaim.cloud us.reclaim.cloud ch.trendhosting.cloud de.trendhosting.cloud jele.club amscompute.com dopaas.com paas.hosted-by-previder.com rag-cloud.hosteur.com rag-cloud-ch.hosteur.com jcloud.ik-server.com jcloud-ver-jpc.ik-server.com demo.jelastic.com kilatiron.com paas.massivegrid.com jed.wafaicloud.com lon.wafaicloud.com ryd.wafaicloud.com j.scaleforce.com.cy jelastic.dogado.eu fi.cloudplatform.fi demo.datacenter.fi paas.datacenter.fi jele.host mircloud.host paas.beebyte.io sekd1.beebyteapp.io jele.io cloud-fr1.unispace.io jc.neen.it cloud.jelastic.open.tim.it jcloud.kz upaas.kazteleport.kz cloudjiffy.net fra1-de.cloudjiffy.net west1-us.cloudjiffy.net jls-sto1.elastx.net jls-sto2.elastx.net jls-sto3.elastx.net faststacks.net fr-1.paas.massivegrid.net lon-1.paas.massivegrid.net lon-2.paas.massivegrid.net ny-1.paas.massivegrid.net ny-2.paas.massivegrid.net sg-1.paas.massivegrid.net jelastic.saveincloud.net nordeste-idc.saveincloud.net j.scaleforce.net jelastic.tsukaeru.net sdscloud.pl unicloud.pl mircloud.ru jelastic.regruhosting.ru enscaled.sg jele.site jelastic.team orangecloud.tn j.layershift.co.uk phx.enscaled.us mircloud.us // Jino : https://www.jino.ru // Submitted by Sergey Ulyashin myjino.ru *.hosting.myjino.ru *.landing.myjino.ru *.spectrum.myjino.ru *.vps.myjino.ru // Jotelulu S.L. : https://jotelulu.com // Submitted by Daniel Fariña jotelulu.cloud // Joyent : https://www.joyent.com/ // Submitted by Brian Bennett *.triton.zone *.cns.joyent.com // JS.ORG : http://dns.js.org // Submitted by Stefan Keim js.org // KaasHosting : http://www.kaashosting.nl/ // Submitted by Wouter Bakker kaas.gg khplay.nl // Kakao : https://www.kakaocorp.com/ // Submitted by JaeYoong Lee ktistory.com // Kapsi : https://kapsi.fi // Submitted by Tomi Juntunen kapsi.fi // Keyweb AG : https://www.keyweb.de // Submitted by Martin Dannehl keymachine.de // KingHost : https://king.host // Submitted by Felipe Keller Braz kinghost.net uni5.net // KnightPoint Systems, LLC : http://www.knightpoint.com/ // Submitted by Roy Keene knightpoint.systems // KoobinEvent, SL: https://www.koobin.com // Submitted by Iván Oliva koobin.events // KUROKU LTD : https://kuroku.ltd/ // Submitted by DisposaBoy oya.to // Katholieke Universiteit Leuven: https://www.kuleuven.be // Submitted by Abuse KU Leuven kuleuven.cloud ezproxy.kuleuven.be // .KRD : http://nic.krd/data/krd/Registration%20Policy.pdf co.krd edu.krd // Krellian Ltd. : https://krellian.com // Submitted by Ben Francis krellian.net webthings.io // LCube - Professional hosting e.K. : https://www.lcube-webhosting.de // Submitted by Lars Laehn git-repos.de lcube-server.de svn-repos.de // Leadpages : https://www.leadpages.net // Submitted by Greg Dallavalle leadpages.co lpages.co lpusercontent.com // Lelux.fi : https://lelux.fi/ // Submitted by Lelux Admin lelux.site // Lifetime Hosting : https://Lifetime.Hosting/ // Submitted by Mike Fillator co.business co.education co.events co.financial co.network co.place co.technology // Lightmaker Property Manager, Inc. : https://app.lmpm.com/ // Submitted by Greg Holland app.lmpm.com // linkyard ldt: https://www.linkyard.ch/ // Submitted by Mario Siegenthaler linkyard.cloud linkyard-cloud.ch // Linode : https://linode.com // Submitted by members.linode.com *.nodebalancer.linode.com *.linodeobjects.com ip.linodeusercontent.com // LiquidNet Ltd : http://www.liquidnetlimited.com/ // Submitted by Victor Velchev we.bs // Localcert : https://localcert.dev // Submitted by Lann Martin *.user.localcert.dev // localzone.xyz // Submitted by Kenny Niehage localzone.xyz // Log'in Line : https://www.loginline.com/ // Submitted by Rémi Mach loginline.app loginline.dev loginline.io loginline.services loginline.site // Lokalized : https://lokalized.nl // Submitted by Noah Taheij servers.run // Lõhmus Family, The // Submitted by Heiki Lõhmus lohmus.me // LubMAN UMCS Sp. z o.o : https://lubman.pl/ // Submitted by Ireneusz Maliszewski krasnik.pl leczna.pl lubartow.pl lublin.pl poniatowa.pl swidnik.pl // Lug.org.uk : https://lug.org.uk // Submitted by Jon Spriggs glug.org.uk lug.org.uk lugs.org.uk // Lukanet Ltd : https://lukanet.com // Submitted by Anton Avramov barsy.bg barsy.co.uk barsyonline.co.uk barsycenter.com barsyonline.com barsy.club barsy.de barsy.eu barsy.in barsy.info barsy.io barsy.me barsy.menu barsy.mobi barsy.net barsy.online barsy.org barsy.pro barsy.pub barsy.ro barsy.shop barsy.site barsy.support barsy.uk // Magento Commerce // Submitted by Damien Tournoud *.magentosite.cloud // May First - People Link : https://mayfirst.org/ // Submitted by Jamie McClelland mayfirst.info mayfirst.org // Mail.Ru Group : https://hb.cldmail.ru // Submitted by Ilya Zaretskiy hb.cldmail.ru // Mail Transfer Platform : https://www.neupeer.com // Submitted by Li Hui cn.vu // Maze Play: https://www.mazeplay.com // Submitted by Adam Humpherys mazeplay.com // mcpe.me : https://mcpe.me // Submitted by Noa Heyl mcpe.me // McHost : https://mchost.ru // Submitted by Evgeniy Subbotin mcdir.me mcdir.ru mcpre.ru vps.mcdir.ru // Mediatech : https://mediatech.by // Submitted by Evgeniy Kozhuhovskiy mediatech.by mediatech.dev // Medicom Health : https://medicomhealth.com // Submitted by Michael Olson hra.health // Memset hosting : https://www.memset.com // Submitted by Tom Whitwell miniserver.com memset.net // Messerli Informatik AG : https://www.messerli.ch/ // Submitted by Ruben Schmidmeister messerli.app // Meta Platforms, Inc. : https://meta.com/ // Submitted by Jacob Cordero atmeta.com apps.fbsbx.com // MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/ // Submitted by Zdeněk Šustr *.cloud.metacentrum.cz custom.metacentrum.cz // MetaCentrum, CESNET z.s.p.o. : https://www.metacentrum.cz/en/ // Submitted by Radim Janča flt.cloud.muni.cz usr.cloud.muni.cz // Meteor Development Group : https://www.meteor.com/hosting // Submitted by Pierre Carrier meteorapp.com eu.meteorapp.com // Michau Enterprises Limited : http://www.co.pl/ co.pl // Microsoft Corporation : http://microsoft.com // Submitted by Public Suffix List Admin // Managed by Corporate Domains // Microsoft Azure : https://home.azure *.azurecontainer.io *.cloudapp.azure.com azure-api.net azureedge.net azurefd.net azurewebsites.net azure-mobile.net azurestaticapps.net 1.azurestaticapps.net 2.azurestaticapps.net 3.azurestaticapps.net 4.azurestaticapps.net 5.azurestaticapps.net 6.azurestaticapps.net 7.azurestaticapps.net centralus.azurestaticapps.net eastasia.azurestaticapps.net eastus2.azurestaticapps.net westeurope.azurestaticapps.net westus2.azurestaticapps.net cloudapp.net trafficmanager.net blob.core.windows.net servicebus.windows.net // minion.systems : http://minion.systems // Submitted by Robert Böttinger csx.cc // Mintere : https://mintere.com/ // Submitted by Ben Aubin mintere.site // MobileEducation, LLC : https://joinforte.com // Submitted by Grayson Martin forte.id // MODX Systems LLC : https://modx.com // Submitted by Elizabeth Southwell modx.dev // Mozilla Corporation : https://mozilla.com // Submitted by Ben Francis mozilla-iot.org // Mozilla Foundation : https://mozilla.org/ // Submitted by glob bmoattachments.org // MSK-IX : https://www.msk-ix.ru/ // Submitted by Khannanov Roman net.ru org.ru pp.ru // Mythic Beasts : https://www.mythic-beasts.com // Submitted by Paul Cammish hostedpi.com customer.mythic-beasts.com caracal.mythic-beasts.com fentiger.mythic-beasts.com lynx.mythic-beasts.com ocelot.mythic-beasts.com oncilla.mythic-beasts.com onza.mythic-beasts.com sphinx.mythic-beasts.com vs.mythic-beasts.com x.mythic-beasts.com yali.mythic-beasts.com cust.retrosnub.co.uk // Nabu Casa : https://www.nabucasa.com // Submitted by Paulus Schoutsen ui.nabu.casa // Net at Work Gmbh : https://www.netatwork.de // Submitted by Jan Jaeschke cloud.nospamproxy.com // Netlify : https://www.netlify.com // Submitted by Jessica Parsons netlify.app // Neustar Inc. // Submitted by Trung Tran 4u.com // ngrok : https://ngrok.com/ // Submitted by Alan Shreve ngrok.app ngrok-free.app ngrok.dev ngrok-free.dev ngrok.io ap.ngrok.io au.ngrok.io eu.ngrok.io in.ngrok.io jp.ngrok.io sa.ngrok.io us.ngrok.io ngrok.pizza ngrok.pro // Nicolaus Copernicus University in Torun - MSK TORMAN (https://www.man.torun.pl) torun.pl // Nimbus Hosting Ltd. : https://www.nimbushosting.co.uk/ // Submitted by Nicholas Ford nh-serv.co.uk // NFSN, Inc. : https://www.NearlyFreeSpeech.NET/ // Submitted by Jeff Wheelhouse nfshost.com // Noop : https://noop.app // Submitted by Nathaniel Schweinberg *.developer.app noop.app // Northflank Ltd. : https://northflank.com/ // Submitted by Marco Suter *.northflank.app *.build.run *.code.run *.database.run *.migration.run // Noticeable : https://noticeable.io // Submitted by Laurent Pellegrino noticeable.news // Now-DNS : https://now-dns.com // Submitted by Steve Russell dnsking.ch mypi.co n4t.co 001www.com ddnslive.com myiphost.com forumz.info 16-b.it 32-b.it 64-b.it soundcast.me tcp4.me dnsup.net hicam.net now-dns.net ownip.net vpndns.net dynserv.org now-dns.org x443.pw now-dns.top ntdll.top freeddns.us crafting.xyz zapto.xyz // nsupdate.info : https://www.nsupdate.info/ // Submitted by Thomas Waldmann nsupdate.info nerdpol.ovh // No-IP.com : https://noip.com/ // Submitted by Deven Reza blogsyte.com brasilia.me cable-modem.org ciscofreak.com collegefan.org couchpotatofries.org damnserver.com ddns.me ditchyourip.com dnsfor.me dnsiskinky.com dvrcam.info dynns.com eating-organic.net fantasyleague.cc geekgalaxy.com golffan.us health-carereform.com homesecuritymac.com homesecuritypc.com hopto.me ilovecollege.info loginto.me mlbfan.org mmafan.biz myactivedirectory.com mydissent.net myeffect.net mymediapc.net mypsx.net mysecuritycamera.com mysecuritycamera.net mysecuritycamera.org net-freaks.com nflfan.org nhlfan.net no-ip.ca no-ip.co.uk no-ip.net noip.us onthewifi.com pgafan.net point2this.com pointto.us privatizehealthinsurance.net quicksytes.com read-books.org securitytactics.com serveexchange.com servehumour.com servep2p.com servesarcasm.com stufftoread.com ufcfan.org unusualperson.com workisboring.com 3utilities.com bounceme.net ddns.net ddnsking.com gotdns.ch hopto.org myftp.biz myftp.org myvnc.com no-ip.biz no-ip.info no-ip.org noip.me redirectme.net servebeer.com serveblog.net servecounterstrike.com serveftp.com servegame.com servehalflife.com servehttp.com serveirc.com serveminecraft.net servemp3.com servepics.com servequake.com sytes.net webhop.me zapto.org // NodeArt : https://nodeart.io // Submitted by Konstantin Nosov stage.nodeart.io // Nucleos Inc. : https://nucleos.com // Submitted by Piotr Zduniak pcloud.host // NYC.mn : http://www.information.nyc.mn // Submitted by Matthew Brown nyc.mn // Observable, Inc. : https://observablehq.com // Submitted by Mike Bostock static.observableusercontent.com // Octopodal Solutions, LLC. : https://ulterius.io/ // Submitted by Andrew Sampson cya.gg // OMG.LOL : // Submitted by Adam Newbold omg.lol // Omnibond Systems, LLC. : https://www.omnibond.com // Submitted by Cole Estep cloudycluster.net // OmniWe Limited: https://omniwe.com // Submitted by Vicary Archangel omniwe.site // One.com: https://www.one.com/ // Submitted by Jacob Bunk Nielsen 123hjemmeside.dk 123hjemmeside.no 123homepage.it 123kotisivu.fi 123minsida.se 123miweb.es 123paginaweb.pt 123sait.ru 123siteweb.fr 123webseite.at 123webseite.de 123website.be 123website.ch 123website.lu 123website.nl service.one simplesite.com simplesite.com.br simplesite.gr simplesite.pl // One Fold Media : http://www.onefoldmedia.com/ // Submitted by Eddie Jones nid.io // Open Social : https://www.getopensocial.com/ // Submitted by Alexander Varwijk opensocial.site // OpenCraft GmbH : http://opencraft.com/ // Submitted by Sven Marnach opencraft.hosting // OpenResearch GmbH: https://openresearch.com/ // Submitted by Philipp Schmid orsites.com // Opera Software, A.S.A. // Submitted by Yngve Pettersen operaunite.com // Orange : https://www.orange.com // Submitted by Alexandre Linte tech.orange // Oursky Limited : https://authgear.com/, https://skygear.io/ // Submitted by Authgear Team , Skygear Developer authgear-staging.com authgearapps.com skygearapp.com // OutSystems // Submitted by Duarte Santos outsystemscloud.com // OVHcloud: https://ovhcloud.com // Submitted by Vincent Cassé *.webpaas.ovh.net *.hosting.ovh.net // OwnProvider GmbH: http://www.ownprovider.com // Submitted by Jan Moennich ownprovider.com own.pm // OwO : https://whats-th.is/ // Submitted by Dean Sheather *.owo.codes // OX : http://www.ox.rs // Submitted by Adam Grand ox.rs // oy.lc // Submitted by Charly Coste oy.lc // Pagefog : https://pagefog.com/ // Submitted by Derek Myers pgfog.com // Pagefront : https://www.pagefronthq.com/ // Submitted by Jason Kriss pagefrontapp.com // PageXL : https://pagexl.com // Submitted by Yann Guichard pagexl.com // Paywhirl, Inc : https://paywhirl.com/ // Submitted by Daniel Netzer *.paywhirl.com // pcarrier.ca Software Inc: https://pcarrier.ca/ // Submitted by Pierre Carrier bar0.net bar1.net bar2.net rdv.to // .pl domains (grandfathered) art.pl gliwice.pl krakow.pl poznan.pl wroc.pl zakopane.pl // Pantheon Systems, Inc. : https://pantheon.io/ // Submitted by Gary Dylina pantheonsite.io gotpantheon.com // Peplink | Pepwave : http://peplink.com/ // Submitted by Steve Leung mypep.link // Perspecta : https://perspecta.com/ // Submitted by Kenneth Van Alstyne perspecta.cloud // PE Ulyanov Kirill Sergeevich : https://airy.host // Submitted by Kirill Ulyanov lk3.ru // Planet-Work : https://www.planet-work.com/ // Submitted by Frédéric VANNIÈRE on-web.fr // Platform.sh : https://platform.sh // Submitted by Nikola Kotur *.upsun.app upsunapp.com ent.platform.sh eu.platform.sh us.platform.sh *.platformsh.site *.tst.site // Platter: https://platter.dev // Submitted by Patrick Flor platter-app.com platter-app.dev platterp.us // Plesk : https://www.plesk.com/ // Submitted by Anton Akhtyamov pdns.page plesk.page pleskns.com // Pley AB : https://www.pley.com/ // Submitted by Henning Pohl pley.games // Port53 : https://port53.io/ // Submitted by Maximilian Schieder dyn53.io // Porter : https://porter.run/ // Submitted by Rudraksh MK onporter.run // Positive Codes Technology Company : http://co.bn/faq.html // Submitted by Zulfais co.bn // Postman, Inc : https://postman.com // Submitted by Rahul Dhawan postman-echo.com pstmn.io mock.pstmn.io httpbin.org //prequalifyme.today : https://prequalifyme.today //Submitted by DeepakTiwari deepak@ivylead.io prequalifyme.today // prgmr.com : https://prgmr.com/ // Submitted by Sarah Newman xen.prgmr.com // priv.at : http://www.nic.priv.at/ // Submitted by registry priv.at // privacytools.io : https://www.privacytools.io/ // Submitted by Jonah Aragon prvcy.page // Protocol Labs : https://protocol.ai/ // Submitted by Michael Burns *.dweb.link // Protonet GmbH : http://protonet.io // Submitted by Martin Meier protonet.io // Publication Presse Communication SARL : https://ppcom.fr // Submitted by Yaacov Akiba Slama chirurgiens-dentistes-en-france.fr byen.site // pubtls.org: https://www.pubtls.org // Submitted by Kor Nielsen pubtls.org // PythonAnywhere LLP: https://www.pythonanywhere.com // Submitted by Giles Thomas pythonanywhere.com eu.pythonanywhere.com // QOTO, Org. // Submitted by Jeffrey Phillips Freeman qoto.io // Qualifio : https://qualifio.com/ // Submitted by Xavier De Cock qualifioapp.com // Quality Unit: https://qualityunit.com // Submitted by Vasyl Tsalko ladesk.com // QuickBackend: https://www.quickbackend.com // Submitted by Dani Biro qbuser.com // Rad Web Hosting: https://radwebhosting.com // Submitted by Scott Claeys cloudsite.builders myradweb.net servername.us // Redgate Software: https://red-gate.com // Submitted by Andrew Farries instances.spawn.cc // Redstar Consultants : https://www.redstarconsultants.com/ // Submitted by Jons Slemmer instantcloud.cn // Russian Academy of Sciences // Submitted by Tech Support ras.ru // QA2 // Submitted by Daniel Dent (https://www.danieldent.com/) qa2.com // QCX // Submitted by Cassandra Beelen qcx.io *.sys.qcx.io // QNAP System Inc : https://www.qnap.com // Submitted by Nick Chang dev-myqnapcloud.com alpha-myqnapcloud.com myqnapcloud.com // Quip : https://quip.com // Submitted by Patrick Linehan *.quipelements.com // Qutheory LLC : http://qutheory.io // Submitted by Jonas Schwartz vapor.cloud vaporcloud.io // Rackmaze LLC : https://www.rackmaze.com // Submitted by Kirill Pertsev rackmaze.com rackmaze.net // Rakuten Games, Inc : https://dev.viberplay.io // Submitted by Joshua Zhang g.vbrplsbx.io // Rancher Labs, Inc : https://rancher.com // Submitted by Vincent Fiduccia *.on-k3s.io *.on-rancher.cloud *.on-rio.io // Read The Docs, Inc : https://www.readthedocs.org // Submitted by David Fischer readthedocs.io // Red Hat, Inc. OpenShift : https://openshift.redhat.com/ // Submitted by Tim Kramer rhcloud.com // Render : https://render.com // Submitted by Anurag Goel app.render.com onrender.com // Repl.it : https://repl.it // Submitted by Lincoln Bergeson replit.app id.replit.app firewalledreplit.co id.firewalledreplit.co repl.co id.repl.co replit.dev archer.replit.dev bones.replit.dev canary.replit.dev global.replit.dev hacker.replit.dev id.replit.dev janeway.replit.dev kim.replit.dev kira.replit.dev kirk.replit.dev odo.replit.dev paris.replit.dev picard.replit.dev pike.replit.dev prerelease.replit.dev reed.replit.dev riker.replit.dev sisko.replit.dev spock.replit.dev staging.replit.dev sulu.replit.dev tarpit.replit.dev teams.replit.dev tucker.replit.dev wesley.replit.dev worf.replit.dev repl.run // Resin.io : https://resin.io // Submitted by Tim Perry resindevice.io devices.resinstaging.io // RethinkDB : https://www.rethinkdb.com/ // Submitted by Chris Kastorff hzc.io // Revitalised Limited : http://www.revitalised.co.uk // Submitted by Jack Price wellbeingzone.eu wellbeingzone.co.uk // Rico Developments Limited : https://adimo.co // Submitted by Colin Brown adimo.co.uk // Riseup Networks : https://riseup.net // Submitted by Micah Anderson itcouldbewor.se // Rochester Institute of Technology : http://www.rit.edu/ // Submitted by Jennifer Herting git-pages.rit.edu // Rocky Enterprise Software Foundation : https://resf.org // Submitted by Neil Hanlon rocky.page // Rusnames Limited: http://rusnames.ru/ // Submitted by Sergey Zotov биз.рус ком.рус крым.рус мир.рус мск.рус орг.рус самара.рус сочи.рус спб.рус я.рус // SAKURA Internet Inc. : https://www.sakura.ad.jp/ // Submitted by Internet Service Department 180r.com dojin.com sakuratan.com sakuraweb.com x0.com 2-d.jp bona.jp crap.jp daynight.jp eek.jp flop.jp halfmoon.jp jeez.jp matrix.jp mimoza.jp ivory.ne.jp mail-box.ne.jp mints.ne.jp mokuren.ne.jp opal.ne.jp sakura.ne.jp sumomo.ne.jp topaz.ne.jp netgamers.jp nyanta.jp o0o0.jp rdy.jp rgr.jp rulez.jp s3.isk01.sakurastorage.jp s3.isk02.sakurastorage.jp saloon.jp sblo.jp skr.jp tank.jp uh-oh.jp undo.jp rs.webaccel.jp user.webaccel.jp websozai.jp xii.jp squares.net jpn.org kirara.st x0.to from.tv sakura.tv // Salesforce.com, Inc. https://salesforce.com/ // Submitted by Michael Biven and Aaron Romeo *.builder.code.com *.dev-builder.code.com *.stg-builder.code.com *.001.test.code-builder-stg.platform.salesforce.com // Sandstorm Development Group, Inc. : https://sandcats.io/ // Submitted by Asheesh Laroia sandcats.io // SBE network solutions GmbH : https://www.sbe.de/ // Submitted by Norman Meilick logoip.de logoip.com // Scaleway : https://www.scaleway.com/ // Submitted by Rémy Léone fr-par-1.baremetal.scw.cloud fr-par-2.baremetal.scw.cloud nl-ams-1.baremetal.scw.cloud cockpit.fr-par.scw.cloud fnc.fr-par.scw.cloud functions.fnc.fr-par.scw.cloud k8s.fr-par.scw.cloud nodes.k8s.fr-par.scw.cloud s3.fr-par.scw.cloud s3-website.fr-par.scw.cloud whm.fr-par.scw.cloud priv.instances.scw.cloud pub.instances.scw.cloud k8s.scw.cloud cockpit.nl-ams.scw.cloud k8s.nl-ams.scw.cloud nodes.k8s.nl-ams.scw.cloud s3.nl-ams.scw.cloud s3-website.nl-ams.scw.cloud whm.nl-ams.scw.cloud cockpit.pl-waw.scw.cloud k8s.pl-waw.scw.cloud nodes.k8s.pl-waw.scw.cloud s3.pl-waw.scw.cloud s3-website.pl-waw.scw.cloud scalebook.scw.cloud smartlabeling.scw.cloud dedibox.fr // schokokeks.org GbR : https://schokokeks.org/ // Submitted by Hanno Böck schokokeks.net // Scottish Government: https://www.gov.scot // Submitted by Martin Ellis gov.scot service.gov.scot // Scry Security : http://www.scrysec.com // Submitted by Shante Adam scrysec.com // Securepoint GmbH : https://www.securepoint.de // Submitted by Erik Anders firewall-gateway.com firewall-gateway.de my-gateway.de my-router.de spdns.de spdns.eu firewall-gateway.net my-firewall.org myfirewall.org spdns.org // Seidat : https://www.seidat.com // Submitted by Artem Kondratev seidat.net // Sellfy : https://sellfy.com // Submitted by Yuriy Romadin sellfy.store // Senseering GmbH : https://www.senseering.de // Submitted by Felix Mönckemeyer senseering.net // Sendmsg: https://www.sendmsg.co.il // Submitted by Assaf Stern minisite.ms // Service Magnet : https://myservicemagnet.com // Submitted by Dave Sanders magnet.page // Service Online LLC : http://drs.ua/ // Submitted by Serhii Bulakh biz.ua co.ua pp.ua // Shift Crypto AG : https://shiftcrypto.ch // Submitted by alex shiftcrypto.dev shiftcrypto.io // ShiftEdit : https://shiftedit.net/ // Submitted by Adam Jimenez shiftedit.io // Shopblocks : http://www.shopblocks.com/ // Submitted by Alex Bowers myshopblocks.com // Shopify : https://www.shopify.com // Submitted by Alex Richter myshopify.com // Shopit : https://www.shopitcommerce.com/ // Submitted by Craig McMahon shopitsite.com // shopware AG : https://shopware.com // Submitted by Jens Küper shopware.store // Siemens Mobility GmbH // Submitted by Oliver Graebner mo-siemens.io // SinaAppEngine : http://sae.sina.com.cn/ // Submitted by SinaAppEngine 1kapp.com appchizi.com applinzi.com sinaapp.com vipsinaapp.com // Siteleaf : https://www.siteleaf.com/ // Submitted by Skylar Challand siteleaf.net // Skyhat : http://www.skyhat.io // Submitted by Shante Adam bounty-full.com alpha.bounty-full.com beta.bounty-full.com // Smallregistry by Promopixel SARL: https://www.smallregistry.net // Former AFNIC's SLDs // Submitted by Jérôme Lipowicz aeroport.fr avocat.fr chambagri.fr chirurgiens-dentistes.fr experts-comptables.fr medecin.fr notaires.fr pharmacien.fr port.fr veterinaire.fr // Small Technology Foundation : https://small-tech.org // Submitted by Aral Balkan small-web.org // Smoove.io : https://www.smoove.io/ // Submitted by Dan Kozak vp4.me // Snowflake Inc : https://www.snowflake.com/ // Submitted by Faith Olapade snowflake.app privatelink.snowflake.app streamlit.app streamlitapp.com // Snowplow Analytics : https://snowplowanalytics.com/ // Submitted by Ian Streeter try-snowplow.com // SourceHut : https://sourcehut.org // Submitted by Drew DeVault srht.site // Stackhero : https://www.stackhero.io // Submitted by Adrien Gillon stackhero-network.com // STACKIT : https://www.stackit.de/en/ // Submitted by STACKIT-DNS Team (Simon Stier) runs.onstackit.cloud stackit.gg stackit.rocks stackit.run stackit.zone // Staclar : https://staclar.com // Submitted by Q Misell musician.io // Submitted by Matthias Merkel novecore.site // staticland : https://static.land // Submitted by Seth Vincent static.land dev.static.land sites.static.land // Storebase : https://www.storebase.io // Submitted by Tony Schirmer storebase.store // Strategic System Consulting (eApps Hosting): https://www.eapps.com/ // Submitted by Alex Oancea vps-host.net atl.jelastic.vps-host.net njs.jelastic.vps-host.net ric.jelastic.vps-host.net // Sony Interactive Entertainment LLC : https://sie.com/ // Submitted by David Coles playstation-cloud.com // SourceLair PC : https://www.sourcelair.com // Submitted by Antonis Kalipetis apps.lair.io *.stolos.io // SpaceKit : https://www.spacekit.io/ // Submitted by Reza Akhavan spacekit.io // SpeedPartner GmbH: https://www.speedpartner.de/ // Submitted by Stefan Neufeind customer.speedpartner.de // Spreadshop (sprd.net AG) : https://www.spreadshop.com/ // Submitted by Martin Breest myspreadshop.at myspreadshop.com.au myspreadshop.be myspreadshop.ca myspreadshop.ch myspreadshop.com myspreadshop.de myspreadshop.dk myspreadshop.es myspreadshop.fi myspreadshop.fr myspreadshop.ie myspreadshop.it myspreadshop.net myspreadshop.nl myspreadshop.no myspreadshop.pl myspreadshop.se myspreadshop.co.uk // Standard Library : https://stdlib.com // Submitted by Jacob Lee api.stdlib.com // stereosense GmbH : https://www.involve.me // Submitted by Florian Burmann feedback.ac forms.ac assessments.cx calculators.cx funnels.cx paynow.cx quizzes.cx researched.cx tests.cx surveys.so // Storipress : https://storipress.com // Submitted by Benno Liu storipress.app // Storj Labs Inc. : https://storj.io/ // Submitted by Philip Hutchins storj.farm // Streak : https://streak.com // Submitted by Blake Kadatz streak-link.com streaklinks.com streakusercontent.com // Studenten Net Twente : http://www.snt.utwente.nl/ // Submitted by Silke Hofstra utwente.io // Student-Run Computing Facility : https://www.srcf.net/ // Submitted by Edwin Balani soc.srcf.net user.srcf.net // Sub 6 Limited: http://www.sub6.com // Submitted by Dan Miller temp-dns.com // Supabase : https://supabase.io // Submitted by Inian Parameshwaran supabase.co supabase.in supabase.net su.paba.se // Symfony, SAS : https://symfony.com/ // Submitted by Fabien Potencier *.s5y.io *.sensiosite.cloud // Syncloud : https://syncloud.org // Submitted by Boris Rybalkin syncloud.it // Synology, Inc. : https://www.synology.com/ // Submitted by Rony Weng dscloud.biz direct.quickconnect.cn dsmynas.com familyds.com diskstation.me dscloud.me i234.me myds.me synology.me dscloud.mobi dsmynas.net familyds.net dsmynas.org familyds.org vpnplus.to direct.quickconnect.to // Tabit Technologies Ltd. : https://tabit.cloud/ // Submitted by Oren Agiv tabitorder.co.il mytabit.co.il mytabit.com // TAIFUN Software AG : http://taifun-software.de // Submitted by Bjoern Henke taifun-dns.de // Tailscale Inc. : https://www.tailscale.com // Submitted by David Anderson beta.tailscale.net ts.net *.c.ts.net // TASK geographical domains (www.task.gda.pl/uslugi/dns) gda.pl gdansk.pl gdynia.pl med.pl sopot.pl // team.blue https://team.blue // Submitted by Cedric Dubois site.tb-hosting.com // Teckids e.V. : https://www.teckids.org // Submitted by Dominik George edugit.io s3.teckids.org // Telebit : https://telebit.cloud // Submitted by AJ ONeal telebit.app telebit.io *.telebit.xyz // Thingdust AG : https://thingdust.com/ // Submitted by Adrian Imboden *.firenet.ch *.svc.firenet.ch reservd.com thingdustdata.com cust.dev.thingdust.io cust.disrec.thingdust.io cust.prod.thingdust.io cust.testing.thingdust.io reservd.dev.thingdust.io reservd.disrec.thingdust.io reservd.testing.thingdust.io // ticket i/O GmbH : https://ticket.io // Submitted by Christian Franke tickets.io // Tlon.io : https://tlon.io // Submitted by Mark Staarink arvo.network azimuth.network tlon.network // Tor Project, Inc. : https://torproject.org // Submitted by Antoine Beaupré bloxcms.com townnews-staging.com // TrafficPlex GmbH : https://www.trafficplex.de/ // Submitted by Phillipp Röll 12hp.at 2ix.at 4lima.at lima-city.at 12hp.ch 2ix.ch 4lima.ch lima-city.ch trafficplex.cloud de.cool 12hp.de 2ix.de 4lima.de lima-city.de 1337.pictures clan.rip lima-city.rocks webspace.rocks lima.zone // TransIP : https://www.transip.nl // Submitted by Rory Breuk *.transurl.be *.transurl.eu *.transurl.nl // TransIP: https://www.transip.nl // Submitted by Cedric Dubois site.transip.me // TuxFamily : http://tuxfamily.org // Submitted by TuxFamily administrators tuxfamily.org // TwoDNS : https://www.twodns.de/ // Submitted by TwoDNS-Support dd-dns.de diskstation.eu diskstation.org dray-dns.de draydns.de dyn-vpn.de dynvpn.de mein-vigor.de my-vigor.de my-wan.de syno-ds.de synology-diskstation.de synology-ds.de // Typedream : https://typedream.com // Submitted by Putri Karunia typedream.app // Typeform : https://www.typeform.com // Submitted by Sergi Ferriz pro.typeform.com // Uberspace : https://uberspace.de // Submitted by Moritz Werner uber.space *.uberspace.de // UDR Limited : http://www.udr.hk.com // Submitted by registry hk.com hk.org ltd.hk inc.hk // UK Intis Telecom LTD : https://it.com // Submitted by ITComdomains it.com // UNIVERSAL DOMAIN REGISTRY : https://www.udr.org.yt/ // see also: whois -h whois.udr.org.yt help // Submitted by Atanunu Igbunuroghene name.pm sch.tf biz.wf sch.wf org.yt // United Gameserver GmbH : https://united-gameserver.de // Submitted by Stefan Schwarz virtualuser.de virtual-user.de // Upli : https://upli.io // Submitted by Lenny Bakkalian upli.io // urown.net : https://urown.net // Submitted by Hostmaster urown.cloud dnsupdate.info // .US // Submitted by Ed Moore lib.de.us // VeryPositive SIA : http://very.lv // Submitted by Danko Aleksejevs 2038.io // Vercel, Inc : https://vercel.com/ // Submitted by Connor Davis vercel.app vercel.dev now.sh // Viprinet Europe GmbH : http://www.viprinet.com // Submitted by Simon Kissel router.management // Virtual-Info : https://www.virtual-info.info/ // Submitted by Adnan RIHAN v-info.info // Voorloper.com: https://voorloper.com // Submitted by Nathan van Bakel voorloper.cloud // Voxel.sh DNS : https://voxel.sh/dns/ // Submitted by Mia Rehlinger neko.am nyaa.am be.ax cat.ax es.ax eu.ax gg.ax mc.ax us.ax xy.ax nl.ci xx.gl app.gp blog.gt de.gt to.gt be.gy cc.hn io.kg jp.kg tv.kg uk.kg us.kg de.ls at.md de.md jp.md to.md indie.porn vxl.sh ch.tc me.tc we.tc nyan.to at.vg blog.vu dev.vu me.vu // V.UA Domain Administrator : https://domain.v.ua/ // Submitted by Serhii Rostilo v.ua // Vultr Objects : https://www.vultr.com/products/object-storage/ // Submitted by Niels Maumenee *.vultrobjects.com // Waffle Computer Inc., Ltd. : https://docs.waffleinfo.com // Submitted by Masayuki Note wafflecell.com // Webflow, Inc. : https://www.webflow.com // Submitted by Webflow Security Team webflow.io webflowtest.io // WebHare bv: https://www.webhare.com/ // Submitted by Arnold Hendriks *.webhare.dev // WebHotelier Technologies Ltd: https://www.webhotelier.net/ // Submitted by Apostolos Tsakpinis reserve-online.net reserve-online.com bookonline.app hotelwithflight.com // WeDeploy by Liferay, Inc. : https://www.wedeploy.com // Submitted by Henrique Vicente wedeploy.io wedeploy.me wedeploy.sh // Western Digital Technologies, Inc : https://www.wdc.com // Submitted by Jung Jin remotewd.com // WIARD Enterprises : https://wiardweb.com // Submitted by Kidd Hustle pages.wiardweb.com // Wikimedia Labs : https://wikitech.wikimedia.org // Submitted by Arturo Borrero Gonzalez wmflabs.org toolforge.org wmcloud.org // WISP : https://wisp.gg // Submitted by Stepan Fedotov panel.gg daemon.panel.gg // Wizard Zines : https://wizardzines.com // Submitted by Julia Evans messwithdns.com // WoltLab GmbH : https://www.woltlab.com // Submitted by Tim Düsterhus woltlab-demo.com myforum.community community-pro.de diskussionsbereich.de community-pro.net meinforum.net // Woods Valldata : https://www.woodsvalldata.co.uk/ // Submitted by Chris Whittle affinitylottery.org.uk raffleentry.org.uk weeklylottery.org.uk // WP Engine : https://wpengine.com/ // Submitted by Michael Smith // Submitted by Brandon DuRette wpenginepowered.com js.wpenginepowered.com // Wix.com, Inc. : https://www.wix.com // Submitted by Shahar Talmi wixsite.com editorx.io wixstudio.io wix.run // XenonCloud GbR: https://xenoncloud.net // Submitted by Julian Uphoff half.host // XnBay Technology : http://www.xnbay.com/ // Submitted by XnBay Developer xnbay.com u2.xnbay.com u2-local.xnbay.com // XS4ALL Internet bv : https://www.xs4all.nl/ // Submitted by Daniel Mostertman cistron.nl demon.nl xs4all.space // Yandex.Cloud LLC: https://cloud.yandex.com // Submitted by Alexander Lodin yandexcloud.net storage.yandexcloud.net website.yandexcloud.net // YesCourse Pty Ltd : https://yescourse.com // Submitted by Atul Bhouraskar official.academy // Yola : https://www.yola.com/ // Submitted by Stefano Rivera yolasite.com // Yombo : https://yombo.net // Submitted by Mitch Schwenk ybo.faith yombo.me homelink.one ybo.party ybo.review ybo.science ybo.trade // Yunohost : https://yunohost.org // Submitted by Valentin Grimaud ynh.fr nohost.me noho.st // ZaNiC : http://www.za.net/ // Submitted by registry za.net za.org // ZAP-Hosting GmbH & Co. KG : https://zap-hosting.com // Submitted by Julian Alker zap.cloud // Zine EOOD : https://zine.bg/ // Submitted by Martin Angelov bss.design // Zitcom A/S : https://www.zitcom.dk // Submitted by Emil Stahl basicserver.io virtualserver.io enterprisecloud.nu // ===END PRIVATE DOMAINS=== Mail-DMARC-1.20240314/share/rua-schema.xsd000444000765000024 2301114574361234 17103 0ustar00mattstaff000000000000 Mail-DMARC-1.20240314/share/html000755000765000024 014574361234 15130 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/share/html/index.html000444000765000024 1145514574361234 17310 0ustar00mattstaff000000000000 Mail::DMARC::HTTP
Mail-DMARC-1.20240314/share/html/css000755000765000024 014574361234 15720 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/share/html/css/ellipsis-xbl.xml000555000765000024 75514574361234 21200 0ustar00mattstaff000000000000 Mail-DMARC-1.20240314/share/html/css/ui.jqgrid.css000555000765000024 3016714574361234 20515 0ustar00mattstaff000000000000/*Grid*/ .ui-jqgrid {position: relative;} .ui-jqgrid .ui-jqgrid-view {position: relative;left:0; top: 0; padding: 0; font-size:11px;} /* caption*/ .ui-jqgrid .ui-jqgrid-titlebar {padding: .3em .2em .2em .3em; position: relative; border-left: 0 none;border-right: 0 none; border-top: 0 none;} .ui-jqgrid .ui-jqgrid-title { float: left; margin: .1em 0 .2em; } .ui-jqgrid .ui-jqgrid-titlebar-close { position: absolute;top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height:18px;}.ui-jqgrid .ui-jqgrid-titlebar-close span { display: block; margin: 1px; } .ui-jqgrid .ui-jqgrid-titlebar-close:hover { padding: 0; } /* header*/ .ui-jqgrid .ui-jqgrid-hdiv {position: relative; margin: 0;padding: 0; overflow-x: hidden; border-left: 0 none !important; border-top : 0 none !important; border-right : 0 none !important;} .ui-jqgrid .ui-jqgrid-hbox {float: left; padding-right: 20px;} .ui-jqgrid .ui-jqgrid-htable {table-layout:fixed;margin:0;} .ui-jqgrid .ui-jqgrid-htable th {height:22px;padding: 0 2px 0 2px;} .ui-jqgrid .ui-jqgrid-htable th div {overflow: hidden; position:relative; height:17px;} .ui-th-column, .ui-jqgrid .ui-jqgrid-htable th.ui-th-column {overflow: hidden;white-space: nowrap;text-align:center;border-top : 0 none;border-bottom : 0 none;} .ui-th-ltr, .ui-jqgrid .ui-jqgrid-htable th.ui-th-ltr {border-left : 0 none;} .ui-th-rtl, .ui-jqgrid .ui-jqgrid-htable th.ui-th-rtl {border-right : 0 none;} .ui-first-th-ltr {border-right: 1px solid; } .ui-first-th-rtl {border-left: 1px solid; } .ui-jqgrid .ui-th-div-ie {white-space: nowrap; zoom :1; height:17px;} .ui-jqgrid .ui-jqgrid-resize {height:20px !important;position: relative; cursor :e-resize;display: inline;overflow: hidden;} .ui-jqgrid .ui-grid-ico-sort {overflow:hidden;position:absolute;display:inline; cursor: pointer !important;} .ui-jqgrid .ui-icon-asc {margin-top:-3px; height:12px;} .ui-jqgrid .ui-icon-desc {margin-top:3px;height:12px;} .ui-jqgrid .ui-i-asc {margin-top:0;height:16px;} .ui-jqgrid .ui-i-desc {margin-top:0;margin-left:13px;height:16px;} .ui-jqgrid .ui-jqgrid-sortable {cursor:pointer;} .ui-jqgrid tr.ui-search-toolbar th { border-top-width: 1px !important; border-top-color: inherit !important; border-top-style: ridge !important } tr.ui-search-toolbar input {margin: 1px 0 0 0} tr.ui-search-toolbar select {margin: 1px 0 0 0} /* body */ .ui-jqgrid .ui-jqgrid-bdiv {position: relative; margin: 0; padding:0; overflow: auto; text-align:left;} .ui-jqgrid .ui-jqgrid-btable {table-layout:fixed; margin:0; outline-style: none; } .ui-jqgrid tr.jqgrow { outline-style: none; } .ui-jqgrid tr.jqgroup { outline-style: none; } .ui-jqgrid tr.jqgrow td {font-weight: normal; overflow: hidden; white-space: pre; height: 22px;padding: 0 2px 0 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;} .ui-jqgrid tr.jqgfirstrow td {padding: 0 2px 0 2px;border-right-width: 1px; border-right-style: solid;} .ui-jqgrid tr.jqgroup td {font-weight: normal; overflow: hidden; white-space: pre; height: 22px;padding: 0 2px 0 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;} .ui-jqgrid tr.jqfoot td {font-weight: bold; overflow: hidden; white-space: pre; height: 22px;padding: 0 2px 0 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;} .ui-jqgrid tr.ui-row-ltr td {text-align:left;border-right-width: 1px; border-right-color: inherit; border-right-style: solid;} .ui-jqgrid tr.ui-row-rtl td {text-align:right;border-left-width: 1px; border-left-color: inherit; border-left-style: solid;} .ui-jqgrid td.jqgrid-rownum { padding: 0 2px 0 2px; margin: 0; border: 0 none;} .ui-jqgrid .ui-jqgrid-resize-mark { width:2px; left:0; background-color:#777; cursor: e-resize; cursor: col-resize; position:absolute; top:0; height:100px; overflow:hidden; display:none; border:0 none; z-index: 99999;} /* footer */ .ui-jqgrid .ui-jqgrid-sdiv {position: relative; margin: 0;padding: 0; overflow: hidden; border-left: 0 none !important; border-top : 0 none !important; border-right : 0 none !important;} .ui-jqgrid .ui-jqgrid-ftable {table-layout:fixed; margin-bottom:0;} .ui-jqgrid tr.footrow td {font-weight: bold; overflow: hidden; white-space:nowrap; height: 21px;padding: 0 2px 0 2px;border-top-width: 1px; border-top-color: inherit; border-top-style: solid;} .ui-jqgrid tr.footrow-ltr td {text-align:left;border-right-width: 1px; border-right-color: inherit; border-right-style: solid;} .ui-jqgrid tr.footrow-rtl td {text-align:right;border-left-width: 1px; border-left-color: inherit; border-left-style: solid;} /* Pager*/ .ui-jqgrid .ui-jqgrid-pager { border-left: 0 none !important;border-right: 0 none !important; border-bottom: 0 none !important; margin: 0 !important; padding: 0 !important; position: relative; height: 25px;white-space: nowrap;overflow: hidden;font-size:11px;} .ui-jqgrid .ui-pager-control {position: relative;} .ui-jqgrid .ui-pg-table {position: relative; padding-bottom:2px; width:auto; margin: 0;} .ui-jqgrid .ui-pg-table td {font-weight:normal; vertical-align:middle; padding:1px;} .ui-jqgrid .ui-pg-button { height:19px !important;} .ui-jqgrid .ui-pg-button span { display: block; margin: 1px; float:left;} .ui-jqgrid .ui-pg-button:hover { padding: 0; } .ui-jqgrid .ui-state-disabled:hover {padding:1px;} .ui-jqgrid .ui-pg-input { height:13px;font-size:.8em; margin: 0;} .ui-jqgrid .ui-pg-selbox {font-size:.8em; line-height:18px; display:block; height:18px; margin: 0;} .ui-jqgrid .ui-separator {height: 18px; border-left: 1px solid #ccc ; border-right: 1px solid #ccc ; margin: 1px; float: right;} .ui-jqgrid .ui-paging-info {font-weight: normal;height:19px; margin-top:3px;margin-right:4px;} .ui-jqgrid .ui-jqgrid-pager .ui-pg-div {padding:1px 0;float:left;position:relative;} .ui-jqgrid .ui-jqgrid-pager .ui-pg-button { cursor:pointer; } .ui-jqgrid .ui-jqgrid-pager .ui-pg-div span.ui-icon {float:left;margin:0 2px;} .ui-jqgrid td input, .ui-jqgrid td select .ui-jqgrid td textarea { margin: 0;} .ui-jqgrid td textarea {width:auto;height:auto;} .ui-jqgrid .ui-jqgrid-toppager {border-left: 0 none !important;border-right: 0 none !important; border-top: 0 none !important; margin: 0 !important; padding: 0 !important; position: relative; height: 25px !important;white-space: nowrap;overflow: hidden;} .ui-jqgrid .ui-jqgrid-toppager .ui-pg-div {padding:1px 0;float:left;position:relative;} .ui-jqgrid .ui-jqgrid-toppager .ui-pg-button { cursor:pointer; } .ui-jqgrid .ui-jqgrid-toppager .ui-pg-div span.ui-icon {float:left;margin:0 2px;} /*subgrid*/ .ui-jqgrid .ui-jqgrid-btable .ui-sgcollapsed span {display: block;} .ui-jqgrid .ui-subgrid {margin:0;padding:0; width:100%;} .ui-jqgrid .ui-subgrid table {table-layout: fixed;} .ui-jqgrid .ui-subgrid tr.ui-subtblcell td {height:18px;border-right-width: 1px; border-right-color: inherit; border-right-style: solid;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;} .ui-jqgrid .ui-subgrid td.subgrid-data {border-top: 0 none !important;} .ui-jqgrid .ui-subgrid td.subgrid-cell {border-width: 0 0 1px 0;} .ui-jqgrid .ui-th-subgrid {height:20px;} /* loading */ .ui-jqgrid .loading {position: absolute; top: 45%;left: 45%;width: auto;z-index:101;padding: 6px; margin: 5px;text-align: center;font-weight: bold;display: none;border-width: 2px !important; font-size:11px;} .ui-jqgrid .jqgrid-overlay {display:none;z-index:100;} * html .jqgrid-overlay {width: expression(this.parentNode.offsetWidth+'px');height: expression(this.parentNode.offsetHeight+'px');} * .jqgrid-overlay iframe {position:absolute;top:0;left:0;z-index:-1;width: expression(this.parentNode.offsetWidth+'px');height: expression(this.parentNode.offsetHeight+'px');} /* end loading div */ /* toolbar */ .ui-jqgrid .ui-userdata {border-left: 0 none; border-right: 0 none; height : 21px;overflow: hidden; } /*Modal Window */ .ui-jqdialog { display: none; width: 300px; position: absolute; padding: .2em; font-size:11px; overflow:visible;} .ui-jqdialog .ui-jqdialog-titlebar { padding: .3em .2em; position: relative; } .ui-jqdialog .ui-jqdialog-title { margin: .1em 0 .2em; } .ui-jqdialog .ui-jqdialog-titlebar-close { position: absolute; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; } .ui-jqdialog .ui-jqdialog-titlebar-close span { display: block; margin: 1px; } .ui-jqdialog .ui-jqdialog-titlebar-close:hover, .ui-jqdialog .ui-jqdialog-titlebar-close:focus { padding: 0; } .ui-jqdialog-content, .ui-jqdialog .ui-jqdialog-content { border: 0; padding: .3em .2em; background: none; height:auto;} .ui-jqdialog .ui-jqconfirm {padding: .4em 1em; border-width:3px;position:absolute;bottom:10px;right:10px;overflow:visible;display:none;height:80px;width:220px;text-align:center;} .ui-jqdialog>.ui-resizable-se { bottom: -3px; right: -3px} /* end Modal window*/ /* Form edit */ .ui-jqdialog-content .FormGrid {margin: 0;} .ui-jqdialog-content .EditTable { width: 100%; margin-bottom:0;} .ui-jqdialog-content .DelTable { width: 100%; margin-bottom:0;} .EditTable td input, .EditTable td select, .EditTable td textarea {margin: 0;} .EditTable td textarea { width:auto; height:auto;} .ui-jqdialog-content td.EditButton {text-align: right;border-top: 0 none;border-left: 0 none;border-right: 0 none; padding-bottom:5px; padding-top:5px;} .ui-jqdialog-content td.navButton {text-align: center; border-left: 0 none;border-top: 0 none;border-right: 0 none; padding-bottom:5px; padding-top:5px;} .ui-jqdialog-content input.FormElement {padding:.3em} .ui-jqdialog-content select.FormElement {padding:.3em} .ui-jqdialog-content .data-line {padding-top:.1em;border: 0 none;} .ui-jqdialog-content .CaptionTD {vertical-align: middle;border: 0 none; padding: 2px;white-space: nowrap;} .ui-jqdialog-content .DataTD {padding: 2px; border: 0 none; vertical-align: top;} .ui-jqdialog-content .form-view-data {white-space:pre} .fm-button { display: inline-block; margin:0 4px 0 0; padding: .4em .5em; text-decoration:none !important; cursor:pointer; position: relative; text-align: center; zoom: 1; } .fm-button-icon-left { padding-left: 1.9em; } .fm-button-icon-right { padding-right: 1.9em; } .fm-button-icon-left .ui-icon { right: auto; left: .2em; margin-left: 0; position: absolute; top: 50%; margin-top: -8px; } .fm-button-icon-right .ui-icon { left: auto; right: .2em; margin-left: 0; position: absolute; top: 50%; margin-top: -8px;} #nData, #pData { float: left; margin:3px;padding: 0; width: 15px; } /* End Eorm edit */ /*.ui-jqgrid .edit-cell {}*/ .ui-jqgrid .selected-row, div.ui-jqgrid .selected-row td {font-style : normal;border-left: 0 none;} /* inline edit actions button*/ .ui-inline-del.ui-state-hover span, .ui-inline-edit.ui-state-hover span, .ui-inline-save.ui-state-hover span, .ui-inline-cancel.ui-state-hover span { margin: -1px; } /* Tree Grid */ .ui-jqgrid .tree-wrap {float: left; position: relative;height: 18px;white-space: nowrap;overflow: hidden;} .ui-jqgrid .tree-minus {position: absolute; height: 18px; width: 18px; overflow: hidden;} .ui-jqgrid .tree-plus {position: absolute; height: 18px; width: 18px; overflow: hidden;} .ui-jqgrid .tree-leaf {position: absolute; height: 18px; width: 18px;overflow: hidden;} .ui-jqgrid .treeclick {cursor: pointer;} /* moda dialog */ * iframe.jqm {position:absolute;top:0;left:0;z-index:-1;width: expression(this.parentNode.offsetWidth+'px');height: expression(this.parentNode.offsetHeight+'px');} .ui-jqgrid-dnd tr td {border-right-width: 1px; border-right-color: inherit; border-right-style: solid; height:20px} /* RTL Support */ .ui-jqgrid .ui-jqgrid-title-rtl {float:right;margin: .1em 0 .2em; } .ui-jqgrid .ui-jqgrid-hbox-rtl {float: right; padding-left: 20px;} .ui-jqgrid .ui-jqgrid-resize-ltr {float: right;margin: -2px -2px -2px 0;} .ui-jqgrid .ui-jqgrid-resize-rtl {float: left;margin: -2px 0 -1px -3px;} .ui-jqgrid .ui-sort-rtl {left:0;} .ui-jqgrid .tree-wrap-ltr {float: left;} .ui-jqgrid .tree-wrap-rtl {float: right;} .ui-jqgrid .ui-ellipsis {text-overflow:ellipsis;} /* Toolbar Search Menu */ .ui-search-menu { position: absolute; padding: 2px 5px;} .ui-jqgrid .ui-search-table { padding: 0px 0px; border: 0px none; height:20px; width:100%;} .ui-jqgrid .ui-search-table .ui-search-oper { width:20px; }Mail-DMARC-1.20240314/share/html/css/ui.multiselect.css000555000765000024 353614574361234 21547 0ustar00mattstaff000000000000/* Multiselect ----------------------------------*/ .ui-multiselect { border: solid 1px; font-size: 0.8em; } .ui-multiselect ul { -moz-user-select: none; } .ui-multiselect li { margin: 0; padding: 0; cursor: default; line-height: 20px; height: 20px; font-size: 11px; list-style: none; } .ui-multiselect li a { color: #999; text-decoration: none; padding: 0; display: block; float: left; cursor: pointer;} .ui-multiselect li.ui-draggable-dragging { padding-left: 10px; } .ui-multiselect div.selected { position: relative; padding: 0; margin: 0; border: 0; float:left; } .ui-multiselect ul.selected { position: relative; padding: 0; overflow: auto; overflow-x: hidden; background: #fff; margin: 0; list-style: none; border: 0; position: relative; width: 100%; } .ui-multiselect ul.selected li { } .ui-multiselect div.available { position: relative; padding: 0; margin: 0; border: 0; float:left; border-left: 1px solid; } .ui-multiselect ul.available { position: relative; padding: 0; overflow: auto; overflow-x: hidden; background: #fff; margin: 0; list-style: none; border: 0; width: 100%; } .ui-multiselect ul.available li { padding-left: 10px; } .ui-multiselect .ui-state-default { border: none; margin-bottom: 1px; position: relative; padding-left: 20px;} .ui-multiselect .ui-state-hover { border: none; } .ui-multiselect .ui-widget-header {border: none; font-size: 11px; margin-bottom: 1px;} .ui-multiselect .add-all { float: right; padding: 7px;} .ui-multiselect .remove-all { float: right; padding: 7px;} .ui-multiselect .search { float: left; padding: 4px;} .ui-multiselect .count { float: left; padding: 7px;} .ui-multiselect li span.ui-icon-arrowthick-2-n-s { position: absolute; left: 2px; } .ui-multiselect li a.action { position: absolute; right: 2px; top: 2px; } .ui-multiselect input.search { height: 14px; padding: 1px; opacity: 0.5; margin: 4px; width: 100px; }Mail-DMARC-1.20240314/share/html/js000755000765000024 014574361234 15544 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/share/html/js/jquery.jqGrid.min.js.gz000555000765000024 22012214574361234 22240 0ustar00mattstaff000000000000ߊ]jquery.jqGrid.min.js:[{۶4O*DIΥ'i7q'n+= ^lIʎki/Ҥv$2 > Guy|jk}?W?ׇGkxsrU4L92 |80&I^x[Q``aC)[(dIBOV G*GP{;AƋI |C|[r(X]3_91ωj9>8ڂ:ނ:H/ -aSʁͿn` gꍎt^ibNߊTb\ѫk yq'uV\g8WMWS6FPז q1?b><8XroÓ K:dNn\!5='~.-2UcbM?\8?O?0\F~/UR(Yl;cXϊ%EE:@*fX!ŽeQ #5|}q ÚyO6dBf yOu}Ne4g[714*LTh!lT25#z/w")ަܳQo~­7>Ifkhʈ`}wuwx] G=jB4NɿfSH8lH*E _`ڗA^r D뉲uy’U5 !i(J=99WΓAPL|˻CQkE2B7uLl'pua?&>Lacԟo\y("y$oRoyDfScv@;_m8}j1a>(@ghnXFswBTTD3ޒP,x"ܾ@\jn>\E}1c8p`Nw݉͞.?qI8!o//?OCȖKx;w%.0|.[%{bZ#Y`H~ȭ t.4 + U yðA(/nÆ}0rˣWP\mQ-#,E?`L7ThN r=rc+Ѓ`."!HZ O}䛿+CjX#l1*c;m}bt8˫]X0 KRßg( S1)Q^uwE&@DիsejUǨnEk :Skp*C]ҬhVҝe"j]NSW((H+9 LrV=6Ri3gݣKKF`ᘟx]>yș±k+P`E3i0'm(l )oa2n욦bmspxмu/=weS360dsL[8k@>BĨ(r2i܁;2 Ke7X8FZ{&U=Q"Z kNkLrc8k5k%g'6sTsf `8e6] e吁9C" -i$ qҙTboh0!%5Qe{v$My"9*–SZ@nއjlfRu_wp_Oc4)yҔe4hJR*_)ض]nQ~͌F4W4WWI6-:jm(ibR;2URx}wEJ^Rol܁@5$ک}Q0j[^ȱF}&]M#+Gٺ (VsoQV5:ªUk ^7dyCg'tvzƗ5KT&{ uA"|/ %8b&ֽoN-W-OS^u'}hדF^yky!}Xe45z:KpkDhHq!.ӂcʌ#fx8}q rU`M5#Q^` &a5O`b/Iu:q1 G}e_cXCe*VzI׋d.~|Y^&_}r?'qQ ,}ƪ9 u#c{mgqM+ey@{a΁uYdtog1?BVAYv Gcq9t;B2Lc{G$nwo8Fǧ 'c4!@w%%rk)䥰: ]H]Hԥs@YɮELOO߻)8>,ͷR&1'eو&S8䔮-!L]gv[@20,|za|o(-{a=~G= y&O?xBHe0!<_4 <ףgA#} ~xq`T1 j797NU#hui ǻ7y<ߓM<'-@[0079o1,k`&%d5r1É:.DR78Zv)(6 60lZs: ; 7sr?UKVWѸʼnMI> u{xNɘ6^{"@Ȗ#n:_ 6/?z}p <̞v\mO,ã;+^D|LG2Հ3Wدf7<㏋$_'`6p }5| >>΀FQb-H^ݝPlTlDj#:'e#UK~Jk ȿ#V LIt6ddÊؼ!]wJe4meQ;YØFTzN?E˧߬7oF{x#"ߌ ۪wSL;lwr4/-)d5  ?}9?e(3Fי@/WY3Yކ 2K qim=DCh0;fw,AsfB \8YD<RsI9$e2vj~8^Z)_ֿꗕ=VeLu{_s־Y̑ +l=alecXryP.8.+0$$ҕZ"mky>Y NС\9gre ; $rul2wm7B35kv5e\k-ο jαNjȩO3GhrelCt!`QamzfVcᄎxlkgV}ȧ}sUFU@jdbr_4?7޻[oJNj].Ptn|!FzҎvb邡mgW\[og^-8ɰ'>ZgVHjw3pKէR&!)]B'>,*\j3+ۆZBNn3Y v+:ƱǏūAAػ] T=RAu9oY'vXo&Pf,?z)۱ڢp=0Ӏz|6biY",&|>hE힢m=svΡYf#6#,`s֞t{A^kB4&]oY:>Zr4rn*eJB K2/|iIQPN1р=//KS{Wxmp~pRs/S%!j ЃDK]!cB.6P$?ށ!6?. 0~$HlȩQNH^ eSFl_NZ-ts w.wZbj:;B:Ƞna6Z w~lm}wbc+ڈ{Z^OWX  6Rm:B\{ʳم =F:Sj3iA4L;:_häs13 dеGV;P8IГ~#>ff4КLoc5#!zdB&P}tH#¸սXJDP1[YEBL_q,-g(w~:jJ䄲!Tf;"Tr{czyl9HcdhHS[eK<Դ5?g0r=C!9 C2`s荖h(+|#I>L=e'mgK^䀇`MʛpJ Y3.oo}aNr\oVM#BS}TT .S6* ,[edW FIw$DG!XEj,)bA8J#"4r]]k7iSl^w;G(1^N{5:ؓTsECʾC؃,W>}b2$ʕmdn 3zg! .q}$DLF^TF4IlҖǍާvI@D)+Ta]0Rg%aZMe0߻|qƜr.  Q+x +Hen ZKP{r8`&M?ǰ G'}O(^cӾJ5GB&=B g=C ;^ڧ3܂B^$węmPb0!jEZ!_VKRK4i|#e%G|R|\DAUNU.?y#0v]r'|M,*f6@ --,ӌ{T3m{_~xL1gWAP:^HJeNUPfb] eC[O1wS6t-5/x&Oi/jNcke\}uOֽ ⼈I mmM%I9czhDQt-[}гB9^iv|tu7ZM7#H$^t{=Q@Q5&6;F65 j#M1(⥮ҧ%k2%>l yOmWw,%}|tn-W:Pn34+QUGyaډ55ܼAKFv_L//5zOj/o梁?D iᝡٚ~@(qyx xvq~~9"& Gk׼ՂPF~F#ȶ2m Vօ6곟`0tJ/c?|>g4;N<[Qs ~@S9"x~W\|hAVƊ3nw/`_DZjQH%_ULb! - V$x \;Eaή|}s zpz(f8E{Uݡo UOV=-U?k $D)%8// >'(r|(}<>i#P=c!5#5p!u6A:8f76lޗ|_ϧ鉆yم"ꬌ$VaHѰfUdAcZjM tvA.k['u3ۼ]p!4-3'gM ,5? ܭ7+t#͎Satz,PǢCq~J$үNIw. AԷGĹ|y)m,%ч ,-/%i΢P "q˝Ԗb-ɣs1I&eВP*j]j$Tc-lCoXy&B ?@hi.,%Imf)w KVezP٤?:l%x[W鷼[ѲsKr+9~у>ɑ2ɱƒ>ԸdqMQJuÂ=SYJ1_؂ -(>w=RlX$lD!2 j_^/#9Ώg'IK'< Yb0f a9aC(}5ɱh{|Ib]i,>,ϓԄ0(S mx&N'iF[&\(I.?c̕Bl2Jq\ S99lU⇫H~\oʏĘV]˄>5D]<Q$?B y"fb-yiMW.v)߷]e{ CvϠ[X93M_[mji7ٲfL+*ݿD֫%y8ea~RBoK}e膙4"@ime]:k&QCV,4i8;Jnw5C8|͍果c߅m_Iȯ_2NHdSgѦnD=}vr ]pR}wYNRCOQުNh/!SMa[P9RB PL2=?ENwb*?f0&* Md9w%fƄ݉ٶ: ۚlTҝՔ!AQnoSRΨ` KID,2ӄ D]}LrVA 'Y]̮.,;0H4T"Ttc_ hz|X_7%en,4\/L#ˀ $4}IL }ִ}@3֚QwZ7>eL N\K-5:̜Js_Q[WfJn6 I y J?%qߦdf6Dą aGIeWy=SMbE5m}2uh%݋*?DkeD7rkɛm}2ok ܛYb+FQcw]/HG~ч NuuC!7w(D;,YnVRՅMp%\YehxvOSr5@/8K}BDGhY3:%?= Sm3۟U9gg8"ofJGE{MF򵎟]~d(v8j/SuLo[>;L|Gfr.lwjlP uwx>GH &'=POЉR󫾓A'J7D6)F0{@`{ԇ= w4<$i8ҋ.|Tk퐛r p?mB}oݖcCym+ )k_Zy~hL? 62̬ <^|]``ҷs[]n#b<4_#%Fhau *EwkGHԨ̠,|bDprY'te6/K:!wrNzgc# #IDK\yG\#pqWWnBŠIer/X'@DwqBW)!uCq]aѰd6]ثc3uCýիBItmR+ҏ^4x[ n5E8vM$ם~a.d '*&˦0՗r/W%@u#!Q5UI|4-߭>lЮv] ?AP΀>r+)gOBA3(7< *d][4x4yM+n&ІB?x> 'EK47bC9%M8  c*$K!CQ|A+(lp0š8طf?WRwZ } R(|o?|)g>ΖiEzF@OhK}NPA@x:1zݦyxnأ;l #Z0=~k*GH CS`dKϲQ( #ZJ 1W0 aT#fAK` c]^ٖ.7,|?yt9ZQUsG3/X_^{L<8k?rxRS}e*ة:7r^Mb-Ƃ5°^儥,8=^q${2Е)[E@^VX>ll eixW4AA=~:],Ȏ!l:zU?ݛKZ[Wr3/ HQGD%6hPF6WhIIH?D:]?1 80}|oCM.Q* Ņ$([!qtTg/'ty~GvԄ!gOѴBn.ȳsc*d#}e;KEϡ.̑Q=&6-|ի&Yvѽa(BZ*(Ck;V#<fhڸqP+͚P~BnJ}p'O̩%u<0=ַU<[&3j?xTl핡w-ڙrf\ I NwL՜y!F|H11"}͐z>ekKlSk_fwq< tm"҄LlEbJBhm~36f|Qbũ0c,#qg'e+Qn%Og=ՑE3mA>|)NoE3soqc&_GݭeNjkHjUTpᎏkw!1npdJL g`ZR} Sخ7oF9@dOeR٢p7"&h&оY/t זcR0yѤk+^ 3Rb1 d]"|i]_~uNPzGXʾJZ2@ߡ[OL lgX0 IR[cD΢ ks`T#m)ճYO|oNOQ*stIi.[ner&z=- 4HzͶe^nYk$bius:/)~nT@Ŏ :Z8({+amءۣ;LdlKAYځ\) Ox $A~vI߳ǝzB?>%%W-U?~K3P !6m(BGf%5Hڳ}"YvD&66~hTJoFD^ەnlD`ZU= с=%l<}b.i=_./fp_&l_8$ ^q0 mV))zQya^y럳rENU( 6]$W ][]݄9e\:+ &]A,(*sɹohj,0a$,ěy01͓DP, <=11ns\wZըxȤZ*ck9m8tCRϸ&:ds{3$4;l1SsI2 #E<Ʈ߶H^W\C$cV}:]EE8iYTfayKj` /tLU/ݞ~{a0-;ŏ?5:=)?S_=w#2ͬ9 6k3 ;}B`A<0ּri+t&V$5v(zTqA 6[!JDQ:j )!2%ck;U{?#s ȹi^$RlNm\>c޵»xtnZJ'L$eM_l9rv(>&,,a_6N+k: x+?Yè1G:Cd22,@ 2DJЩz |v٨&k45p LJR{v[q(ЦͮJgf]:n%0^TCA[h۟HUkf]+RvAar&2'd,Y:6m0@|-wDOo+XxeSX/+i<#s.J<G5Gkj>g %6H`I_b [}vkb1Eg\7ΧzԺ!# rk|XnyxUbcyO޹>(R2wpkR앆7R<'~ TmMWjD~SN!瞔[@ >SIR aQYJYnIb<8 ZMWZ ԰XpQyeonVz&=FU!땒XRGx9؁y}{8y. i-|mDNp16FbH[LOlOh p*Yƃ?m²r48֨r|d+셅ǷsFcEreGhG!&kXF(|MFŒ+>K"0> $g`=aSouZΠ|7iP+J}fO*J궿$`!0,[?:L)^rRTjѷV˯0<\oϤ@\~V#N7uv}H9=RoS5pPBlM1EJkNU :yS4^頀EYvJ`n~q(RMUdÌpG6Q28eTd6bĻ+,QCFPWYg#G6DR^ _װt)t=SIV*,ˬ#k|Fm'V¸NET՚kK"22(am#hWP&K1n>oFxXG^3Ħܲd!|/UhU"s_\!$'򲊥8/6RI`#5mc{-PI+N R+X d$$~fgqځ-/|]6`Ohps.X$wVYfYif(i.hJZTdlėTaD IS((s ʦnK)[8~e!ʖݬ xfQj;Fx-g)F:ҫ! PO75ȽtQDUzP%j ken8PU/G#K굵fZޖvWx'El'v(_dcT 1̟$6 TF_eױ_!2"YjdVg* MFقk(!Td_4]Wg2mvjh77k ыdu[gۛ9%pnfHJE)EÅ(MltзՖ"Jwp5m([ 9gJӒ3~^k t"2{ӿ7|^ð}VHS6ԫL aM+l~TF؀pY˛˦);n}y!b_ ZV\P381&"݁ [f5.B@;JZK] =zd]=M\62ZB,cU&c%_rh+;#[öջTsb |Mۮh,gc[@ @+F# p p>]<*˙Q *ĢL<2//:ɭh#ΆrSy%%&پ^GkkkX`WV.x8S7SAM~']ct +qv6/˵gKy-Ue˾1|ȷtU_y.GK贋4@LUhQF4XNDs&hdOӌ:cGtګN#`V׃~P՝TV9)#!<;͘f`4w3Q"8VVKf$ ݹL '.sK(= u=Gv>9ZZ-#%4BՍ% 0&cY2z]cjV׌͎5NHCw̱hs\YocѹƤCkk1 kȣ9my" ãWM FA~ ?i*C%*6'lNa9F-c@N]_d_WK4rs"Z#D~C}+5TA >ܐqPؘn1ddȣk#HUfcz+,bƲ8 Q7_L`9;mR4( Xx@Qsơ P[[W'OSӪ(~ƛr.ԷNmߒu5-eQf8vz ׺bwvpF.H;Y4L,pW6P$d} ;_/`\[ȣ9&òbF*NRX~3? d&;:qxǏY+\}]/ gk (UU2C&:~HY]2;F/`܃FPNM*T.0dw~ilk.{ #XNի(eRjU~Z滒7$V]DtT]%'05We!*\J+I5/WeF,xcXku64>DCFH!OX(킭m򕏭̐)3^ fzTh핼h&Y#lgFdT!O*ǬFRJb{$o6y2$\lCCȎkd kY];d@I5TO0eV8~/`j-՜:}::MB6J±ڢ]S*/ѰRpѹƣ2̬0?d}rTx6Jx*ygXgV=#ETs}xtu58cbF5Y1Pӈ֔1\hk,,j/Ғ_C8xiԸ. g!#x->Ж_M">|Xt *غ.0T٭CA'o-beKB{M! e>E2G}V3n_ٓK2IEs˜;5%R/h+/!o.ԹluuH=_&!c1h rZp%^@|5$]Sn~q<.PRwx̋4 ZA~}h{:pWvK Nyb*X }Gۂv کs Eaj.sFfP>ST?_$e_f 3*o3RS)"!;F~t@䋌gG0C- |ZE@%QRWz1$!{BT~X>^q P3L1-L301:^N7 8(je)F0$ݮv`cQ)(dsFVZ:l1sjQU^G ŭ5v-'jDVCs8Gba-_pLѪFh*U썎EԸ>({H8nC7h(%p7v! Pf]coYTZ>,XKΗ* oTM6 ij:RSA&bqь[MDR$+b(%I;wM9$R,wHzI䌾K4S Qe}1+cT;S~6C>iCi d+TkÞ3L+ھVV**.SNb&79?IO#2<N"]b蔈$肰[oFxA6mz" QVJD}HP2aP5yBTTQ$Z`a c=́{v&TuCb$:XH4ݝnCKX/'LhMg*L1Esؙ1fy_Ec{m7evlV@{IN |+emXjf\;<;被l ;*L(Q|T^˹F5-}Gu.򱟗oRW&' ]c'C#Bu.q}`h+S9ꘋLpDQkei9~KҌ'?d 5C1vc⚍]O9Mr?]jGx\Sê 41QŎPBi`՗CI)oUd#?iŠWcZ\Gwiqjtcj̀ѐXdQ>=w<9K>k2Us6-8gS9gjT2 9_y[&~t)tf㕸nϥJ|7SW|e]IOp<-͂hG_#ňyh](y#NH| {{r;dA[3V_lc@"~Ѱ`(!tG,03dv.WACc@9JP/ {]?H=ov` n`d9S!>_tv#`IrnB¨qt"C.[p1Q2v}[N_-)+=%nFYv G;WP67E}᱄ ׿/<}]~&~cV˗7W HcvDhKak$Ϙ#>Fh~/pO12$ʤeu`,`GP ?e&;Dh휻UGRa}`%X%c Q칄͋{שre8dma@#-0RGN̸zkQσ\Dž_5US.Y59in#㸋tI 8~ +!V%%C -}рd~pD 0%J *"ȿ­(uTp#mB D9ho b^Ή 8O&YyRG.X-ZIpBG!N+]zabܑbk> !]0e`3.)u&`cW$yKS N2;dPa)0>_ `ZzkԊmP@{&0|Ǡe= 3EV ngW&*N)0So(n$E[ ~3 p tZI^zD,3 :X6lN%(:Wɏ6z/ 6_Q%Tp_`/X5$ݑTRGb%BKHH0X~*<Ė"7Q w] 55A 9RЙ!eD RYDYQ.v)69RۘJY'\oDj?\\xҲ zk"9_|aS!t!Ee .ѵ2h$W۶)$[CmSxɆW_t9%,!;σQw:ettpesbϠׯy`2X?{D:AHP)N,!rX\zldU='A(j*[A$_G!6NfNʄ=n{Mp `ybz]Žr.!yW1~xzxg9$ |H <ߛ v;T-V(i&73:=kZ;E1H⑌2^eFQvīmQhU A4ڠw^Pdf/ȘyBt+F`TJ\=;^G,o >.Zށ"YxE!cΓ^0 > PB5,V \L%c6M6}E.p̢;Oؖ筤-ZW7lbBe*׹DY 630$>8)[r%AJ&Qz3nyt ͛G#ὁ9C9Fuݝ+\;8O1%`Ցk9UȧWl~q/y}11E}<53}1:p<L{w4o[P{Kb7q+jU<⾵*ϗ tSyI>PDn*І Fշ[˝ñ_!ctw}?|Gq;wtO[DOcsY sdxJ0&һY%d,a9ԑ]{(fF ͂pɌTr䟲h!lHnD[^QalY}aq~{WuBp!S8(K<<*N#UoɪSء⸌ Yd-] Ο&ԑYGrydܽKS?qP/=D%Wj ˫H6@tNSq=tD4ikPIh Fҁr&:ŋj%c&E"Ґ1S:*BnVZIp.㈃PZs^Nv04g='** d-vZM5+9vm\ꐼ|d @+nPzt;߆rW^P:4:.8 ۘq'xp9׻V'&՞Z"#E7w ZBG8NĴy<\PeW;ZQοD9KZy\5'T4Si~):|h '7撚EZ'}uZ!} Fn[GIB1E;U笫N\E'ݱ 敳L"0ǘ"\y;mgO'eH3a(䦿g= >4ʹ"95xhn !^QLӚFL $#%btP~;ZT(dO,JY`[9>*Dc(!#]?:Zz'w&Ny$fgJ$wCJKDdlkz0á@]۹p˱V96\}1u*6VgɊ rWEZjnϺИu1fKK2fZ0c~N3z6C^;dO(>WHOVq O,Y:4:-&KBθ4, /cӖ|%cJϩf}DAwStceVc=9nv~TCCߏsdhf_v<ݿ߯M+[kEƀ_lym6`mճvNeu`zP];i];UE{\)ܥB]:m r8AroQhGOגKe]{E[w`c/]xfS^%mfˑW I+is'J O|3K hf Uޟۇ zݨ>x@cwܶ簙1tUݎD891K2 ?, Pk1g1"'`RҸ@K%^I`dI;Mv" C g KzTQ ]gZ@qҶ(ۺ,S]R#571f#Nޡ^#"m1X$RkTޗ.R6u}+}8:H2vJ[JŲ 놺jJ|r3:#bK8M2Fq#N29 I8J///hN$K}oR+A<\U*1;dUpgu+}krT#s>7-\n.q{Pc.vg/vg_>W몮ُM|f:J6A>Žpyt:|CbWCMJX钟!}SP<٠[>6j;4W JXPL=[:xIRØTi>ZP۔mjՁH񎶉LuQBZ!`W|u!+'oWt}0h铤0VraMӾ2Cu]D iǶp2fn,=~(yzO2̯D WP([gh |2X;#m$$GФ4TM. eG&S@mAWptkR(HG ff8TS}w?q\[O$K8؄*ͣ*7owB SJ̢=QLB$tqU!KIV1cMPO[Um<Ǩ\FxV.7 /N,f`ZKl"1aJmXN`''i]|||jH䲸cZ1҈Ϥ/R[qɍ<E#<'&j): V8Q^'$ "K 1;q <3t9.Wes勑mцoHEt3Za^%CN .F_GHǗCa[c1Pk=#_u-JVo& L[ Ć?*bK?L NbL216_lyBĊi6&/=#`4)ƊU1K \cxu^.P^nNٞǰzSF~%VY+q$~82O&.fT.;"($5'r С&92EkaOE$Iepb 04)x Of @#D&XZ t&>X4W78-5Xج 3I'T4S70$;8gJ)9F=վJ#ꢧ;UJN@ m 83h1[#=چՏ$  JRa}aƷWD!vAMt h&HHܯ b) ȻBM [<@ddz{[L2:a$U㞖(*/kfH:=[IwSz9eTX,d=lsiPӅ&l!lS јr8=Q6^(z%]V \,M~IԒ*| Te-?a?4HHڟ rE5cJ_*|].b |2jD+"r<#R+HHΣCW-r`Ҙ1i2Ԅ| \[CN/t!T'+ LXþS9S;,?3[\^f7el^YCT)\ZulwDYMXjB/-l.OP|cŎyD]u]S:yrG|bbE BE5i]5$Sᄅ") O'{N)2ES5GSS4 oRp /~FvIlz1܍| s^41)eJ*MQ+P#u6/l[ oOSul4xK9v[r%rx}WY"U& JJhHDTwJK J?m#CC%@> |K;:|AkpǯjGO/v 7 TDw|Q7\@EsU`Ѩ#YaZ3 4d+ 2<5c<8,w|};F`DX{[hiko' ]Ӣ$̮~t dn*|hL ׋$6A Su_j_*͟ky3j`y0=gIX~(KLjggŹXaj^N'm,UE8-wf*RqK9pK2^k6Ť߳.q!2fϾ+L\9 ەEh̓G<j Dk$ch `KyaF#4~GKRmCf }!esѥTaK݄+xvY+UmZFʬx]=ꛨKe {Y$8|c6* gAs& l|;%5"mvDc$VPV1 iF{m&1ggꗅ<撚;s<˃H0'<4˟[qO#|u;j}(dRS?!-HaXf^)b7C1$t,#>1`NTb T^lӄ$q`S6xĻKּBGGe?k+@v!;DHd)H"@׶P^@n@`#er)SƇ1$_rUU;] VCS]à7=fVr]*.D[.Sj;NnV, J_Bӧt4 53C^fY|_dlڮ:i+kM p <\po1/2"cY^ݝ@K[xuXɑh,&܌m, c󕔖8p[c#Am!I]$b-)7Q1-ƑFv#ZcS ; dV ͉eˢ?v#0`)sj"Re'!i,*(kGH#V?0Fi7xY+[;buLer:+jPn7xx+3<"1P&W]$<*sQ& d)i[9Lz=\r%SiݚAwƕܖR=|oa1Mq~Ͼ,CFwx#F  xTݕN(%[_o|w!@  jZݛHrf,E'ma˴7ЏBqVZ*41^*SE0A 2 z(iyx]}/?'?o/q*'Rt?'pҊ>+$-IӢ1~x՚qlÓ_2mQcJ~O0FS>NTx Cŵ] ΃ wޣ+w2S}Ϳ!}Ub<Twyz'\{T}`=z!PdaT OMk>ԬKW͒pYݞ3v˸YM/ D sZ4f NR0ziEzJsv6Yc:ۜzK[P!y-uW H?Jd5m[wmIzIi/ uv(flµ(+dF{"vߺ5˥ӏj\(X'kyJKnP_"[}>Ó1HɐF@Es8ƑO"@Tx Qm ^:u"O0ZlvFk+ Duv#|vvjN}+9.KQf H1*y䍕Aø&,#ȧqbxӎLzHgy΅8ƒd"<3$qr0da h=G|pu0>(̑nM(bH.8DDBJ:ɩVlX= 7nA֤=Y拦'Q|TbpjToBXȀC<{k'Q^SWwPjjT٫ݫ0*#FFωExS`&&|%E ʜsTlv%9W xbtpx7QTH HZov;FJ[/ohL+)K8Vu.at:Wo62I;!f7b7 v;f˛ ^#S^j`XKDUG@CwKsj$I8jRW볳 OhPf&VF_COh f90_[g0#i,xë,rW>"Pr%A@⑁ e, N t@yv0Cn5Yd~ ދ  @fڛ"QR t ME]V\A RTY|Qoȑ3؉oT?JfV/RHT+`8g+&6E/٨T.~赝h.oӶY"H3{D9#^or^g1G}nwbC]m|ɾ "k6tn 麬w`մ^ t݌MD+0> ʾOPOnL'a.?Ż!#]5Xi; ڜ.l. l7bp>?ꯃ"v^zCj:Z$~(/X;S.f r F)`OM HFʣ1bN_.ġis+2Ĥ c<˱[_wQca s_u|Y ȝ HczFvzڏƄDZW}|>AC@ފ+w9IIZA瓹NM ^ ' IIAQq$|̀jݔ,9M,*"g+r@Ti3~FzG,e>ⴌB&preIܘhH0oZ_; ";"TMtԟ.d +6So!°gZh= c`4gDtAJ^s>gS"tk :ZcwV/Nv+`I E- BdXp-]U_3lQ鰞z"F59om3yt9+ \q{1ـH"(N[E5A< ,l!N*S\t\nZ&ZEv xӷfIVբekV> ln3]ۃ^M+6ˤ]J >6)D 5l>L~"{p5BC.lX-Zr\@},>Ni93(>wQ Y=*;3ܾ|n?(؟!+.M>hiZ)&tEnAviDATJwGJ!AJ>l̗ *VX+ Mz1^"c,Xa`u(VKTC;j ω'H6'rDl;\͓YWBj !|݃Esf~cV|vWe' ac-|va\-#ug::<8sϠQPp=EY@ٰc:0Sqy뇙cAf{%d~SdZ#ЇGa}kas5leq?DRdÊC/S;"=A"27auGe\:bT$ ȣG77_yMMM͛hRj bNj `W&0t>`.:%kR@ӶSEM upQE֭dB!M~F>FJcR8X~3*pcEG̓ENs&#iJqv~#%JK?d U~ OE"ȧ5N g}0Q7G3 D#E{~>4tW[O^Oh:| ɨ^s&Hٯql:*<q^̂DA]Z gherkeX{QLU iUWBCue&@oج5 HGyC0"h'QGL`'^`*Q87 A, | , ʭד~bzRqA J8;֠VAxQⓝayhٽ!}eѶZ  LԺvЖbx'ҍ.t;[CpfBTsv=`\*wb B9ɎɆr@P% Q8ɻ9lF1E(tPNVggeb$䞄 WbFo\S(Ur;Ag go+Uoĺ^xI6)72aWw7B;`($PƙI% FYF-ح ]Q zPja,#?F*_pBTz~;wŸ"e +F!5B{lN; 'L { }c4Rʁt>ƚ/b796s .`F`e8DTTO#l6U|_" fNVLl9<,!U1^ˣH3EHZ: XdEMSxmhb0tt6 8$2NEalVa 2YjQWFnDL(h`W+ɘ!D BY'"ݖ0TusU *\E gRP[&%N?CGQlAD18t[G\sdo`dxY L-ƕ2?~뇕#DCkMqpE!*t.lF"pXzA}}j~A雅ȣY=/V/#:%$ 1e sRP9-=#;n~`64wBXb%M75Yl BV$ tyȫѦu|Wc 5ly!V I#uEg|֊Ҟs.V* C&kC}^uڿ~DBn) ȑ"cXq ʀ<'Շ_BgF*"Ց^ƪoHtUY9yWE1.Nd3A1)Lschz9%k~Vsn[,/.4?/`!C #vzXXNie+6 AǐO d0j_g\TNJb-eϩ$ҎMfSbb-GU_ɶ1;r庀.Ha&%J2@# JvR v=fU Mq  /MW N T/ <%a5v|R  !|oH0k#]z%5E G&D1z N9 ejƏZ~SM$7̳C9[!PUV)ճ7P)?Mk{ӣ<~8 GrCPSa!- FBYVP+ };jse+k>v۶NlA[Qw5_J-m +wD#8>[ UېQV O2lQV,!6! :Hr{+I5Zj}_3"#%K:dl 4ѿ Ou>:[ 9BGOLcd6Zr*mP{Zy>4jTzPML3ߦb@ۋ_iӴ*9V01z~rnHl~؂ 4ȹ^5q598@ |F"8 ؘ qZT]䛮Fq@.G(5Tt}54W P(x(_l'P/`]I8㷿t Q7̓}c\dIv6A^P~NճeI~Noh&3lԕ|ZQ1Y(h೰WfU꫻<%4-`\-C), mSszs^(0K$A7 I+aNs['OfDĤY lw|Z?ڃTZ:exXYNΎv4JAǍN KFS%#?A1/mLocMf֗X~y7AW d)p (37Z4^xr8hcy]} byw;aQ<,&Rc0rjNFURz5Wp0CH{ <1VGuW<uyxPchia};K8"0R:?U%a/8,N.(TըXȭ T+~>Կq[\vq .oe|ݽԿF2iu巿|v:N_N=C8ȯeiK6b7ֿ<@MLCAfڣjI`1Oa#4ІyT5FK{ {lh]bZ=.f9α6uhTHjC8ӧ~5kF 0eo~AYXȖQҼ,ۄx#)@c{c?.%w#:QF;etYE QjCe {[/JQuLQ~}]*-Pt| ~wRo=+Okٽ~_eop(A.4!~[7߼1>,WGݙ4s2\ܿi߿ϩߴaaO?=gp8q:(F;  _:V=w.2Taivq[A'Vifz-{_X@ 2nzs>Y'&[`q1H=Y)- 51"1e @DQa:n;Muq04Gp>oMpz \yo}z| %R7!(o7ߞo|#"( R @ ȦxMfh_m.{I,5 `. e\ci fh;{ srp2+fiw(m]T:'d|2϶e|;2ͤp="oofy5Z:P3c(Q.xc{30$7sDÙ`1Lt=x/sZcT2eM/%QFDWDz"/W\`D`L!&wbV21ڊQb!X1?ߋx!"[2'>E\T+oϖhzz[ɏO=Ѡ[=gOp"ɯ`h(|+M[( |F'1+ MH|Sc'2*"Ga B<82YuBC#4av,JH-/z4٣bpffP T=9eQyHn3 `lbaRhRhuܧ0ޫMJ^rZ`a@+_4 r%́*Bv:ceS &Fmq0&P9!jYH^> [& -tϷ#"XkCqWGnur`Fy3\Ot@Ll#O|xg}LCǤ~llz~N^K6Ko1ܢ"Y t`٦|ʭvYN_d!QDrHn:Qq:H:-}/6gaj3Cs(DkF"n#7p*AFRuXq!9ƨglv Dj+F{,oyƯ=/d&u4B(r epxE=CQ)Sp3 ᫝^R% ; .;Z c$!;Qф%&لף_i]T%"ء9TMHʋydy CyddZJ4膍On5XD^l+?&s$9фG1o{O9hun)t6L81kX({,ojU%pkE*WD+ƺ.V )0288y'\:Gy#l Y.'Rp!W^81,iLUY6 MChPtʷ ' Fu*zynw)01 M/8kp1s)h۫͸٬ӯX\u2>363.~܆ZfrR i%O-.1>t=x *a[g]T}goiD)=EKDj8U?cqkޓFE'HR~ %7̳YWVsly+zڛU+y}p]'J)_VEŽf%XRBќ QT>&nYօDCe4%jJD#s2yhnɍBgULF}2)QMl"a.J:D^'% /\0j/9kFu-y3WA61%|S\LFV@gDMn25tIC>-RO*~v,:בpdK}WAE%MVV|yf> x*Ո@%_KcԪv{6cx$Evs#*vl,amLm#**"a7Q=HF`k]KY?L#֪n}ԢNK1  i1w#K]$U *U լ}U,@Tw Sd(>ES`hB kzK%BW(%vܱ}eٟ?BHgQSxx^B=5s6HG%:FuSu0TU=ܪÛ}E8yiz7D}vܲaI1\7,4*dOvJ_UYXH.߹*FW#p\~}}$g+F S˱f:&^} :?հ-(&,*^agTYLex7l$6`C[><ͼLK|"},Ems#9NfY ҥTKa nk٧58Y:W&G冽=/nwm=-!0 Es}[9,"4-1V+Ǧނ6eBP\KIrCdxT/?d Pϕ9#ي.NMi֠{Hj3Ҧe]lZ{iP˟ pC^K ŐJyZP%gZ}=^JFY╵\di#h2.aQ6CEm͠H !{ ۿfm5TJ.0g'H𓝹[׷MgF2R[ZFQ B|/0XwV /(>HȨH_>v)lD0]hWdymSP8(}NX$/پVI%] 'RaIRak2i_h𩃁Q':c^!27bMKу'6de_IK = O9>e$d.%}E! @Y)+e7)$5 S+SOdl[ٕXR3L1*(?&v*ChGHoZ](؞jsS^?Z )ˆSڝC m1aLGZ+{ rL:>*k\m{ ty :} G0<~'Z9g:wN*}(ɏxX1dGSwc_@)a6$kDۂ9iw{YԧRWFp̈h,0FջyhK&v%mާ`m4~@17lv7nmePsm~U4iռ 3)6JT4զ{##5 n[#sr827S e^MbU;xWJՃ@E-x2`عjԞjefY}㲛6Wu-xݽ ?*\ l &|ʽao֜VX`ʒ } h)F~!vXkRbEmWyGKf$9a-o'@s-VFK VTҽr0m>n3eiqD˪jH#BG:th~ 5]l9̉@靘* {( £2jO]`mhNIrkOP|}F^Oh/YJYT.FRje4qe]_98xxi)) ߞkKHLD"3Tx̥\uCԦ#Lx*UeOO7ڐX<)E"c-۝0Ɲ VuF#v`L= VV  ۘ4>0!C~1G0 ^F(l p!r0gX->܆F1vJ͠fMĔ$mѬ|OZeN6PŠka#XL("ZFɅf_Tw}|`@SSy! 7[ ;80}A,6Mݜ`/GYT>DPQ+YBˀ42V"j ^nj'?Pwc GaQ̸STPoRK*y7תr: I- 8 JZ-C9nhͅ< ' tw} /~FW 3)sŽ#\J:*ZC%n Z5멙e RwjC {j6 #5k =b P^BPix'KXn] Ҩ i D0|oX =a O!Yʡ b@(F'Ai0?|Y EIb6P;ނ7;}zEժzlO LVrA;i>i65o\[UX~ zU9/WA8K=m*.QD¿~-趔qOϯ8tTQK$ 䅆1]Ш2w}bsOj_fd-Ra5sCDHzDlr4Da{^9Ejy\?!KYO`75xȷ*jDvx+5#, ,ñ2(Xkf {Zkt)^:]$yfLOSxH~HX)!-#`##Yjgt'x7E)7ΦN)G;j,G;ᤑ)¦:8 p#{0|q+Etf(x bDI'뱀s-vOZ]qc^>ie Č /~Ӷэs,k?(d ptYX"S=͈Mm&h.YP"Ҫ[ Ոgյ>Uߤzԏa!~߅N\#Po+awO EA+5O+k^N@YFkV("jlG.y_ F 3(ϴw~-ͭT˷R:WƗ5VF[5B%X׺DP`[MOgL4E|//V%u풔/T[ +LFA Qಁ;n5c`zDW|\HȦՎi3`@X^OU.bJЬj~pf H fULEWѹtms~B ly0\{}o |ZI@@x0Y Vc<`ܸ4[[oMb)BWȆT6F̑uDJBŶMxeO5CbC$2m&m1qΡ^4NIi&[3#$^sC1*QA&irlPJ;a[U1IpNOVW+K k:. P*e>쑒!9~o\[(^C8~Ut \BTSlYB.kr_An\bEXWz̖\1t(AHa!`2*\*W4,>6a'VƵ.f5NV+'B,H*Wss]o6T yz杤j M%) ~XyRy]U^x>^~zJyY<;9UqU9Jxy!9\Ki jds&\k翚}_Hn03VЭ̾2ݩRq 8CAqvvS'Co t}E7"-B >~'J+fuOҔ \;խ@B|ڇZ P7Ѧx9X*bm=Zvdԍ9FA"[9֙գC]tCfr1\NB_`}հ&Gùf8N%> [qmVm-5(c|gv2q\\V RM|f^ !Ka5c?4A(KMJ~/R {8Sz7+)|XL~6'7e{"B3[0X5ЀEԠebJtyQ`\Ѝp/ƁAWIĵaO Q=|^z8&u~`S$s!e#4XE7p3|gei[Bo!,nrs FH⍶}ɵb+ϴliړ|X6rggV銙x,v]܄Uc; uϨ݉e/;Vzץ9 zee+LX1VO4(Akfh,dcᲄaM(19#ӡ>#-:vT rBg*Qtxz,V鮒Q㲂W*jીn|@xl#DKt&ѵ%lX&ÆB?|$1O^R!%kQduj-I{l&MmA >x7:zN {[uY)4%U,.lo"N%T7[㿥>yp1²wZ+܌cf,uɄ5lƤ+H |" -ud#ѪK#zԔ>ָRu[xWLHqæ/~&hg&߯RNa3IH=]v 6 .~:-IyvֹM$6[!He؉+`4H; |)x)jHa*.áa8[3KHƱO7 {VV~(!>4$Ѭ́iJ^|=ʀ&^3J/B"ME+LXMt-ZYEvd~O7'=SJǽ.{">o)7D@!Rtұ)9@Axۨ+G`UOaœxނi34(F(iFKx;ʀ$s`AIm;X$bm] D5Ox4e/d/`T=݇jzCaXLB >."< JS&.CϦ_yRt RV璬Kখj1W\y6PunnUq❬~H`[ gPt28)r.4ؓLB=Eyl%|C]~qD=%O#/g$--^!'J8 ;XǙjA>R}t&zK԰{"'d2B!p9rߣ+[lY X ;m*7?~12ε~~ S[}ഴ"]ZWd f[rUpO,[^|=2coGomj߬ЬxpD6@PI\i;f2.%augAV&N}TϓdVr&\ n%¡6?|hߋۇqd0 K4pNm`4aC)@y.V+&R "Edp=G1 ڛںw^7ߗmB.x] Crw LQlF),(b$,k6'ܙh[؈M] aIk"0$1Tf2I l`Y+e8 ]Jm Л]L;jn |kr6T0fP-~'$:}̐DE4BA*lYw3>wI"AuE4PoyPU>Տ]DzCD 9sw0%,d缯C,lȫM$E<9ހ2к!S/Lv$[DXU85d~*t`r+!U2~ Xe?sF9-NhK7.ϱ81{pVSN.;v:%lc8f=q ^Ko`|z@QivTjy'wHՉ@$~<`[ 9i36uwSuwsN$^)ECLpJYox@ԙ:;#Dczdj]Ģ6tsCr~=¯yP?h_E =?dCũp{u7æ4RwDm+WᲔol՛ebVF[WLy\+<<QxXDX((螝d-rpA5- 1IXNEWU o=wA.`j e`06W }kTʰhjEȇiFWn|#d`IlfgbH Yb*4A,'OhYQbxǣՇ% o&v1,ᵗH7~$75yEe0'r2XFpXV_"XVC{W@ɒV _\78FqJ>쬩L[ tE[S SJ3hh2r@6&FBq6BmH6AAFIDA]x :gxeT < 'a/1>;(dFy^0?dhxe@oMFA^9 [$ƽ ``ݥX7U6..jAMMCM坴+Zj]Z:sPUCU-êkaU.Vջ6CۻIzߓU>Z-2*O#c\w.WQߪΠR0y'%} ̊ܒY$9 9aS5Rc +ՙtt\μj*-]a#6F&D76()΢br׾"'hDQEqhQE:ЊMy\/G;307&*뀃h!p,5w eelGE wGv3kBMuٽww;;kUphtkvSʯE޾g/ΧSn2O"?cL'?7ߑ٦{\n6ն0GNe#VNeVEwUI嫿HF8q5Ȫ4NN;_zxe˰øv ){GGȲ#![C|*yy̎^:|8~NY$`rFicmuiÑ+6zL'AU B( S=v~n/Mou_Ic%6FޱETg2EZ_kS髱&_Z`#㡱(<P$V֦.x]uԥ,~%V*~O6UQ*Ӓ l]K:T+ Kf5JqVO؇5v᦭i="0N̻/OxrZcj!bg[+Ť.x1hl$ca1kqGN;!lܴV@Xj?`E=.kk0w ( b^_xaTȲVDUŇIL^_2}_?_hvaFfwKQswɦ |eu`psWokpTv>+;Q&'_T 4Y3, L+.zV\Ԃ 5.b\j7GRLoZyXUhs, !t\ll! s[gcULҞfh(76E}܈lxS#>7r.#lފ-:U/OjH0O Tr ֨X샩FSg2I ڭdwUAU"V=`.qJK>DufE[C\ey_ѫ,+޵>ȓ%y dAY8PCeMfH(Ǫ -8V1wR6cVUr4/Tc>F?٫ʫU=^3<ڪ_|0FdxJMo6.PIt:̠|N)S ^CqONƪ:h 6[ Ѧ*z\K*:G_d3hpBEle)d$yTC#TWv1RUS%|-R2;Z &$fa±s6 )bAE[ ^axEK(jzhNeŨ$1&"i>f=GEXnwGYUnуy0RDpY) 3<>>kE)5_Y^"O&Ƽ'Eˍޓ&zO\~G5"N+ոnlsWƫ8"8q79 Q!!rAU]`$P3@6eёiTHQN0)9`aDdcobp➚Dpr!"u B 8_zIݾ)Ij2H%2S@PXd:88ibЃho|?8>Emu_tRK_15X$ u:vrΖpZtEN#̬{ &.2;2v"/- >T>AQVz hlᚚG+YAA Vp<ϭ9ܦu -H@X&Q!iKf-3OsPྟl<|BGR s(R1OtN\ 7ִFO xU֔#Yl,81G:>Y @v^t '! Vi|.߷"[ ״r_ SOS̱hu²˂dP}ggt~ Lg|T<= ,n~.o*DD _E &(*.$]tYK7y7z;MHuV66āl j GGZ@_ Bz ?,U_/өQ_ ,"t鿬l]RaުJ<6%  #?m'\^.LE84c*} Tr:q&˶5t~VUWuF!fb\ O[=S>89T`6?TSvwad ))1'b΃c޶q-;CB,|lԀ Зw9U,TW/ S 'ωa7$öX*mAZEX ( 4)L̓@7rc$',3aǹ8B_sfe iF(Ư0=C4 B>YRjީV_B/L֥NsgbNrs0b8#yRI}WL:F&eG/_&.,LeB$e"22Q떉$IY& }*G_JXX$k?Șk a f՞@MZ€S[R 'qRo\cXϺf @q!LS/4`|wu*t'`dՇ6'-{HI/c23W(oT /&`|a UKymz2 J˲dlCf8RwU0SʱZnȨ=́7hV!sLq~-?xn++)zŃ򢝠#e!yO=42X3E\uw'0Ge ?f\Oe&&}WemfᦨY8F`1S(o^v> *GvMm1zl \Rҳ,눐Y0Fx~=Ŵz +FH_T>hb"|J+66h.ͷ- +W}5&sGsw<5:vJoORWە D<`sȅ,@ <=ڃ *o֛:VK4[{;qq .+76^8z!LjvZ*6@a'xmsJ|ːטYF>_صW Xc_amDԌqAUstwJ`%LC/XAϚi<*'AVi,$א)!ZH=SKؾcJH9y,zXEX)LfcXrovw{,RLo4 cZ.D;4wp+V$h%IсpoFGz|kF\5b9C^{váȞ"68dn&(!+*h8Q{H X|ɛF6:YT;JF_}_2M&^ۑ 48blqz_/+oO = ڵ!{KFT0 emBʀu C `V0IPAx츹7CߜM `XYУ *'!V#M㌈t(x)iS9:U8.f6'YB5ĕL# hnM`ex5jM>Xjc967 ZQ2Ӈcz{E:T <Jq$l`D*wTȔs& y!e 3 '3W%sV_z?4sFYV#8U&&Tj,,"GD_SmHrqńCoe ,if8Rtc^\pO`!1h^6).E9Far=H#[8ېT~'8ePh%P!7eKSNs}ǨO%АH7JтE Hn?Π.cwjQAAO3Lnf/eL7\6Gi~smT,wp,ߎQ{!|³>t1"-;34 >Fml>[yn{%AOHm?J [BáyXV;;8ʬ=cc0.hb5i8Dq+1dХ @ E9%<7c r:HN'm[ߺkG8^ aEX\8P +LPϣޠߩ[[ U%zffykÃMP1⎬fne7[COâyv եtӉ=o@08N2!_8g!0 mzݠ[o,+8>:'~˩Hf=y@߶frgQm~0os擡y\MwsNDPjтJƛ5n$$1_; |%K+ _E)SUP$Wi[yYך+#n ˠvp=| QT$It4Kf6, &&R( C%eb8VGc\@y:˹-JO:z<-6yXC.z'ٙS^ Ҽ]l|~J9,1 2B )CԶ828"{•i&/̥ ߝ$f?1nluhT`W r^UV 0 G@) (载Xd8F> atp_f1 qC_9fYf9Ijb-̢!+^IZ8KΊM_]ح֬rfջ5aخٯ]_]/Y}ͩ`ȢRcP`TM%YZV`5s+/(7!G y+jk qcWfTr[%lx4_yn ʃo6f>Y'XtTlgiD[/8oUԯÇr0 @S8DMaYM: 7HQp0CWW> [;hWqq J\A&o'=IM;:l`ɠ=\@ʓiYr~W5 hoz=JF 5h:%z Yoݩ_ё2֏gUpĊӧLsøH쨰G*])}=C\ `66;:)nm#Q1sX9[l:_T"2yh1ِuμɞ+=&PM 9FP4fȾxZD'p39u4a^޶mw qmW6ѡSb Uuo&暔R#?HC4㹱w_]Ti[׃ْLUrȒ[ 躀ZO-, ~%5JגP)IڴTTT*hf ]Ra]wކܓ7s/EnA I,o gF^6޿ι⁀,k?> M7f*R6^ں/ړvvP1"g.c'|v<`ߌLc>6@ǭTK5!-`iM5+]T !<ܶ ;t N {fL%܉QD03|:N Ǻ-+ 5pF%ϐ@KŶ'*s d*{%BVG̿%I:DG=wM&hRԖ]:(ṑ(b!( dh]+s^<ɨuIvf^V @Rjy%Z3/J!s^hݮr#_a&A/Ngm)dx\'DJ5 Wa*2dUvGɺNG<8jU 2J*JA\R5fD SN,$-4MWtlsY9|^q6P3I%aglM-jkdhChzm*ȄQ #v.xȐF9Th|pr>S` zyp~A]6ECc74vB݊\pP`³ ]BG-Ovde\(Y?~w˙ q1tO-F.bR"O-5YZ$_ȇh^t=5Uڐ$qkӦ6~D 8dXZ/ߍezCM( fu{Xx0),59Q1Q[psVW,Y\ppè{UHwik44FK1\V(&W+LP:I9XnαWpFF ZS1s t{c߃Nr\1BP~@}0[0BzCB{- Z t9J5P+?}l@j\YڕeW/ɲ=YU?ey~OJL۷U g}ύG¿puahjTBXV؉];e]"^$c]'wu_֊ fzzz{Yqf[ECDʢ%X*5ݶUkr2yN^-̎2j⟚nsg_y$e$R؁*mFjAٲ-{~E2܆#[U|VU;U۩b֠]s|ߖމe2+_z~Bb }}W+R+ZO;![\~GOlpwQCǟՑb~x*<'z &twYo-罫V/HٱlJ;:)1+)]֊kM-FW/RT>0,syLԠ T%HѶDhX7 G&;R[+Šآ t]o@ٌU2E^LU *|MQ|X|5]P1`rboo0`iX!$V (zmk Btxk0Gz[:ph$9-5/l.MHgyͱ礳P4!\N<þ6%2'Q)YDt\KhRzDf̀xJUwk "id1 76_A$|MftNM ,Wd*s 3¶jϲBkɍPO  ̂(\loUROmב9ԩ:Z˭J2BWCZ6_PQ Ul˫ IQjNFV|A>̓ԒUcdJ;3n`…B27%4~FwiYN<%6 Цz@1>SJkNمt7DIc?:uߧxfa; ۜH;OF\ D3WA~r'Q^Zsj%jh'{׶Po ->z6]W,J,Fch06yvf.z|뼈/nSo]8!f)$_Le#j/z;.稄;;JUJ=Ƅ flI&/N< -EQuv[Pш17 w_7楝dm G&L$ȧ1%xJNKx68o5 ҏ/?S]_[<_‘ToI?Q%DL n72OB]HJʧ[`?[s 7j1,7$fl ~rBV@+a8l 9 cZMte|y/ u՞++pc]wuh߉[qk7wޝ~-ŒNn7L Ls̺$*ڔV<Ձ8h{c;MT%QA㨴ݹ(ǷkʫaĶoӅr1_^o7>.nsfoϻ_q{■Ƣ"cVi qBDe膻۶/ *GBf+CWʻZ M"#xvy͆lFWd"{-C ::WhG]beSN+HF @м}lU>@[C_vm~T.RV(O$TeDCؚrv\JoU5#̘$'dX3.<L]4QQd>(6w he VqAU1׎f]$WHMuv4Htԃb_Evڱ5^9wt rc.ժR\8>>]'SqWcp.bb#O#Gă}+lB66Lus}³FZꋦSm7Y`"--*f'Z%gԘO[׵\O۹Vט}߼̫NPGmdxuceVA( vc(V^oHuIQ͉IsM|f Wk1-'4IT1N2a74AN#:y_Nku ?GPA4\K`%9ZW. !AHtp3D W/i j.e"Ep4'%6QiL,jMNsZYȶh[h"+265V:-,xR-a!a2"@_3$̿ BY= G! VH\bO?@-eNY$RsM̤ '[Nao\}6<R #V'i]:K" d҈?xLljhlr| 9#W`:5éhS>KY įyrƈu}hf/] (C 0?y0?y[=z; d)HQ9y p@:OC 0 nq@OIi ( ^'0T; Lnj>KȗDU@$x*3c1z;$"7Q+E6[ryx6io @X˫KNshlkm |zUe]N8#a *a.S"< ?6$Ka1]CyM emo 5!`8 4͗0ˋJn%LXɝUpb[P}':OI(]5n0p/0{pFI}129P^9:9@L&gl؇1SrɾȒI2&~YW *:4*MG+^/Jm*}>C+2ݍ2iFhY$Vc'icf*ْm$aФ\#s*9o__<E Ƅ'_ue CyNGMA"0 Ra9Hs//cWK19D5Mv bΛX]hhW^D6PBf!?Dխ-tHvIyKP8a2d-McpijнnngSZ1\?+Wm{{=/X&iWػdVtl5h/-ȥJ~arxU8?.Srݐ_lqH@uC68nNLm>~F7W`Se%\vkT&;b-LYYO_Ϛוaz!uvkιf јȐI |uhA{P뺢#D2~w Eù4)^n r8@R-P56J8 }lz*/C)nCo=ߞRBM٢iqt-_ahZkYA\QŽG.P O.]>e_ u;ؕ`EaD7qAjզlf>fOp%B;Bo2Kg7hhM&c屾IG槼:Xg煖-]2tiK.21A u>H4o-j}aiEo 9R|*i~%@M~@/>fA`fLDB.;z!}!4$?^b-56Y{GʔK?kj'[f +1wQc |kz,_gz`]HR S < );&%يJ&˳W"\N+'d}i-oZ˖S- r$̵Ѵk gDU1t^zy&g/tfW3vfAYeV#eee2Ϯ+De:,/X>n{]uc?sCWJ"Nэ!l 0W=v雋wbOf1s#*בy|P:1fR~o>(n"NR*]ly1[_XD}Z:EQΏwȹ#g-ۑig9h-Op <%XB3!qXQ\'2Frs%k(i(Q#ک)[f|  mSfmh~g$˦tsnѢBVCj1+Nl's]Aϯ267.va|mnm_LwkE5o`uZăuD\^Fi\(vCLDMCfd b4E8 cilNCǾ i t8bN$'b[Vi31噸s :ěqIï%j {^8o_IN$dlzTx-4 /=2ej­$8%9Ǜޕ Dgl/b/2L~?=5L$TvMzߪъcFF |U V_xQ)w-RLUmr\qVMr?NP7/emWCDb:ni:b@/ = MdbaJϩ;/l>.~h0J}h,h!$_QJC`JU Yxe,uQ5o#amNf-Դ@V>SMx|骧pl 5]Ѧx o ՗ ez4}זVL_.';2szv/aリY<\Vt쾘D,y>9Z`3p,3a/K*l]cꅱxVMe ! {.Ί玛 $B7"7|0?o"[]L[Pj i}VJ4R%J1*5h-nd' ijARpL.C+1eib<tb1]pڛ2<zlnDmء|8%QU\EX:oĭFF~H4u Fem8?q]\ }J"NK1fL&|ze )ʜ"nxb]zˀ;^Xv^"$%`HI u| U~ zĩ Kad1m]gLF#oI*BȤƆR )(Ln%:Tգ [wW r.f Nr΂v0r_aUyyb6EY(V#@ ^E4}f0^fzٕK!$) EvNl4{ dHCKx>omwx:q;r^5Û&F=pljd[{ߛ읓l}k\yʉO3Gp.;-tG[v>=bU6$is*ˏ \ņ(*1Pv{g[+% _nC/y"D1΀l50};D+mx@K7ya}L {Paݓ>@/1(DR^T+y7(`IςL_Txxc\~ ]qېWY%J žUP|v[y 8wPxb;;iwtq߲yw57«HHSăL>-3L RY y(%YX-ꃍmV'WQbN?//POezzԽ~Xkח7ByT/1'>;n07Zp?JR{' ~aLQA4ټA \opm`W1 ߀EfjxUZHKr}4HhO]8pŝX Ij S5E, &z.BGޏQ1bpdoPsy ʼncׁ]j[$`@@^39%uA2ؙEc8AF/I,&;}-&h_.fP'1  AhFj,!Ed,=滝>v@tiڗ[Ӊߊ֏;ё&? kVoGO0ꁴ)tÊʤ)~eׁ'"42ЈZ-BE2n};"xr+kC3\hSs65׉GOI1O]V?|B1 2@H [;px£/chY{Ι?։"ߛS[H2  *N s"l!Fp/IސI%%0Ÿ(L,)[[> o1M1w26k|NLUnA73M,e K1]N mo"LD͟Hrr\N$UAMb9h0P"cNQ|4#-VH88W!֎Mc}lv`JZa U2Dl@93=e5q* HO^Vi-cśFDI2)lyպ֔.FG̍pZz7S#JOc:W9eZj%&0Lcjylj|[lvf@8=x=Ku-Me+VKzIx´f4f CۘU6؋]+ Ɋ8&`CЅ FHt/q}e(~L);#XLW`I|sT~W-`*az[ATYyC.7i"yAB!ud!"DzOrY^Si4Y)Y݄ V,c3uP'Ym#}i:-|N-Nܯ2Ǩq$ vzF/*(+l[*annΜ3" &f~8~ba`JDQ Z`>pe,fG§4bQ&zPaw!|p х%ly+&@4l9"t fMV{5I)3Cnvq~P&qtz9Dxk C)~Ro(dW #]TZg"YkR"ͫ`*=|cbs9t8\x <彻 @dD_x7@RXT˿H~7QE~ 񴋲qO~OvbBro,0rwtyr/k<獆&^F$JO20xs33)3 !Ϩ+kfCah J@:E:pGHavTIljP};70hl 8m~v;Ňiy+g4n5 :+L, зlt ڹ*5^/4N>I%N)p *+)(0gn@rޘJqÉ.M ߙ4>$w܆VRW YJx+*b_su_Mj~?gF~g| ^\M(O'ps}1ьb!UlaUw(dӑA6j;X& w_H;˶t=sںk%d[d^,`p6\2sIqT$#{ ֗uT?5v|=^5"PlfaZ"]&!(9cљUdezwLDpX*#LEehZ-GA0 y/ԯE ggEw:P$o74BU~P9 VidmE29t(^( (Mf׫v Wf [( nG ,2ڰ!m7,! ZlK3Y 5ƾ霟u"w6yq$ORmb-]= VQUt`iQXWGWU^1dJb0`څ62lK dR2(M o4T˟g Y.RzNťaI+_fA E9LNm@ȭ u|: H/(#XN$h%\VL3P0Bc\|Q0l7-XXK㭂̴dJXb|E%X}d:o59 &Tf()\m֜<:lVՖ%TvDu%\U1@E @X!iNo A& SC*j`w2*;!e +K1Iy [[WmڷzNyFfEiprk;tMN S@jR zX`$Qc mU&q$9PǺCMHъH!ǀzPB=ԹZ(kF2Ma.;!-Yy d2R &$4(٩hKF+'BD],1;2K)^pR44"n9*6Tsbc I}kTTbgO}xq=| ]/%}`a[{*7 th0 ߨ3ydeX"&rmx(g'l"D1FX@GpӼw"wE7/4^q$el[ #Uš%Am"dO6^Z:f;[w2„ \|Csqi JT6_Ra;{4VŠW Ϣi0µD}+o+\pP~%GL(gvUɾ2q9{ɆHnXhQ΢u(^Y<(P1r{8]Gip(6團s}rvG[$H}G$:~jLb _m*F>H v~BU˂Ѫ,m6mokvEvW$y$$G͓MFK\|<1B~d|bdzeJܸ ayfX1Q/8}Tꦚ/)X@b`J-V[&iRn_Al.F9nܬOFz-z١:+ =hNRX0 no祈n>iٔئ5l<:LdEs>]Nc{"A=f,t_<-2QRO2"y1PI=?DJOW6:jA JޛE4(M ȖU4z>؅BfHmTŀb&YZS-gB$gL_5~kAۧTʘ RgeF nk&[ P WY JR~:h&xI%M*c\Xlfg îZDy5Z& ";N]u)bUx/D Q" V[`*}p&X1,u%4ԁȎFSXT4@Xل:ѷꆊ$i C2OOߡ!;plK34/>b&-CMFbT6~=To{7)GruxQMail-DMARC-1.20240314/share/html/js/i18n000755000765000024 014574361234 16323 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-ar.js.gz000555000765000024 364714574361234 22235 0ustar00mattstaff000000000000 Sgrid.locale-ar.jsXNWm$a2 cC~Ti\H5Jj{9ER!@oQ!d!FFIIJ"р$>vv0MiHnRlJos0a~=w_oQU{]*p`d;)˚,C|>eNdmkC9R/e}ޏO],^Ed>ǂ,lrꕈ2^u&5®8ӟq K[Wi9N)CrD?T /E4g $Am_[-˚ 94p,ENK%ۈ#v0ꊻ !`3CP] بTs*THV#g̺ EvOt!izClx5a|SA 0BTWO|ful7h`"_G6gB>-xmɼO: v&ğk{"iVP4t$;vv1X[R飋HTY By؀x:{d" odLvRƸSH@ӡʣ&PI @:qNa4F~BH)a% :hK@mt@KQ\˙$OOWO2Y18ĜΛM=0:UY Y,d9i'916[3.d.6ߧZŜ>N8]nh]sZ sL23/T O ]!Aa *FJ""ʑݳVʂ;@Z_6ږW.҈g8x U*RU}eHzb툾yOH9"y".=y#כΦ߯R UrKC*%ǎ:GcJ[e<]AxW*pw] Ee19(\_^QC/Q/ O `\$ǤlF]p1lQH0Z> B͢X-c#$-fڮteڌ'؟9I3U 1mČ8giaˢN!bLaEPTeH+]Y^|E9 h3lX(| K^THqL@Ӌ7v:m1 M{#I&;)U3@ԽS`!=o:Mn 1U=cy!gR@LvﳊP¨;,cTD177 g/2 ]# T $)@qaN<"} 8o8 FޫMiҔcW]o?SQuYV-JmȶJ4r3tx~ -1B? <Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-bg.js.gz000555000765000024 431014574361234 22207 0ustar00mattstaff000000000000 Sgrid.locale-bg.jsX[o~~ŘMJIHAH(NȗZrA\y/^tB@@ 4"@Z"0zΙ.uqQTٙo9s.ߜz왑{MU''$xE`[w0R`xc !`voV]\nFQVffuoTi|l8̱MbgEMn;ZFVo&AaU׎Cljxqh9f:cձ* ۽.V#! ^t8q&zqћ Z${H,R@Cď~WaV*i m@̦4Z7y<HKe{مj(}r8NE;}[Fd.T*32; vY{<~,N@޷#('/)y D̀߉8!ge6Fef`J*ˊox 1Mga^ v0؀衻f}i~GlR <;oZǛY樭c ,΃RfK2X\duյRMswj+ګYYw@# &^ra-ّÙ L~)X@4P#JSq(K+J @Ucx5,보 ] C 3Cy#ey1/uKE1Lu33Mf}H&Bc+:<03@X0vv!r!F "ˁIȧR2R '*.Gm[Gy7lH7B(v-ķX}n萔0T #d&PbIXK=aE0`& krk{4Sy 4C9x)#3dQ`l4g^-Mɠ.sbC?U)]ۋx>챧`IuR\.周fhSQ }p섌&5!;ۇ{ M83qmL2eJFi&H{C($]τ~3#5ء$ANS/;Tzn6pT.4|sy娢³AOQ7 hK۴v-6f+9I s^G r8"$CF"WAYhR;>%T/436HbC>Ҹ3 yٲ "q@ mtrೖpyW";М05zыုe䦴7p&:̱nin+V\9 yiÌ{EK ަDda"rXr&]|;[5XʋA;ls>Qsq -,=GEגә2;dњK>hZOl!ͼNoОgMHzFG5z INj)g4T~F`?7^j*;G(9/*N['X0]t;G E6RΡyYK=:s!sV \X1ߘU7n[]d8@QxoYkeVJRXBm#jV`Slf}fI/)i ayʝJS[iV4]cTW~jJy5Z ֪jf-\{73 רI>}ͮ*jyZW+45/{_Q]V(IM;evYenŎKK[f 6&XϪS>t:#eFdR<{!P%x鎻 ]ه@Q@ց?1!_/Q¦5g TDF5u(>1'k%*2֞?<؁iM/Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-bg1251.js.gz000555000765000024 305214574361234 22522 0ustar00mattstaff000000000000 Sgrid.locale-bg1251.jsXn6m?㵐Rk 貥 M8hJKQq ׏6 ;-ɔ(w$u'4bg˶spFh-9 |)fcXDl xIp vP'BuFA;yPx(aHLzxAm> Ďd0'l2+i,)Y`OD3{ўW\< ¼--q Z<\>Z Z:=@p& NI5&߀ RጸY?mЙ/(&hiV D;♜*<#&^T"" &,thV oz(C1R2QwefZNA/Sl(3kx!tp4=zT45b%xy]h fzMR3"FAtnYٰ6rYVV4Gr|ג,g;u$]2Q9ѺWI<*tbbe;7>Zl*˩KmQLvf~9$z$G!*~*&G>'_ʉ70 8Tl, G ϞwoR;RXɛ ~]̒'$t'HtD T54Cde25U< #%;K姫1t-}rOb٩N.-128epg/S~K&S}b(*-iv7Ebɯ䭰hKmxNlmӊNgpyJj_Psf4mVY(K L7ܪ,w nj0 ?-:;lAE|7 8*ܚZ63 a v UݵF%1'qFX$4/py~4 ׵^]-d&m?%4&b ݨ6 ddI>r 0k.|)[}PWT?4s4d7 D$U/"j:\![b%1f^|Mfcq:HP,dZ4aR*zĥ!6j[ORxm>6܄sſbe Kd,߳6r<0h%!BwbaziͰTyM26jvr oݧN岬.H 1'4; ?G(enQwE  ;H8Cu9]&XLlmvSGG9::+MV]U {oa߳1ݞ:ELdb;w?<7øՌٶϻ˗?]DtR+[t=Ш<{smA*07"MpCtV)yԶTKҍpt( 9Gҁ71T -˂-R2Z@PӉ=~Z} &m$F8&}$V:$TzZi=;@ԓ d,P۫NK_?DJoQMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-cat.js.gz000555000765000024 366614574361234 22403 0ustar00mattstaff000000000000 Sgrid.locale-cat.jsW[o~.dE]M[#g jhDC*$G[?@z5S xD 8/ǵν4zۃǽ+ x%PBõ)(Ҷ\>Q3Z8Jj{e~www7nn*B9jԺ ~p.Yv-efFdSƖ֫?Jl2ے*u /g;;<}۝|@x\${~wZE>Юe17CvnWxj#$9tF-eD#n7f%sȋK)F"k-P|ŌɪQZꢡE![[-HbMA#Ple-: K㒥mKB9cw˕piQxLf~MnћP " r^r6ظkۆ+Xntu]% Tj1Dǔ5rF|bʚId g4wjxsb-I-BWTp098c[NץLH^FB՘j S%x,"k5T2v87KMhXM-l9PXqHk&s< EQx QsB7z.BR Ǩr'/ o/Lܝއc&V2#fŢOjfd)2@jDjoMQԢ)(QSs&zĔdɍJN9krb%N_C"t8HO_K: l&F-jn##<ɝVmD3AySѠ!ynbjU4JlCpO yŒMs7e!K]~CZĢK??cmq#JvOK2;ϿOI?I 6UCI^+=b<֐,{)R5)'~ m<-pp ذ$*7)yOa[ L R|,Υzd[a_,9?6 d)BHz="ܨ^Q*k~'cLN6nA鞫DlGr fb?צΆnyH 4\ ; 59݀<7œhGMwMp 3&LAPr9(#5Iy*sG q&,ISҝ7vnhh/¤`X :| x^]_)A:/Yh!c^g_98Ohp!*VpÄVvRpvӇgg3F˚[*԰}bZOXq5ߒLI1|iK%Zr jƑsaŋA!# q$y0<FhR=cAd1x$K2YaUEDEcpN,ڟcvS3w ?pv1?mxI͹.u)}wp?|$ r6DG)"r;N۹c/FROMsvx?8>3g ԗ}㬁c}Bm$^qtoe݉:l^ӝה (l9O FRB[ƊwHbp͑p^SXG/7>Ѐ ޳oр{=OĘ?b݈Τw9 8ڼiO'E%fB?}tұEjYpsɢM|oq{ LW Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-cn.js.gz000555000765000024 514714574361234 22230 0ustar00mattstaff000000000000 Sgrid.locale-cn.jskoV3w&Aq&Va_@J(A+}gȣi%KRBi P£[%Ж1$ {ν@ڭ|XKL{׽l/Lq41&FXJ5FmJNY2u]XȘ&+TSɐ5=b9H5 +Dl"NޣTA߱l# ,ЬiM24VʕU襊-;޶nvoL':zfҢMD,cQŴT$55A:xnb=M}Cƻ'Huq9!D`[ffySw\}QUF3&X:zquvzEme##y]fCAA. }LTʖRn2I+(B?ј{ՙ-TeGΓqh|:C?oz"1Z^xS_mO ԇs1!t8ANp4 }, : w/0".mJ+jNSRɪzWO7O=e 5j[7w^|C*z퍕0 B&n!Sί YTͧ+P,k{/v&Cd0(@@{so@4D\Cc|P'f8uIBM(mpЧEډ oᱷ5Bc-$n$ u&;$Zz>k?niO2M7 #kZ*&Յyk_l?yu!8^he;/켜| #+혆lY\zarm^lš)/7 Tl"i.Ǔ/Fan*[0^ƽ kwrFHX=c bVM`Y a8X&I*NE|ZQ˪7!qS%^Yz <:Xu:.e?EK{Xx5 \OAbzcLťՆZMfwO1[M'Hmy b~1B-˴'YXYGsw:[eL0RJhlX/x+k50&svo6>zѴ 6j/M׆nӊ qEe'i_~*U4Cĩ,r>]VBb]Mײ(S~Ykm",D;*@h&q (qE|8ǿV#Z0T]Xو!XƇ>@ Ab6&sʾY F}"_ߐW´/,x[@d:M>}=[ f/4b*:}D@J6 *7;_.K?Ok>A-#Cw$# $iOTir@씳0y9A# 9qtABmKi3F'jgDz8۔ r%˨#2se]L U>f/>4߱${ TyZcx">*N+p^$B+VHWҜ[,7Y>6T~m18 S_c[\ĩ2GKd2 ]U؋Pɱ( Pҵ@rAY 0:A 6k uU+)3.s,ڭ̱*0t~~U866h+wd|$0Mq0C0HA-MGVu"E9̐ [Ct*+e&0=zsQ R12װK7lTp: \7)kp6l#,5aq~bҐtFa*#>JH*JRDM}6 q{ϸMJϵ`wd/mBM L 7 fwD'Cz!O,߫xOwwB[A3Cb(cʁz5bٳ)6fީztB\cS'nP5jNgByl׽1vG0B1KJXIO]RF޶$s^$`#xEU8 ^0QI{N~1EoN~hA@90tZM 7>5ɯ~FPViez+Z14H/@ V oI}>r4۞"%Ruq&  `iiOP~Fzq^wd^s_h6nw,i*^:iVSm ks6Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-cs.js.gz000555000765000024 400014574361234 22220 0ustar00mattstaff000000000000 Sgrid.locale-cs.jsWn>SOQ삔@.fen"K);%#iCt{z(ZAr>y2{!@hi>nrH7w;؃ởU)pbF1ϑb#HiaL 4Q  +La8׾r<NJa+Iۏe DbZ-CLDBF3~J꽝 g /ߚѯ'|\<!~Af%85ciD*N7)9қ5KK1Kbb^|&%l 鯄? )}E}XNE1 < V!q QY U"_IAפ !*^2y+DF޺H+g)FUx0RN=x1Y^"$#Uq aɭj,9Sh yQ0UKе,ιF çt8_03" B< x >$KdDcRaXf*/c I''1ir}##pb䉕tɩyGS ճ[sDsgQG\(V'/,X H<1s%S҄1٢((PG 7!F $GV*JB̡?: |',"+DEB> wz܆K-W+)Y!sxWT^t kQu "ծ%$H`D`E#Q  dcƘLf2K(q#Xsqb1t|ӟff+51.el) ?FJ$0/k3ܤ{Z*Z$vFY9!>k6]7aSVNC٢(>']aJnTSJe8XMUb@!$$[at|e}XӮic8cy>etm9S6D?E0xЖ\k[G4xd$>oƻgS&BK/)N*&Xw0Ǫبj5kr[>Wx(yݑpńӀ/E:RROB\ #Mi,Xc^/@aI nKq OY]iȏ{R% }Jdaͺnuw/ ;+mB4> kseq`$7|&492cZpQPC5vBڔ:ja]Ԭ% >muc=N>pY Zb.81 e$W {(- T~T/7[ȗEYvʹ{QXǒ3}_s&8/ʲ+DMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-da.js.gz000555000765000024 343614574361234 22213 0ustar00mattstaff000000000000 Sgrid.locale-da.jsn7Y flÖ>, iZN.шTHlsHEܾ``s?wRNhuSwpteGl;J)1\Yɉ`ᖝFt:}0lNK&E BJan U>|v#I/KB_|P N⥿.vrUn ٟBv7NɞTvON^Jgِc &sH7d²kvž~Z3=aO?n[jU T3t~B1pDّW~}XFHd I5X&H_.DG)8*C(}*5X N 3=1[y)d}\!Ԉ6v& mCm5y#w%"]^㇚l ^YB9쐉l&eK"$X{cCАHض"݃DeEURm!ɍ.W `^%%\ʄ+5jZ!C: KF8 RifABXnOFijP<>(Pe 4 ) eڸr>)uIBG[ U4u3~U V >5~/^s| g0|3P0_-PgI>ptY=IεqfUQؼv~@6LA:R6߳%Z.%3c7! 0K.ÉC;Lg f ;um j"^Uv|%ѱi 9b>% DcHϾGUA/΢ӆa4p(V9`#o*% 41~sy^Tii.1|_Y5ʙRf_1O8I:؂N9 / 43Bo:P|KJ?bjUM@h)LB 32=0cSU1/hf[k7G Ƿ|o|^K0l\0v}UF&4]4Q`~+V|ȇe 78pU=|v%7؟P&yyS?Ml(ZZ=꣔1HfR|VJ6R+8<Ղ*=:{KƠfak]Zǚ (t6{3<)oyQ`pHFjUp vF7iQ&dj踬OaKix.Γ<՜Rۨ#-qtѲ j1Jx^H1wF ͒nϕ휲 [OlT@]QN<(:0t 4ML:qPm9]pSGz\c sGK( PGlT u;|〲'jk0 KrVh،!{/ٱ& |{Rd0 gbp Hx?]ۓ?!-ڗqtB|xE췡BѢMD]; l$%윽 9uHnDɈ}ggJ3B:ל(`_J!7KhY㽾J,X,WC\vnqfyOb@E?api6#Jr!+-f={fd Bá*H GxmD)b) 'М#..z$JᥚP!k[T&cUa{ (E̝vt4JEY yϐMHs\qD]SvFI-1?y1HuR h~,m[v3~9@撋Bt1f -rP̣sw2UB $'q'DQ2$*+b^EgBe&!\HA'撋tZ#S(AU4Na^r *&d3kCKexc&.RgA/!hbqMФ-Ay/'_^x94HZCu\HՂ{R [ Xo972ST}>=(6B)dj)rfre U,*LJ8}:N)Key_5P2ԅH'U( H}Q'Lq^Lc?5|CΟb1~]lUXdd$Rs+NqUPC '%{0?Pqѱ๾FJ_ ~^̙ 6.!/FG M'c* E\~% cR'E~s+ye-L+HIQBYln \H6>t eK&akѣK<b ķcOYU>ډL̛H =15kzTnpqئX;15);aܛaIe"ƈs& uEL"lWzȅP^t~8K)먜?a?)G$l6>]Vܣ_e*q4gLQ}wez.)m7LBjlV6z&̏wp1x0x-NxI´Inu`X@7t=$R5A|A-Nu8 fa]`&[8 d+PޙĠ¯hʊq`Kk**TԜ&"vN')ӣ7/+arSzU8bM;1kKj.a&cEusY'rjlA:M'S%:(:Zfo堨,ժH/$[͍܍&L1P3F79tE. hmIE")#VyҺ }SxOxJWqcޒr'ۆwش̓O[]R B\!ۆR?DN[? YhG+&#ߜ?WQVy8?ÒNM gFMxܝ\ @EᯒA DΟ $qzLb p<ׅkåAjhFǴ8w:PPe%BVR@ehӮ+R( qU:-|2bln<:S?=hC*+d=I|Qn \8bD H+ ۟YX쎜V'^]ZZES6 ,̀?^OGn<33?#'X\BQw||8? 4SciG'lBZexMG.E]YnU˨fb=SF,,H=?͘JlNs`cs I$Hreyʸ750˳m-]ժH֥Iy_|PpGq_L[R*7B݃A,N'vSm2]' %J#5R{"':M.Yν2jt;kXL: n-6?z\i!ƩPRSd Yu#`XͰxIaRf)`-]͖6op@gy#7m&fR1^ը1υnin@ܵb ,Ұ\e<;<@6,m`VY.ϕDXUn8ZʍTۧ?bRFmd0mO( 2aŻBVS됪YjNr7X|s%O|0JP2y dc1LXZY]lWUcנM0rIVn[7R[NE[=%?Փb>a'LL<{#Dĝ1(V=){$xP9Tؼ>' 0<&\bPO%AbScBq]2)2ɶD!0Y>}2;FMk=}69 ;Y)aV9qnM Z838,D,{1/i/#2U 50zK`d}[yY~S;eEc򌿈NN)#hH-#`87 /^c?H0(lĒC_Vᗒo8|m%>/V[Nh8]mz|["te {?X^(a{j8}p…V3Gٸ"D QDW?:>ҦѽܯS>f }&fT#=1ޅϹ^焧嘆 PKTl4 mh{}8O-d4I Ѱ6{}xCwٱ& W|sTe0&gbp jIWtۯ |?swA:s ']|WgZ_bhn4E6Nb6Pm| YNAEa\zOitGv.gs#fc[S] %X,/^l'֋i[ ŸÃ¤/f| 5@/ |Ĭ05<9fr&gX%,y )W+2SDÎ]p[w7auod5Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-el.js.gz000555000765000024 436214574361234 22226 0ustar00mattstaff000000000000 Sgrid.locale-el.jsXmkG彄+8\og#c4yvf33H ؁@LHp!lI?KTuh$Lw=U]]]tL|;v򽩭tV?8t8T6M*oy0 :kf^ nYyH9RVtdXL5K.^hzq3wݒV[]ގ;Ӎ-Y>g PI3?5QqԊ√Q%Tv:1pM~uښ m)X GuG^ (h xYK~Nnez'ٯXիWx~u RVhwmuq;y& c K35X˾>lP2DXդ-͚6M kL䫡b*x(T%jDBAxB+Be*, UY8ZѺpoBe݈0*-ˆX> Nxx\f0;c {)`4b`EToў7r~yp$E+ z+]buAWU-ϫR:]PoBVaS^ pݍ=iE|k&Rc2 /0t]&!'PGR4S)S-qV,9ˣeJL^%/0ؖ 6j7PC/;z.V]OnX,V%]s^)}eAH#kMҬVdU>mo)-aFчMVCATBWzцDa'D]rd@׈ÀGV'͌Ό2>⻫ͼMA+o,Sq}9vX1zx.a瘏[ pqx һ4?,r%Tr% 0@akz795bIGy/sXcAN!C;`ҝqTz!i5ýz:] Պ1ƌ,GSڐu}aA3u*q'Ζ1PA CkSX5=dF6bݚˊ=>:.HI]u Skm-5r0.O`^HrR.CR)p L ysJJόa8#~fZ'MTҘY7􄏠3e|/1+#ey*WM鋬c2F>L,g3KĖuIC0Z c$!9-d7v2x9"8 Qj@_(0X79dsh^ 8-5(@n/Sjxߑ* 0.0%6~T u2aѸ1b(Jro :PפGX}ȿ93>3xa`GYq5(;`23QFplrOxaNJ%!5bj/ZP] ^ /\[XQ۱\Z~ճl|t'Ϛ5^*ҕP Wn87%Md"OTr€uzW~#o\ּ(, rfr-K[f 3"{ȕCe>EѼt40?nf9K~^ڷ9tޏ UdGۏ(Po]'èbDgy#ES<6gK(0b?rUfF?rpFUǓ1?㡁 ͼ?\-Wٳ4eYCZ%[]G>9J347E4y֫ D S7g:3NM7j}84:zk\3?hL}z)^fn5tYF>`瓅+o56b]-062DdKo6n{ti:k\w;#8jW4__9om X)8.q ,쩂ScuwT.ߡ{w-lwL*?#abyf[>^э_t7Ӥ&遮}}̰ [0N3{J̺s'pMor=nn%UMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-en.js.gz000555000765000024 450114574361234 22223 0ustar00mattstaff000000000000 Sgrid.locale-en.jsYS dIqzBr .d YKkY ] q=+[zi2-T۝mzNiLoTfF'Z( bIWTbR@Q3xV~6NIV$!1ei$1U*ʙ68:v}}-8.*nF |6Gj=gYgϟv/_^+޽@ޔRm޹ފTTYiOmi:.קT^m)|^.⇂֣+Y5C,St] '|H$``{F ͼN<5q?F,'E,Jѧ[u%?:d5^kujK*J%84!r B NVĉ \/&2I봜5!.!żSC5l6Aa ,W: 64hh -*T)p &Ī5Op8"]TAh|8<>"r;:d R ,>ILr F^rɨ$#ׅ\ %*C_i_e☎m  ɸp؅;|-T$3V.aVi/=2J C3aJRQ4*3+( 0ݛ{$0*2#eVY-Ux6eRATq\ ^*k~p+I@u^x\/O&n$|Ƒ\lH`+#wܡP2JV#@`3Zvi4Yڏ-6Z| 1-PSD#=QUA˲itG`"ZuR݌̊ L,4IɜHqaf< #ݹ|nHlgkz8XfR̗m^"6(7Seg%W:W jQ*,Ayt. q&gz̍x W͚Dqۿ/tSxi>$ǒGI]Ch/=:-|c6ٽbvgرʕ纣/Mj]z~L9S2W WHz@QcC\ꏋ…bp(URl~+4-t.JN`W" Z˹@s*>u ZwV=[\0QlEtk>΢2KiS@wˈ*-6TS^n)V߆b&ly*P~N"B-a7ySs#+OW 5~ rܜ*]ɯҼf\ 4y [9Ug0ngw2 ]sP\@F[ ur{i.Dz) /K*kV^ᗐ<`^ur8,9<_!~CW}C\k~ zEg-wK[G? E9b`h/wЋO(/ml||lWc^1c?_v;i\bk+ Q dKd,*H A,~O9 ߸Ls%¾]O߅cԡMzTH0WK8IVL0q"aHhigjSбEͼP1TMo_!o)a!xN\,d*'BLsG㷫emYsTt:4sx\=ٗ~S2cߍa~`c~}΃dzB!|jѯ\4B3 V}y:MH]j[X'>h26*,0#tO(7"ss~>]RՒ /2H>6c^ϓʌ }%SZxkoj̀qRddՉ(9 oR1XIKv|+d'(bۤ~$D:SKж8Yi9iC,y5R#`2YlIxx&%kM DIDrr/Q6c#] "9@.l-8 1])}e u(GQMbjdyܾn1gy]ԴimoAk BVb-VTU.9P2d.b{'w(i;ILÀ|/rdF(jQ"M*|@TU7#t$`^¦W4JIޕ~'Z{(i"4zCЇ8 gA*)~ "$(<3:3autϾbWl2Y(WZ5?+!=o4֟NB Շ`>i*rb*GV'ηVd!{ۗl8Ϟ];KmSJf8*(Z\ f,*jvՖwoO=rnwechA_8^i.µr iP&{ jʎqЁ;">]e 4|`c-ro~TNM>Eϳ"eL$ߒ DHggXX m[PS%6uF;\כjt悜79w:i!,06P}Y.sߤxtBņ\ B74`8npݒRn%U.-B ;zS4 xe]+oEu28uq7J nllQH}7 I6ri+đ7UάGbJ7ԒΰM J ӡ$$$`uېzkZ f$tqXcRC߇DPiL[$]@OPޑT ÚD7PPn~.VmBRڱ\О!]cxx}yUML0dSиm$vf*b-D`kwy8N:+FI1rN7WMDy>D#,5?O*~waoFfEKl0!da Bx{pZI)Ztc9gd!'`\?I?gf?*il\kd_!U5Bŗ<@ . S0'nTXW(]Cu\,_\u[{ɏS<.N0PN۷;􈫷h?h{;?c::jp}*Iq޾G}K{]~T3E$6^/!nb*l#-|˙Կ˷777^7Ս{b)GG+ ݽA+j%pblO; $.Sڥgc|0R+8*_ǟr`hR٤}!$Q.򧿹wZarh"_a =_'a,ȳ%((|E$OOk@8yw3SNſķ|SRP X%^0|Rt퇝mxvl̷d;_KaInm)jK({w'$DaEuKZc텗x+JՈyg!9'ݚo턠573Z Wa70q,aIs='Q54_; A&2`0 JE]!qd %a2ǵ=}&e Yp:II.iIekX$Y, VyM'+VJ"@Mkt+~w\n eM{1K{z 8N9γ< f4=5 :CDW /RenZnyXI*ta4Lm/?rx2cccc'@[}Ww=-K)Ԉ 'RglES^0LkqWihh+(re  d#G=Bmp?P "gx~!Y.H i|ॄ x oNBEw?;c'] -8lYF,š %1NWཪ(;8ÜMMylF>OV P%SƮ|{BX 0`(밋v`BH>k][Bti^WQ̼V'z*9xuWH@׬ЃU=U3No5mZ*K0;7G }rͷe ϗgp(Fp;P3&BiV2ѥ'L!yu 僽'|r#>Dhp?T[?+}ܱ;%DpQ^ZlEfl~{m tM/'`t"))~pg9PJ쁺ia 5Qc(AX. )F UV9VBv'x~Yؔa) VM~F0u ~è8 B"{j=h5&Wnr!,` 9jc=D˙F.BKH tF*lא#tR٤%ĒM bjnP~]akP"3mM7 G W5)^CI<}Ϟ96 f%-f3|o8W>}'`xyz1Oyakۏ5{^B+w~h g^MOz+/D IJ+9*_Ī契!12~ӟ]oVsP&ioYD x۲%?"nEL?S0WEٓ'o%'8݂2a6 'Mɢdo+o+0#Ox_y~ Vgfd%1~#oV:5Ugl~}X5't l֞5r |dT3e OK>_[[P)kj{뛕?qn4bwkfs*!th)iI&KD#W|V=`F>, yWM{5mX$la 0eC| FnӔՑ17 ȅZ.C-cB?<ީa>٭kGu]CyAn./kP:ݭ0;pE7M,Fr? 驗؞^/%DMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-fi.js.gz000555000765000024 367714574361234 22234 0ustar00mattstaff000000000000 Sgrid.locale-fi.jsXn9-~vfddeYyv2Fkb^[ VSlCk mj G]EȫZpL1"8B)a'00LY04eN]g'㓿Pg?36:Kؼ_ĹFt)\YW 7&. \_lۯ+y||ڛ!h3n&q||Sl:~er註Cg:?8WAdq 1/5 j8y#fd ǰ- fq?n^QJLwXD75f귌;T)X82n]2Dޒ33N!ɮTC̎$+ )WquHLſij\K ВxU\p{#\'q.0^#ֆ1~_c%dXA1Yo}sKU:o,i-B1xv57uM+Fڹ\؆#KG`j_|fABgΛQ)i*lCd_ݬH 'Awc#%%:((f=CQ ZIϛFY<nU,I^Vu[;ƪA xſ3 |)n^__25ӇR[N])/hy(@p 2g0`Rb^QgN ʑA6^=*SfǫgW/ TK)W~gxJ1HE(אPa 3TKf Sz`] {ک;}g1A[v\[pSҧ<y.h>"Y M?UrⰃC=e8-[!f0TSaV.nB O 301J[3f S"gL^eR~p^rN;fh8ô,%w'kl=DLQY̠RpSӟ.󓈶Uywww}=#L}mAҽl:dUNϊԕ`{mxWx\3l' zNQ1j9Br :%*y|v2"13P3Z:J7LhjBTܓ;GF%"Gث|H`ЋS1G% d%;*j<8@b$tRF΄C~z$3n)EEdDyVkR1ZbeVtVf%A㻆ҙ"Īj( -!Zjhd+xA1~ fI4#U+)'_@,fD]iAUE]A5vOjv6̿0W$p@teX"p)*(Ttjǥtr ZB ,:8C7jO̤u#}S:rE-bƣjL]$Wf 4)XB[$12/)+R':@:*_>+hwAr9dtU{Sl*m}Arz\\8MrLyHH+_(ʱ2\ -fEEi_Mm"5.m/c)$ǿ-7n 0S*sklVfS$(l<,&N7̬.hbciHEcJ`1JjOTa5TWֈ%ӥ0F<0Gn *Rqt#9s# NApb\ .vBj[?ڌBc)%2h9)!lgv.Dykc}޾\zQTsIj63\+ d%7$髷-Ȧ=l| AJ̣AvBs9nkMT"YZZ|{5c]%J~OiݓץXx="aA6*k< >R4%8#t+cOXɢz_yD8,Ű}3"YSBE_YDvq4mnS8SmLjrLJq7뺢'u))r(_ -{Lp컯&ڔqB櫛RA+!:HMwQ'sfR-_U۹ _+!GMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-gl.js.gz000555000765000024 372414574361234 22231 0ustar00mattstaff000000000000 Sgrid.locale-gl.jsXovz'c'w@Qxw%ٽ-8 Y4H/)9qݫΐl9"|Q^mJRvz[ X F( 1b]Wa J᧙6cwH\Ȭg;.E$%R%h#HiE}fŤ=<}@lV+(Bh2kCtkK?=x:CMfeh!_!cr !)'|G]GTh:]JܧTæMYv^N&1'J̓}I]]$!)a]MRb .@XjQ*5JĪ ¹/5?qB* ̺n^x3fz.$9i{W@J }5_aݒEd orNՍ pPYZQA{ ϗ~P:t`e!j*k|Uf6UGJwbBR0gSQQB5BL8"PG=1ڕ >UC[oVnKꞏo&1pN'A(<+ƺE @hm]C̘s-٢w!=ᇗ :AG\RUWe⊾ ,ц DٹY-/c75%?τ{"??n=."x+4 '88o{~_m~,폷gwKAy~~H⢢eM쓈o=~|Z8zn箸{{ޗ~vp?j#~˾]mVp6EC7Ow߻Y@:&g2Z l8DŌhRE^IK& vww)I F|7tgN` ሪ"M;@X<;M.rg !$M ؤTEػ3ykط"'MS0 Hdž!6H]@\n/ѷS3fQԩF$T`hAb.Ŧ c ?6ľ-$Dm^DÃgRvX/hޮ$D--#M\j(qksfgGHW{ݛ;iMMl4c7 +IaҎWrr|N؛-tI 햫( NB{H9N㤯7j괈9LbnyEX(LiÖ١h*o_'T}ǷD<`}:9fb`Y=XzET+Iܭ\_ nCGn-K`}d[Ɗ.VK+t3^ed8aY!5X1mn^8}̘XoYUi#l*KrnEs׊x-qX DI;HL -%H9E~BG}\rdxGiZ!wX]LxvGr^Rz;R\I0 Dk`):I JOЯ3s 9>_s]g:u#')"@+1wBɄ4T<(9p|P'vH3St)"R7nAeDA aTQ|Gav Q>.NV1 9b~xF{١n@%e ӷ(n (i>>ݴ;G< U'>Nйu֏ uM gKnC8jحAULJ|˞!A[XgXF1kZ7P6XJ[lbMg ¸Iein[._iֈP*<>S# {Ew^8׻!#v19|dy^*mQ[{GSJy C^yգ9i=i:ָ>M7WN bade4^s%/;XnorP.={?R {X@oo{aרD?]>g1@+C|Noʛp &܆U%m=6v _oӛvan|@+*eKT#iuΛ+5!3mxƪ"cń9%͍uZu=Z6 _-Ty/< Vaݩ:dä%McUI&UvR}BaC,¢=_2w9- LrrA2͆+ _Ζ!v+:j*YU {d4PLiDY zV8^^$SȔ-OX#n]d,XcS\q M%r%ZLMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-hr.js.gz000555000765000024 450114574361234 22232 0ustar00mattstaff000000000000 Sgrid.locale-hr.js]oۺ9Nup(p5XduR k2E$4^?ҧ4iW}XX"'7hVZmuq۷.LaJFp?އ͜ϹT(^ssᖧ(-+f/ fgfȸȠV97^S9<;mv)U,EdDY`nx Uc dOKQXs+摭 ED-&zRcP܉H\% QV%M{.<NPpdbg(9`3boEb,wfJes\nJ;3+6{-[_XZ5B"dX iqř<ƂM]Id+!0tɌa> I3gI8E*fki6嘔~ Lvk+$er ̢F 5xls.ٗ}k͵YOn&!MkX7- >*016GU/W|5YhؼтHvP6/'zDŽ XHi)C8i^'&5ҸըÀě{.^'kZ:TPS(E9LrE4Tz o|c!%(kFk0N'DZe0 [Ԫ#dQmW(Rgn)`e9:ÿE1Ѡ!Z:Lb=؎xŰj*g!NTC^o=(shK4 ϣHпj!1>v۞{rVҩ4Ź #2ǑQxԓa)XYaDְ_DڱPNƎF-G (jz3{ewEdߴǫ0aajρ9,tce+k6MRn,dl+GpQ +<([oVbdTx1zfx^5YHr;<-40,EA2xNNN>J'n4cacc0i7T;x?p5h4L)R/ 5ݟ{˔ƹlZ+:G/8o jnm7cDi=t`"qb"׉a1 9٠)M8hOwW+jް|(0jF}sY9޳wp!>$o Xp .q/KM"-nAXLc))nt3s}x Yw¨B*WrlrV#,D+4iU~5.r5KVd|e3g]5еOѶ?ȎTьE:c%~ {wIiDl92xp3M!" ~i"v*_Ǵ99͹OA<$tԔu><>,ˡ(@ۣWGJo˟(R5)va+ltWo;J>;bߎ+7BSpa)l4|AȅN]sQ9=ߨ0U[JُOq5|;e&B͎CM/ mgp[ }Kb[оЙ"=jurli*lsMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-hr1250.js.gz000555000765000024 451114574361234 22543 0ustar00mattstaff000000000000 Sgrid.locale-hr1250.jsnۺw N}`pp5XduR k.Zm8׏6 ;dM(v,,z֛T*uBޏ ?~C09\dtƌBezipߓQrA/ .u3h*nn3~< e\yZ0!T~WL)WT*܌ `*W'5=̹r8,*+DER]Lf<~@9qSm44؂m)6|Fuam߁9^6ȥ&\Zjs0m$ha ղ T-ۆiF- Yk-a,2Bm6AXPmj~Q25*J {Dqc~9!X  as({Ip!/04X.y GHm("0 x.<Ne/|9hCvE DeVl%F&1R:ctNE 8a>`*GB(oaTuPh`,l'ӗ|FI9uYţmȿUG2V!;x _O(X^a+}** U=jѾ`hhS -fŞlGdu5R'P{]} ʌ([n:} %K6O>?0Io'?}'޹|jKƛ|9PizCyhx`H,<21m[$kx- d1v,T_b͢#{A3V ϒ Λ 7,^o I&tTnsaJL6?en7By676Ie*ܬ{b08oH?Zİ SGlPnBs'N&p3:N bh`6ƨad/?5{;蓃O>CHuĕ>\ ъ&4*`ߚ~nъ&}\+X1ޒyG~m1tFrw#U4CWX"b,) azZ:Q[80x <=@L]Br=q Ǘ1-bp®~S?/--5[_bh& 7{߯|N'TM]qem. =sxg !6mh %oo[ Ra5g{B.,FbJ;u:Nk8{g?s7a&'B/M7 mi-̔>ǥ|1֟/chlLSјȺtz`~zAZMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-hu.js.gz000555000765000024 361514574361234 22242 0ustar00mattstaff000000000000 Sgrid.locale-hu.jsWKoG>S4%"ŮֲbǖXrA2͙Ş9ak{LW}ztswkXRo}}{hYsapjJ0c\fP oCXq#&^IY7-@y_ J-fP S 4|eömWWWrB2)vKXn'.\W|BBm K[5=ApϴZuWĤ_"j miAWJWzYxO[,uDHR vkb-xzFæhJ 5\ b{znż 'P^L)"˞,Bl< s4csMI5CCL'BJ9jOTi~ok}%{(0hptnNΐPU iU`O^H9R!zz`KWŀM;sY1.K͞q'Ռ[(ühR'G51ُZK (( y 9P#9LB-aN1"c6u > ZT]ᦊl O)'r/yϦv/-tHM#iVגKbLy<`* * CHA^ߍW,nF,1b/[#>a]HRa!aR kmǩī{'ע#~DAMFZ>}*0n,1wX۶pM4עt`[iS#q QeKʨ5Կ>E9DAÄBZzƨ䆇{fwXBVrrԤU{Agɣ&0+m~ܾn3Le!R,yuJ.}FZ:_k_7`!'D03>}> G% ܿ~ y$Xʥ(|xE&P.8+U c(^h_dחo*nxǎ_G_6q*$ f3lcJ*X D'*~Պz/,ضC.A{&7t%|/7axUPΓd=M'側u 7 kww丽s޲& -BpSdpՒ+zL%g5WI/\ .v̇V߾}Rg}ٷqc#dTIqֻHgM 0Au;"n?cT0Y ga\AblFA+6W O/ W ..NW|^ >qAc6sE74\܇BY܈1^J=~×OEp onX{Ƞ3GZ/;SW(33e֦qK*43L9Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-id.js.gz000555000765000024 462314574361234 22222 0ustar00mattstaff000000000000 Sgrid.locale-id.jsYms,?d(y(%w::MXvĴTSjG#y:xÝ"Q߻ GI6?Tg_],SR+JGӧSRfǚ)#q(+"~Z~~fm5H92O8&A+3p*1 ~KS/uԸv7լ͖++ٟB>~iVg~n/h_[8C7me|ʜ@-Rg0vfo n!yQE], YZԈ~f訴IV0{B3tVۖ6JTZ /I }1ϙ%6@A[1hd\]j ]:/ qD+O &0%r2o82Q8=ukBrpێw=r&=j]6AsSyۦQFu&di#\:0ζo9äW~fN-pn+"ehرSl KZya14}S7&ogYŲeXǬ4)L)+0xW󓱛`s/J$κ&uaWd(6KFƢ6TaQ)9x_B]"յ_a[Wadt''40KY0gb%- bA ]`i_hͤ|TB(Z܌igg{f/y*&F^0!}XKc}Ť"N(s\ ԫБyUX+i"*gIwl!#|xy@0*ȝb4ݍXJ ~`hSH(* t*Ӫk: M(adgb׸Ƒ#,,0k r/je؏X)n:OM 2/{; ݢGY! u_ +g z+?:~2r{/Ցu6G(nU6zD+::+\hOJ{ ,~G# ~Ɔ _?9Ŗ(jVfXv `6.Ï+Ej~b^kaߋa_#.D^amJEK}yE:҂`Gyom!亖"1֙r !^8*pG:NEGNDSfbnt].;{YE2zjdz>6&x}ہo`Y|Dyc4$Ow]Y>o*HN#CSWߜ'-ljV`[,^jۤ WI>+]z5ћ#56;vKpVq7 6ޙ$"݅I. 1,mbxi=`ܔ䢑^*l_2|j1  @g$ tS|I,UWD %5gXd&SiIKZpfxln.04?mU?޻R僎&x3&xVj Z {HQV0;-`.ZYF/OJl:E!棔ٍF,б+r:џ'{} Δ=MySa;X j,Y_Q)y;QAMruwl9m?GF܏6_v{} KC< z0? 6ӐA@N'.擮+7q`#ʃ& G-5c=S'nl"2 _!Jp e{8h'xΠ8MnET`oDE10U`Z1Q0"ݸ OXTVtzMؤ/%Jwὦ}]Ԧ<|YgRm$wbg[[nDa ^60b}_~)YG)G)>bwB9)OS&otf;N:y0 De:ġjR^0l`AF urr=hOGMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-is.js.gz000555000765000024 356514574361234 22245 0ustar00mattstaff000000000000 Sgrid.locale-is.jsXn9>ˀߡ;ن,3`NǙ  ;XPTlۂG}r[rSVle;{XMW*V֠Pfns;0zR^\25SF2Bz>ma\N!TsX0 R\@9:tIӹi vNdE{2`@ˆq.Dq^3ۛ.π YB$&v9 _ih# P Jϒn۞4IX! 3t< ƑʘM舐 *aD:*oٖ_G R0˺pyx-pFe 4*s""#2TJ@e1BJ@PW鏈̡ٔd<[ˆsX{E~V\La;F A\S9c0^SC6`G>'٭>,ȹԛ_05iNS.ŭs|203"$Bht1RhP(TIUU1Oktdmo`ϵ9mB3F5ø; W B t,UaJ7o=a *): e%,Ņ£Ѕ^k7mL]e淚bkzgz;Q9^PB튲fBOk CՎv cakY|0ʱX!Z]AYUׂ߬R`KRII\+Ŕ:R=J55̾+&Rԝ7!ߡ,8N˯^B{0~y<h>ZlWhlJV\[< W =\j*nP8$ fup:8_+ pD l/=1V|:j'DCRw{,?U׀k~>Tx2?D'D}2 (ʰhiya5=>axQwWh5gD3Ș\Em%qV5*%q5WfI*n]ڀ)ow)\L]dKӈzܳ] vw)=GU=q?ݜ^bEbQL"Ƌ Ԗ3/:?I5\a,?}9`ڢ OwZ~ 9=bK5=/^(;1AQQ>U~7ؠr;wlO᝔ h{0/#M^DB [mG#{p? .7*_~aG~o÷ׂߡnjb݋l% Nֹۆl\۫N_Oۭ+{evu236<߫_?Uڍ2+fi{\ۀ\8⡟hTgԹXA-8#` йȖeppB{Ȧ4M#fa(b2kX2X_a.WWa)=}x7ܡ:[Kv[enn<3X[>_09H2)}/v܄o]\HV܋ \4ܘm?? GF+Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-it.js.gz000555000765000024 317414574361234 22242 0ustar00mattstaff000000000000 Sgrid.locale-it.jsWmo6jh0( #/]8@%e6钔 I #%Yn~=w]EH@' "̓ H R)ʅL G6D ZȕQR AEk+6CrTGK@vOT!#Za2Jȋb eYxF9h6 ,ˀ&bقT#Rqi Y `_(8C+~cun2N /e5X2!-d?Z1Rğ&x*l)+R'ηGs SaL'lg{f< M z\[HJC[V"'M@\^RoQpYs0'>dFw4Arh@K$aZ欄^epi簪^)̳ *)%b-!r,[*$5N$_Dҷin>wHֆޤ%,BՆUWn"ۈOkUOa}UXXؚjt/ASKAhE[sWSmP}Lon]k0 Q=б2V)7x%`\I:5Zi|́Hቧ= 18O ⥷ 8Oq.V)[2lgjA4 ŎXnC |9I>2cn݅I,1Q~4MՋoQq^&8rN?cM@ږZ)}U`h]; nx[Ļ^w _:.HkVA1<.3 k^k{3{"'?N~yoxdVc?" %+7nvV9Jn/!;fipOD(ttɡʨB .x 8 Sg0 6k%vgxԃ]FSG(2!o?SBn(-u+4VH,auY]H1_vY~l^"Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-ja.js.gz000555000765000024 432714574361234 22221 0ustar00mattstaff000000000000 Sgrid.locale-ja.jsio+r Y") TqF(PM 7$Q PǼFyeQxӻ 9#~}nhFc&4_{4(Y)u)s?tջ*M7~?}Nhݷh'f4 0a.B$]1r5O p2q{xxE~ezQh62̶їJY )vA_s{LY瓖<iBՊnE:ʔIJ [2o ;0b 1 m[\!Lxt+2CXE/jzQmtIAx>*W0 fYDMHFCuȬQ:Y`. Kr=y~%Jh+]լf~Q#,31:ø:Ya/f2?fPd>͔4hތ 4, Ies0[ŷv5C$E- kC]aoU&Ƶ/g퇘BR^X]eLC.l$x$KbkCs GWQ:c!okS!!bc"x; /_ۢpG5e7/?T _#q~e 6S$ŏaYI[O8ir%䴓;:&Ts #C3/)-1YGԠjүsE2(qؚ->Nњ1}*͸r(3 JH)JDa(oΧjm1MxΊ9wj_98FW\Ûh>{晫=ֶU5?غyvYBSխb 0!e=7WQF\~O=OZ.GpaeHKtM>j0׼jiNa!1lZ5[,]v%CQ3`pA]EqܺS6(MUf}p*{jg\?Gᓆ(ω^Z+(\ɃQ,V?rmczGqkF bA?xx&U umֺMLe'*MfP%q"puQ?vYGyP>5K fp8pnb-H8׋5jKWQgYr6?" C1HyA \kԥ^ǫ>ԁ7=uݝ,Y<16P} `oK U'L!~ ] ;CTɡb NEh0]"tX X,4R(W)vPmjU[֬Vھ&O.V6֟<2_tf/.jH 3oiWM~/JbG 7s!{(#y&3FX,u"9/@l=%C.mD~ä]8wQ6OۿE!9<9qdg\-<hrkبRΊ3'*x+΁)/׍,Nx9@({KwG,4,T*-ǻL a6@4a X;*z9b`x QƎ!G\?ag .\`n7w2?'Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-kr.js.gz000555000765000024 407414574361234 22242 0ustar00mattstaff000000000000 Sgrid.locale-kr.jsWS,54ۦ1I<ƸY^I!2aRܢX'vm7d`(3v s+ȧ~9q |Kt~wds(5::cG7tbP;o[eګ?z-&c5U zxЋ#sxq|Dz] >? #EȥΩ{zNO.Dm/mHBw #aZ=LۓeV[yE⛼=XrVJc!{.r;2=!;Ƶ18Cu Ac.1l c-!aUߑ=sg)(EӮw {-cVjQ윟a%;Ob`ğ^ҝ=I4lm؞Jx/b7{h[~>t;@u{ Ͱ8Uͷ2hM uq-͋#S{ѰϪe ].+zYz}K J m8yCMz(M-~l0Hh z=7Ю3A=d$1g@::?:^У7ϝu#y"Aʷ,A kq|ݠ/}\޽:.%!8ibi5.R^~}jepBޒ Fdlt oi|GG:% p~RI8-z8+mb K(K :QYjźIcZ l=Op oh%Pdmoy[*.;Bn߉3) W*op~El[! 6ݦrQ_2ĥ-%]MFo)>NrvvF_I_< w>~-`Ql;Pua1R}8$i$)$g{{.mu*UGm?Aq= 碂Y馳KK$c;7h@ glpZs }ҳ/=LM[rûe]j6 RgG̟kY՝" &Iv7;G 6cxHx aQuS1b${L\6E iCX + \?~D1n yJ{|!-|' .!L;:[O 'o-J-xլpNeqږWH7nxd 8Ytn-77}<2zlmei^r\?o\+D#Ԙ*AMwL<Ÿbbʊkr`.ӽDWF`!Xtg\?@*| AIXvWH,/(^! =1WG4\\Gąqs𛕬uH_d,5eՑZ{KKK_%ǖ%w|9%Ep\b~Dm3q>WdlǓ|)vYH,Ig$i`AXÆ<6JjJ@ ;`fKhC5H wii>=K_%{ ^Ǭ/}cαU$ 'd2C{S IDt=K|dmRxgŎ?>:32PXjѵNѡ5 '[Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-lt.js.gz000555000765000024 377114574361234 22250 0ustar00mattstaff000000000000 Sgrid.locale-lt.jsWOI?;#26V+gvlda1 ZEN},GsFs[#ms}6dQrʁfv0VZ&p # UO$7shLm۷,D <+# 9;&#x{|PuHάVvkE+kR8OZC&oZB+o 7h3Л_1 ]UMA".ԏZ<J nwg~77LF[<,+I }j9VȒŃȅvVάQ!Jsހt\$_]rŴxĪlY 9XC:Bx ᄗ(tE[C K>m# <fIҨ0 33pIir P׃'Y`~8A^scFDͩ#X$25L'|` QJ,V_y/,< uY=NT;_^BNc=ǞCxИoE _39㒞d#'k66KjI9sxKhDj+S#2{QF#W?ݸ\IuGdDb&mL>5z, #䘥;LgKt]- wU1 ˊf&x1%!c'-ܠ 014< 0 FϠL/^BԬ"?IF.-89KJ9Ύ=ƳCrKM뭰!q,R2|_(P4Vwq6 +Vi(766xJ3b5#5nt2[ 9$=wEbM ' 40a6d~Q'~tjm@VP^Qē'zQb)0z(riK觴2j,m~s9S4 \}kncpÄ">@TIjs {L R\#9\mJ4/qΉPBcOz*Nܭ_2}ub)xW%Q%ãPD`#(ڕ3V楟̙4C޷QUgz%xc։GDz9s\;e TΔ( xKhю]<1$&v4zta:Ka@|D#Y^zQYI!=ׅ6G[p8}?G|༡鳥!#W  [xqbg nߺDqYUj+g;*pw~]v>diO(V\vsqїjwn q] wf~8Ҿ#})&  ~=T+l"!>"UsA%--=ij~+?J1Jc oLJ?q#(2vd {O%oDi~#]0)>]ٍgBQ(˝BQRzcX Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-mne.js.gz000555000765000024 371114574361234 22402 0ustar00mattstaff000000000000 Sgrid.locale-mne.jsXKo#>Sc@ F^y7rV,$͙&Ù~^?aHNr(iK3]G/7N&V(֋5؂[-R8Tr'Z0 I32?V$ Ue\wB?z+o'Ց~2O-܍0$E6B<ǷWQ  *X-YU&ȉи K@[nB ؾ\Fj iu.BĵRHG᷈1NMJ' CSfqr-TwJ6XF5"VpbeZ$>.ʕ䏫|?/ZEWhF$E7|DCW{^5yIHU"45+ڷсы((/_߂N{3ս\~%~a#ۄ{6^vw/a[xCүvڹ8dvÛFx5t+M"MZF'az]lp.=HF5=VO__^^y^K}կLf@V`x?Sr2h{?*m@po^3X]xi7x'ީ(V0!W?eP`eTE8\қF9o8Y\^$z&EŵABؗZ/$EH zr,10fN]:fplDezB_qE#uq LV;ƔHk͗q}KMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-nl.js.gz000555000765000024 335314574361234 22236 0ustar00mattstaff000000000000 Sgrid.locale-nl.jsXmo6 `pVdMӮ6E-e&钔4EɔAVxǻ4P)o&ZW6=N_ -ȸ[Ob<;_Y)[ﲚRl=O٘KlZk@6A7rXd$*>3rG<3Ca~ZNRUaE|DyC܅, {rrȕ4YbPl}~dj[/s0q.k֌.aN, 2vt1+ b`j֗,%Z5u 9wp,.e"]e &aXIX&9:,cHnk e7|$U_3z<ӴD+De=j`;8=yIVXk5qg C 5e^rJEtǬH!Az4U\:) /8~ 軵~&(ROJW2R&15Jd襹kdpZ3%Xh(7v}_nV/&/*ޅ(3Iwea5h TwzhKyrDfyߦ~v ZDH(sT L*n%tT͕ȸ=8x;:vB6xn^O49T Es)mva Wv5CkGNΔfC5CƴԢ#_ )[5a-謄BrQnʸ!͆f*Ǣ[A4ğj5}`#IЎ*B9a콝ڊi&JnQfKq`zQ1U,sP (J- @% ?&4,G/WuqxQLz%D, q$#d[|m&,7ԛ2/ Q1sdnG;5C:T8pV"G*/`RZTLY]BM,8SdBlx1<Sgs1; w8nyv4!R#l;|izGE99N9.OhE?gS5v@8̹^}rmz&pɍc[S-Ηl ;(NȽN}~uR l*=~ _>~cK4d\2vo;|>v]d?{޼9 Y MPj7-0=&sul.4v>uv^ݛNaLwm9?zg?d2sn莰*)zaq7[Ol{ŎXbUGy{9v#{jC@IeG\JQGL{SXrԥ~[[!_Yq.V (S:lS𳏗c!>A`jΥbp Yl&%^06ϧ,|GH{0`AH۟:}qЉɲ8f=~eu0)o!^lʳ'ʈ^L8v2)#k* ŊN. EWJ]W͓Kpv>J9/)4`=_JV|| n,܊N.-0@;1<+}po`[)wVN㡡vdHXL$a$W ¨,Yv+ߓiH+$$^Kd^ag:b,l8 ȦQP-@ǝ(7[|X3+ d`^$ٴjIJsP)?Yev퉡Db[UBkrp-e ڕ)K+-4"Dxcú3L ?dQ( a}!b"\'+d5֖P;ݓgvZH neUt6y&s=Ou`1XCIkuu}y6ar6*&=:{G̟I"5%mMFH`|kc:Po0:y뼩b+K8*4:f/G!`/2 e8:WOIB<(WnGs  }`㡩ƓLffxW}?u  A긣FKO7=bQx\9` TItDY`RBcW1Lɐ~O\_4,CL(=v(t~aZai }I7ۡZr“'m %`2oq|64}:=GauXä絠KZŽuniUUD+i]uO\-@Hi_lY:Bp+q]:~}D[&ZyࠤO>h6Zo[^d87B[&MrR_x dd$.N^)q6K`2Ũ4>o/~fG韯'>rinzӫ/Ό.%Ӆ[< [NOdd(SScC+YRDN{*[zsHۍ>Z"S3"ͮzk-s5y GgpР ;c?= `d5 슅=L#: ltHma/h^ݿXf2 & Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-pl.js.gz000555000765000024 376414574361234 22246 0ustar00mattstaff000000000000 Sgrid.locale-pl.jsW[o7~mȒE!o/w 8.vQP3D 9W57}[sH4 , a|B`{lEd۟mww.VȔ)9WT"mBuE΢)-+ {y1?1!ڹp['% _O2^$ƾ7J d.HT dVs2*- ] ŪDC(rb ޕ1AFb< &ˮp7йK5*#^48=I}_1O4 6V;!0I\-9܄3ICXGZF@&R#+hFQ;{-U^:zxdMYO9biu8@} n(>rm}n̶0z̼t mGBp1i2.wzT+r& &l*{Ԫ.) 㻔22{hS*P:wDH\5NTrZM2pHɹ"A/bЛܺ6ch[#n_$Lm&ʍnPMϟ)%U}7]\ (:ԧi..~./$8? ܣ/LPUG`Ԇlhy:> ,MWb.sF8m?o]Zm&;\: =X+$p"H;w H={FUٞ9jYR9KzTAeМ=5o+(b a6%BSk]Ij3SݪPF11Q(AjN{W,  7 VhJ 9A7~ϳ bvqq6 ;ٞ;ŌUt:.-"_{^Krz )GϮzks~臫No;{K gSL ,GP: Kf\ܯsγ&wKLl RnZ 5,9ϛ oB~z0QoxE!GJEZ #TQ͎H.v\T#=P5/WruMw^<02XI=tksr;Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-pt-br.js.gz000555000765000024 377714574361234 22663 0ustar00mattstaff000000000000 Sgrid.locale-pt-br.jsW[o~4 ن,ٻ@Q(ͺlė ;XP3Ԉ )َW?&CE_c=d@+$9߹ʈy,d Dr=kˆ`C~=nHqѢW$X;;R&Y X՞6ęhZ'>" 0Z|Y ւD .?]7/*@x:l[+nG5*V.oUA5Tb޳?XH*fg3I`FA"e"p}vyqW`9B:CF1/8G ^c !'d\bt_XV<N $UVqV* Y33<:ƣb5Gr.&cLcOͰ9?ti,xJ0Q-\Bee/n^{WjP4_*Zуh07i}i~LcSavO-T)C?Yc =7xw |9IK#]0 >Կ|2RV0{X/.BZ!ߡ]7(]W-Jh_SG,b_h0/ޕ>k>cnh^]@3nahUJ]HHlUőq/JܜС`*-?MA*.U96HDžĸ؝뻻j.8xK$rLFʱGE.x<^`w>߯knԅ/B_Ӳ^C3-?cJZծ7͖/F(y C[Ϛlf *8,BY*ju 䱚*]%HRQ$V-4]B#Pr91PR),V̸x+QEZ [Ry.^uOq 9epօBL [sTVtнV=q(Ît_hA5_Z zzƏA+"сgZ`.;q,~BE ]/C'cX=|@}nw\ud P/(f0Uwco·Lf>hM{3:˟w)#g>CTi;SVd$C҃g"sEU !{] Psqr30B lSn77kW]atE+p '4~v]xߏ}krRXJ_mc*0z2NHPc oqVD Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-pt.js.gz000555000765000024 365414574361234 22254 0ustar00mattstaff000000000000 Sgrid.locale-pt.jsWn<uNNvlw&.r$@ Zef$CJN2Y?L>P_!)[v<3ynz=uZ)yn.=|ɪ.꼖Nҍ[g( ^KK'VfҪБ3QLL&U5},gJ>fҩ\>EAJv2ZoH~-4\Zap`%^脲œÁ17\QV:#XS<@dtD^+IX4gÖgz=zXI:gmUw9؃8U_=^cb# Rouu VZ&pXBIX Ueafx-T-e;ʈ 2(]]0vbL9MViEQUU j0c Nͮ% c:հ^.ZXtI@*UvYwbliuE"/%1ڠJsJp,lf*ZB'p5Ah)m Tp|uyinLE{ZV>[iL]i e8ѬP1v[#J%}g?ZST(t< ~Ǣ{%dֳ%U2#:!VFݓ,:b#pz t!z8"LEM;5Gጓ1ji̘˸ćLآ Gܧf0K6jW9BH*A7OC6 ^5ԹT% &Q=P4,(]?!UT<6efYT 9GF [ݧ-mFA"SL;5w5В- RI8&C w` -!Pɸ#|+2!!0&HYF ;r̓|#w iёQY"ؑ.Q| 5?s7xj%.qӥZƅݟ}Q @<0 k" _y'%+~W?~ڝĂCy|odm|3/mXQLЕy--eG"P[2HlU(9yT/Dd Ƒ0ǣ"g8/o DBC\kS#MRE W`,t T9.hk Tc%r;6ܢJoBҳr' C9V1O4=SR$Eh_ZCiAf8^뢎mD:/cIU]x30V4Z #ڱ*9:meOcw,8"+C^Q ˜;S0,`,sŢWM c`#Zo/VJW] =zbw3();2mڜ+{Hυ[xGԩ}uRRqE2SyMJX?Xٹ7 ϶3g;(mϦN?؏ ?MkIقLFVdVkK~MƦ5A(h!lA!,9i$lsy@o'.g(C@v[֖i2*s/1+(f=j=Lxf\~|e&_ -^KyA75kGWgeklz M~bnppB=_+8mWiV jk&]a&] _sLjU:'k)p ^I/x2%r{ӿ}yvvesfΪ^5wumzP rᧁT!phRhH~ TI@:Ij}^1Jm )[arZni.|fssrCmàs츹7JR8~c?O;9SEQt2Sx"p) Fq;R{p WwUOi"SGŵ+b:2"6K[E}C Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-ru.js.gz000555000765000024 443514574361234 22255 0ustar00mattstaff000000000000 Sgrid.locale-ru.jsXOW/V6Ȭm*E(4䀜݇d8%mSRU~x/GvYCRw̛7yoor855{ f+j{\lXiF)V)~u+~kLV ȖIj_kͱ7ݗ[Zuv;HVC TLC6:w_l:p!^*)%SWZيO^HU`Z7oSSʼn*F,~Sߜs[Kza"-50}Ue\}૰lCtk<~ɦ~^xgq7b߅萅ga+|@bi؏YfXg"&~#pE!zrBCD \W]&:&yȒX]օUt=N6ma'] [gp^n%ǟ ͢a'f!^y+]6/^C.їa7r7^k5 Nh7jM;Ԋfs©BRo0aYp^5CkQ~?OY qDk:)kPKZhԃw.%!Ъ.%ږvBɏX1?ZXH3,NPP]ˠ$`fYj?p>mMey1qk>HlX\,D 8u×eJLh{x/R0h)CacM\6xE)bpG5n"jRx __1O h>cZ kALj[ l{{L*KXP>3vnyUf8'r"ಏt ؾLw>aA `Teڄ|/>+< [KBE4 5 ~A0!aSVP !Q|Ii14.Ҡ&Ԫ{P^[Aفtw]J=ޕ4iy-~3Q 2@@e"g2b@tl&=HvH8 ӿRw_csipI§•"yKDT ݹw`1=}cŽ-K Y"|-gZ"I%3Y%6(cRCVQ;ٌUFhY-ȜjAcn>.fr?$w6f`2~l-O9[O>ZncmQk m6ʓ}V.5Rg!9~o&9:)i U5O<tڧW|7б1Q =a99 mt~Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-sk.js.gz000555000765000024 375514574361234 22250 0ustar00mattstaff000000000000 Sgrid.locale-sk.jsW[oF~ dC(J' 4 8vc9 eW?`VCokϙRX` ues@GRtؼommL},NYDă(fG~OmNV^ Q("!" 'hUE iWWW=U{RDz-z8}5*|ƽN⍭Ɉ|T_ Hך_< W-Bxo ` ;s؆9W,7%-?zf3d>侌#bp9bZdCv[Ab:"Z 5Ɲno5GDƙBӬKl|`kqlޕ2d lԠߵ`L,Ș7Vdkb].oC"$Mʏ_6ZJ+@ވDUܘ8gMh8("Z"&fp%/ʗR1@HI3"hpK?[e 8n7b(u92R2ORL`{~ƃyFDxLTDHvc8t)cOAKx . 涢yL6"aq]0ҮVX>[DUF$a*AVs;"NcDcSR 6cq*PwЌ|W#d!k`5Px`5{fEqW^%PK,_\G7*pld}T])cLQ qV'#$C҅ј,.?P3bYs$0!xcb0Hrd>T5ɀMe.g,%?`EM0Spm"԰Hh>B`9<:lMZcZ:6[i媡?ũ*d_xO*~]C[9dmlZyB␊V NٲTf@ֵ)T`]XWgZ&L)63gP4bB(SL`̱@agן&0gbDDb˴5ZDʖ*rى+A֫ y6$5xh|Yi6=(Zz+^$AZ45+XA-AN P/_ղ ,2Z;NVZ+n˱E&[~:%6ߊme˱h#f~~y[Wb4]V$ueͰ a5BC j֕u< =@>GSeM>|.@-B3F*vJUL@,oV/0,U,0aLMto)%8&)U0ʙZ!XlSF ClwM;b ya/ټ]~_,պގJq/Zl5IY~I#ʩ(̄t6ZxGf<?82[nOYS.T o6nj.L >^j^X(;6|d^GSc!q0xPrxU}-Oaw4qoT[nx/9N@νG8,`8XΠdn׹0oaw~ N;y;_mSߞ0=MәOMvw[TMLpl;Ffz;'3הM|yqqi{/}{,V}ߏã~J3ZA4L4Kjn# d;b܅va/8[PNnɌ%qMy!ы*V;\6G+Lg8d:9ؽKl/쯉Kg..f"nmsX^vʘ/z܁|ym\@QH5Nmͧߙs5&ԷXMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-sr-latin.js.gz000555000765000024 370214574361234 23354 0ustar00mattstaff000000000000 Sgrid.locale-sr-latin.jsWn#>CYمlC] 48㝉7㟌mDa -aa.yD RV^2'2ACB: Y[2;Յg;C.STfTɻ]%ѵ3)vvkn!v\tQXmܪ;#[/ֶ/־ꌇC='dXTFdm=e`MfIÜvv3mߝ7fE(mHԕc J1pEmᣴb*v9d&[J.I U(L:MEɹ$ Yt:!=VƁ3aE.az-jIPbvT8gDnn,|paa\0U,$sRAJ, 68cHtA2|D9^+ .*t3|W[T+Lm/Ѯ<./AŲwߌЬ v;~OGa)0ks0T_SNj0Bf/ƅKp}Ε|yѿ0eOU=^n=\ Tr]B)8LZsk*+Ԝ 7aDj%ۉp_M˜QC_<|tS̓Uu =}G<7~"(a9' fmtB) Ga%-U#4 ؔ)B2(H+;N0-jeq fTBH>`K!,>OmDۃ)L)-Qٻ?7qe!b@ӼK`YsBtWDgEG:pV) 䤂VZ Eū:.>!a[ +2S>H/q'F|=>yZ#Ѕ0F܂d.g$YzLRIc#|?ݭRw~2ްT{(Ǝ"K)\oz7v*v]'}V0j.&Qljh7y٫ǽ?z.|+1}3D[eZLt-~/Q%U4muBRHGXmUEMip2FDu RiàĊv(R4mXVzecSg( 'Ͽ^4FcVd!șDRj=Lqx)AZb]Xp<8X:rs5N hgGy~ym AGj0#Ҏ#8ɪ>z]3A|Fnf!P7f,Dkv=/~SI DzqƗ Tn3[xgOd{p) Jբoάo0P!EjET I`¬o@mhjl(mE@ɫxjeG1: &< ^ijÏN!ЛǒHOyt25m@䂮be4̗WY!6D'.l7Nj[]-V-vԺ8vԡƘ>v7ki÷WEB4*"vM•sz-NZ{.oӾU~U94vgV^?d66 ,OOwk o kÜ; jC}qK2$2B].ˆ!]\^lLIzT_Pr\˟yG7 L!| D^wjrVH`5{i6" 3Dx"85Qqrɬbm6qhny?7ȤMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-sr.js.gz000555000765000024 427114574361234 22251 0ustar00mattstaff000000000000 Sgrid.locale-sr.jsX[o[~1@R8 `Ŏ%$8Iq:YFsYݕgMe(5(ѢEǍwL8)PX{ |>sKf^zAMMxqsu._Dޫv(R+_%؈gU-YmNBh[ VM('mcTؾ>LL0_p{Ѧ IYrir2z8Ar&8ICѷHAzz.zbz1A-L|}b]}+QX䠠Yy<+BN⿝h ieDF_2hVăJ euSt.9JNDʣ#c(c>S9*pi<-hsԷjlg2t a[e^+F0 rQ폶Ge.5$:~3kʔwfQ8XUq*9W bX~ܻ2[*&#Ah "ױ//= o59 5uT7!&t>8;>h2!GWR5z_Ɏ%<%]Bw9.̌w 06x zvǎf+Jͮ@/#"=0P) ,l{+eVI?} vkX` Pԅx7ZK8h%X:D#Ģ=>hWvJg2k"#ԖG̖jՌXHEx@Id[ /'OuXB*WLPMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-sv.js.gz000555000765000024 361714574361234 22260 0ustar00mattstaff000000000000 Sgrid.locale-sv.jsWnm?dB!Kv JԈ4젠v%.\ɂi^/ ɕV׀KΙ3Crf89(UVv;ϯȠ7㙰C1LYE?a2 m  m[yl sn33md8+30@I" Jqn 0뫳JlzӺ4)okw*X ړdC+W:<rw}{Ӯ _9 *kNBpcx .u! ,3px8ZÏ8^L<-"OPʂP979SRe3v &9M'2͑E@̤_&t~g Wũrg>*H?斓 -uK jp1Ǻpx*-.'RYhUŗ> tKa^ p)qǬq|S4I6£,xV-Po pؒzt[np)‹t0/#SM_TY‚*lԄjM]|ȍ.' R0:8I$LJU /QV tS5 7INax N咧b{{HC dc`!t1W0^.b%$/bVkhj%> J 0KZoRj ٤r?gٔDJAPZ300sT-̋SJF{F| B3s) Ϻ)yۀK3cWUYؓtEM7L/C RGo#oSpphM>A*[`Ham]PxT rm\:& X(+:4kp>kÕq_-,4k6F{kǢJ oR%ٳuc w fwM!-3}!`o1tۺFŌ-k[eG3cڻ&s^J`Q,RmYQզz={P 5y!.OÊ~}EKP5JqABhLb=`¬)TSQdBƨ1>cv:%JĚ# 9E^ح0:=?-*yO9TwZYo!7FRr_vd/q}!@cep"o~NgѩގX_p ui=N7!e:^o UtRx* &WjI.yd)6㶁aXT:X2l9yb}Kq}7&v('_FXz\*VJ|Q/Ze,T+j7$0+7W"Mr]-.7ü/F#z-UxH/+=9nwoާ8i;̈mw4`!mhDD+XE8P+\NZqqU&"N?< ^Wh!\00_舾F/ao~uƧsml~pt؂>iر& $igOMc|Z~MչN֝: NJ7|xtUM<#ݵ q |@:MY DlaNY'̍(p[p?=as*O ɥ)=d`}kNL{>&PώpՄ&9|ᭅf]gJ EF9O)so[r3aqMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-th.js.gz000555000765000024 423514574361234 22240 0ustar00mattstaff000000000000 Sgrid.locale-th.jskO3Hmhٵ!֪ZdnWB`Uew`yYVq,Ku)",d7Szιcry_ =0]gcQ6L65z[s3 ԛlZsbvuExL66U!kۨ1 5Y,t g ٽگg孭Uy5EW`R>T[ [ Um56:1Qӗ_-;ƸDT_#_B+Yu3R|։8ۼflf8:] g@Gq}G@pY@{-c0(Z 1 a$] Θz]6r\ ʺ(2ؓD]1:GDF?ᄄ ɻ5@Mi8za$vE:0^ ;lѬ'ff rhq[E, b,1-΄0z;zYR[DAʼn5)E*CN(Mt^j ߁QP^Ҡ%iEhL}PQV}naSoAȟ Ѫn'N  vuNrF^cvNuڷʒm;5d-ۦCYW/e&v:q ۥ][EUE#HḺQ`ITl*jwL' = ܱER/E°C$QOi_!~>jH%[/,W2˖Wfi:A+ FC9^ JsoRx| v K$U/m-K*G-#Pe_H3}:LĺG8Q}h⁂Ut |ªgtMizˢlk'' S<[ƹ OhWG}ͧ48Ћ;)ٺSQussHrVv kEk<I2_54iu_~p5{Z-gV[eŀ[ 5g*Mɐ~ІBBFH9OJ9YT%H!vWeu&\xMɀ%UoPp賧i &D4˫akU\gXA+;j)U溰K~]hy+Ľ|,c^_HQE 04Is;?@n^ ĖD!VL+4(]m(JMg:'YVE<QE3KP!xjl !!O1clZ04Xf-WxeԈV-$'S? ;ѭubx)%G cQ'ỳKL>E4B )THN@LlarJ?{,b+~$!KRmhguHVxIMWf~s4@DJuuEóv%u נE uqLLMj؏\^ŋ00I@-kIׅ=˿-张]zI/g| + ZdgM5FIS1+.縚Cls\:wQ g:ՎDg}8Jeq\6\dK[ ~FW`vX:xzrcOKxF6X.F\e;ⵑߡ|?RO f1(ehe5nLwɔkP߫OnAXТ, ¯֞ X56@z6x0s1Q B-8ٶ 7,aaRA//$/M䟦,3i1^+N4ڙf އOSgnUs q)Cϣºr4[{ˬ~c.Qfx85;Mh&JKxaho LCb\FGҳ"`,6 ~} a{ .>427Sf-s+؅mg4M5BWMHv5HTB]&dHB>}݇o&< ǔi03)ʩv$|VFwZ陼9 y]==\?SPTWBX(tJr4"iIeTlIt@zB' W ;؋&Fsy{s- z>ѓ 5CԫEwY%L2,AE :`ER+RE%SΊd)DŽNo嚪+IVD"R-k3c *ET%Bug[f{m`~&vQphpR&-XZ@ BQ-Q\JCQl'-!-!t⎏T莦B L`2dL߂&cTT9 JEH()$|@}.0k |D5| bQ6Ih 6"Ȏe94y׎G?a"7v?nS* 7C | o@6θ c )uwK3V AXA r_I - 2gk [ũy +o݌ 4 _$T愈 qk}=5jڰxCR?ـȼ/HÎ~Xj.lt$ ji llCᥱfUܬw RR݁MМ/ ߔr7 Ln0=?Kl{'=F͜fD5 k%5 *ЏxEb}5ETT$M&(%W:$1M/yH7u)G/Y]%ϳ-NBPz_v/e:"~Or#6[A >vĄ[bs",G!`d1t;+B"=6Z_V`+ (F&@J,{lk9jq\`vl ,>p * ip#n`R1u2M~E4 ,|At쿝Mݐl.x|~wI_|= eS2e^} v+@ Q.lLO0LVDJ<%{~D>__7L:쓏j$a 'hj: 9Zb-A%AFt d#CcIcu2m:T,ULUA*t'>;05`Mail-DMARC-1.20240314/share/html/js/i18n/grid.locale-ua.js.gz000555000765000024 436614574361234 22237 0ustar00mattstaff000000000000 Sgrid.locale-ua.jsX[OY~6❑6d80Ɏl +!@}l7pc)2!h6˼UuN_|!bVk ӧNOw}gtF6JC0 V\ݴM݆YW=K'>Xa6;F5SzYԅrdh^hSneV .u͂nx+giNNV渵RVjZhzGf쵦54:ZJ[Qau7ĺ/lcXыC9CT=(r8#_8zϣ7l` 6'ZV8!=%.vvxF[^xtvHOr{;ĿhF4fmֈs9: }#&Z\ <:M:kV'zgR7mCDQy*<>g)Жm} G yF>zAQ#w'*Hq2Y~tAGAI5f%jY9ui-¤b.mޢ>ɲݱDȜ0˰pfkX˚hged}B-YHn(W]D{Ȳ-ʆP}-c3L Mkâ,ۚM`O>+h ZxSM}cؿ56Bެ[" xTO2u#Kp!2t+aI7q/mw|mQsT\={:{i&XB1Pe`nWE oQёRf`Zg9 Oh.bJ%pB!1J>ܽe6JscPD%Ô=vݔ<jq ̘!^!w ?>b \KX'Y&m,l)C z06@ȑK2Άis5$8 #R6 Rga̝ˢwY ,t2 EC7nIJA[$~DǔkCRGK|E/jlrP0xmL@Jq28c/8iOjҌSEDgOj[˕3umţ!5qFm -BCE8Vy~Hd{@rVwox`\I4 mGOS) ,lڵ[USѭoXqf.'l0bIj&e&:1Mo<`9:#omǘpHYר$0LP xBO-zFe#ΐ<ѱQ EA\nh8_WA!#*?%IOrvU yڣڌlB;IJ6~SE(wO2OjM)_̬eBҖzcxe냚۪8"9!WƓ|^o`6krL%L9\G`S;G_0 ܅G<l]iݯk8/L01^oF4pU<"Asc1T@d[%dCkhsM剧|P*V\Œ,$[Rx?E㹏/3k6 ; D)E(>ȝt_ JKȸq/,L3\w^9Y8Rn2|U Tu4+.NM{Y0p^{)w=zYg6=r(n XiPaF^k |/}XBtMail-DMARC-1.20240314/share/html/js/i18n/grid.locale-vi.js.gz000555000765000024 515414574361234 22244 0ustar00mattstaff000000000000 Sgrid.locale-vi.jsY_o>$tiӸA$YdZ\[M>Nܬ(*A[Uk`õ=1@c_*[?s%y`s'JkF/G)#+/ӉsLL r$LoCcf{GՔؘb/v j.+J'Is*O@AP--34(8J;;X'S@}R1 Pg;q-'y{Rd-E7?C)%6G2`T7K{ #HX BUȣj64v2S^KE}ٓRT!ڂbchm-4\fn AQ95 A03 K}겲\`&av/%XB ^%ҰRfH@=n*BšYxv1aRYl#>(`(>F'ZmJ 2{!!A}5yhL4 v'B# vtOri|2[wV``wL՜P5̬ FGxb.X/(yꨴ [ۗr}r/yS z1Ivc^*/< AoC [7C@GiBAJ[H[<53(fN"I";EO' K6Zߣ]fV԰7, E͆8$ ҆f)m4GIZ XއCv.w|f&ߌZYw'2LkoR] :ݔ%2DX'QdD:$+,(ƹ Vx8x,-b$ķ?(,8oEqb)Wicʴg 4*m;imN˙8 *FR4!'Cـ}WQšni$4< ֱ *uk|XibtC;(*5j&S&mq̄^@iC3mzL4߻ʙMئrV6z?QϷmS*@|Zjy1hŻ|矆(NGW!=}FBFUZU_ke}?蟟Ƽ5r Uũxhv05*,S4ͨl+f'ejϾq>>3_>Oz[ Cv%:ܪm#{vn?٪hfqQd ؅wѠ.+p ]¥gү^_iffYx52{U!*Ew9wv0֮uWOc$e;;;VWWݳLǕ55eYy` mځńY=ධ~sw<Nj\&}J(u#'OYU#nY^XliI4Qf{ K?m)6-e#)(lbqP/c^$ȳ,MZ+)~x^@kׯ 6.+x^n까zy۝U,6~e?C|uyZV ʪ󤕆615z ZdE$IXGe7Y17)>,*.Z5%Q< zpߕkBA,Dm,b 2/g@.Gw_8@,?B4ĥ>?Ȥ,Gx\D "fˤ-ko-IBȣQ%.đ3h4(S72Wi,7yjPiZr(1N5jmu{'BRUlMx5 3uN Ӭť  y08=N6 sbEU,!?-j]@tSc p<|2Xÿ!K1 һڂr$zrvZOpnӹã8 ` Ԑ4:GhyڻJ <^n֠7RM-WiU:(p00{#1L@!MPaB1Pj atO@|d(Da$M_#U:&M 0[,BCek %~Vs)#$T6#24BdĞ̆=k\S6sWנZɂ--M+;OuYzY21hNr Hآ|z ,U ^J:hٴX(ޮп%X8xyɺFY C˚rcÛ'w |Vyl)M= 2;*&Q^h r}W`3ᓣ6FЊAL8DYVqEzs4.rRU61m<ľ;VFtDqYv1}#ISB)0+1"B>,C6~t TQYLV@/P-h@guEN3_R!++5h QګA!۲? J,]3r0pc_A~XV,*8W>*p%&eCQ oM|£ps8qyPOALܰ(_Qwxc~&cxez|O#Vtq1A݃x*^K je]nU0 `(7l}M«[pE6eN|4lL<jTi<"nhUp(*4"2hi`~ԣVyĎ]F> xo64VR}Pf=ZڃȑxsY,[0$f> ڃ!D2buͪ$ç# 322H-iĘ)0s:pNH,J7i9)d[ey@]EL%/".xE9k|>E->G0" YLiɿy`&xj`h1`}?;-Ĭ,YN;IprO` i߂|wgX-I-k@V'ZᑤsKUhQBgeˤ#=H193?\؅(̋ &>.KL5.At.B"EOf6ٳY`Tg?48^  _ހ} : ʛ yNΟg΃5wYQm648ҸW/~y҂06±V*vGd5Ի5%~2ed>U+4=qin{ʉl]VtQQ|a]xBAF* j`"dؔ6C\銦*"Q%wqq;yHJewX ar 2/},lwIT7;JD&,+y| +Teե\ѯj?E(_! }qy?lO\Ycq 9mLmXX_k12Bgݚ]$y!zHMr1C۸3iɖ2Jo>zPվ\_3 C1O z.ךɁ|xũ 嘁]bclXt '/!Ť> bB' 5Ŏ}EF 'sW_YrҴ) 4FkVB[UtyxA'7q:.5؇EhJ+ATyֆ9fq{Pc2iX`r7cO0m1Y1w.Wj\9Oߝ+<)Pn@I^=Jkwoh?|hbЇ.33:#Z#}BOBHampаFNO\mI߂^bT4ۊ,uE],F\_T1k/`Ԩh^< UTZ6m?%/Pe8A0^P= kԝ"] W E54Om`!7pw%l:-"D3@dTuELn:f(43i H=q dN3 vb9kxiZ;,W-eiklу` ˰ ]V/粛ĴҠב@G܏F +M1h_`uB$>'J'Q*6בXcbN=6׈._rsBU'Iȫ.*wdU6S "^?PWI$fׯot*_{ :Dq@hd!/.1_JJ.2i.m0\(,ar1ǁ*K@zkNĊ6UYfy[Kp 6+0tv]/1Ĺ+i_9s`h`VPhحSv^`{~ͧRZ'\o, '>=qi}a|Tj0}ܥ.jh@ MbpMx8љ*#OhORtO|laq$v-,<߄wN͍v`A[6-7 |zsTWl%H\7egj`kSs gu]u \$9:S~gj :Uq*oO ćYhf'Cþxqj< |m n[mIߴrbK˕:@7%M[殼X[Ѡc f`Ot\RvS͎phaFnx qsCn͉B^&-J./U$nS)؋;ȹJB% "\;2ɷ$Wy|| 2߬N$ 2 \~cP/Dm2,oW1̵ ;oF}-VU)K}@.9sTW cdrj.=KżPsE~62\&:qV8<GyU,SΨ|5. ^PA>[XQxfw -w(0@ 9pWύy?}Y3i MqW d&dB~lj!Rw*UYg`o{yGIPL"Ss"0R7m$7oږKz6y%ȲCyC8$IKkg =C9ܙW/NaNj=WC\=$:oxV2uk+~@hj,Zwi)S , ZR ORl@E^mRc^_g|rR.5_wOvK&Ck8LCq zpx\IS2yj|-Z8vd,uڜѧ>Ag;39Q5P U>4m!PV0FyxYIF;5,6aHdj!FLӁ":ilgG_{vY #^h4d:r]#^LQ[o>D<ngJ5=hlHW|WZCΚ 9ykEwDiyaP ꭸCՀpzDQPPwGF-xn4ZC9D1ik}pSCDJ/Q:j*v2f(< O,dw@o>=>{I:1 hˍ n.W.ӹI;CFHHmfh6?emJ)kq!ܢ^/@dr?V=dW) 6-o$XWAL`Oz֪tv[Oɸn[?ٺ߃c>PU6Kx &=~q!q᭛9Xu- ̩8¬ƾz3TYo.\v~墪 ?.PC\8Tӽ؜2JV)3>K-1}#Box-Ա)*|o$2!Ը-Nl ؃ (vrFnKvFN5^lTVjƉb*7rjE?S}JtKW;5rtܞY*KE>q+Zad(&vbl~{fBJ'"p @k0êx$ X|P:0 1o##O&~wb?&XH0Nƿ_Kn\r:=]ܘg` 91,7?ǝigI9}>+zo[Qaցm}?5丁}_i@yMTuINxJsrk\{"kK۸BSblpE9Y iRq:qL "\7x|F]oDI>? MagDzBN 7yY;nv8W] T1}E"dcٵ8SĈo_; :BYlU`:FSЩhq@gH8r[a}(f:^*`#d3HY(;M㎇$f=tβx97&xNQpeqgsǰLi8a%}ܞ8Nx`|s؄<_9uggqJ#|҇0VCB"w/[_7S慾S_ T{ <`v-qߛ db;0"=-N B8qA<{nXt%Ws֫n뿾B:7-Q7T JBmCk<YM܏cOՐ,P|` Cl 8ze~6ϤSx4Eg 7% ړ<|]`%.q6}ӳ???3898GeՉQx0"-OYghvjKtJdv:؋ĝʄr%i2Q4Cw:?q>涀ܠC aqI%PZ^@'X)PeKwOݳYZq]LU.N)..sT!>{v2*8b۔mS7=J-' Mi<v\Ô`ӲIݔXC h$1p5bUKf0@b-c;K4!0y|s5&H&ha!V+V߶x50iѾ \}Z$\JERz/,ؙq`Wa['?q[u< /vOX{:{C>:'WMOV?[{eEHA}miOI]Nu! _,} ؄H}HMail-DMARC-1.20240314/share/html/plugins/grid.setcolumns.js.gz000555000765000024 356114574361234 23053 0ustar00mattstaff000000000000Ngrid.setcolumns.jsXYs6~Vf6D ZN;dIӇēHH qS.d{~{@7/H,>}R'E0$,IDb4!3Ab47fkGʢK\|Rb2]]]5rUO 4TB3 i /"MJghŨ:7Z25,?k_>< Aჽb. Kb}˩:)0ڏM ^x2'\W,VwB"AK v/&l_f19-OJE=>6ɮ~rir76IrS"Ж6,<~` : ]||ndEYxgX8#)Poc_:-6Yz?uS+F::k;B-$eI#ITQ4a= -(u5MRU(iϷY { +=6ў 4G@teºׂfsA70N$ԘZj=`de]{Y!թN,%Ռ㞃x6>W9>cIbBy;6\+KMp: XxTF[ճ]5 +wv't>.ֽ٘:-j[Nfk`"oD`}PMzC)%ScG8 a:ZWּR-nxv 4fO7{ʒ,bBpGk*\{2oHr&,,Nn>Mtd&F'.>L>7{I]U>PqV6˥C9~6m/8D")IE \ <F敬$Tqr՗D+cM]N;jJ몈H,u^+~mw-AJb-><6+xf?πi:!wh՟7ظfD xC4NllՑ%`7 fe8,i:Ve  Rw&8jVAT`QZSz4=zv9ʳ?Op s =kbXWx3,zKl+]9Klij8 k30 m 9vP=!um Plc`K'F63cN?߾aG}{V, %"0оZ َ$oUەYN%mSv5v\}}[B ko}o(-MC#︨ 2܊:of:둥-+qtmahl&i-'kD/^n԰ tl/k*B7,H9z5w}9y=tcMBŜ< HhIR*eD4:H?SG7?bnF150 W6jшऩ֚8! !G&T0ԏBλhP)ѧv+ iJOSD0)peU"o3Ssj`b+.sR㺯YzT>N݁|8%)B]hoM-);51 ArZk :қb"4cg|oeϧ_fZ ֦J.*fޑ'ՠdcWC*_ӖpBWn(E=.^ߖOثsWI+H&\Im{8 ɗכ/NȘI3д[BYRKLIN,5>tj6Flho7 m&C2mͦ 0e5 XU<ZK]S~n[.Rƈ뢗uc^ I/^XWz0iai;tK"GO[`M"kQs3lT6Ԩ_})dMail-DMARC-1.20240314/share/html/plugins/jquery.searchFilter.js.gz000555000765000024 2051314574361234 23700 0ustar00mattstaff000000000000Njquery.searchFilter.js=ksF/UIejW'ˎrPTRCA_w e]eQ%3=Fy-pI~<cvKF$|."\]=dztv175=  tC6[EF̧>ipTqq|t9=pzzrR]d,Y,MU0N7+A-s$'368-/8ql˗ nb3Ke$fk[ǩϓ\ '11U$'Q(C`f 4!)_ہ;n&'a(E:$[,N*@Ŵ#H~%0yheg{|d09o' f*>fG@8fE*!XNػ&"Im6{ہ̟P: iTڃ1J^%hW f*G, (3E?fgc z|e0;7D uV M=Kx 8`}yb~r@%CT6Wji s  B#’~M;c0_lh#߽q_E~;!eϤctT #IA>☺ me.̆g 1+:yXDZAہX@‡mbo, rRn} %.()Ls`ȔcXR]ηP{{@5#]NOi~1L^dfwќ !!yr+3p/*c4>/`*'Ԙ"ז4`dAmhl{_QSPϮQv|o[mn`pnU,?zy<^ɧ`4vme6_@^ȣ?M~7QΘ6m*\p ?`:,'Ē8 %0d!NPEvtpN>ިj ( НT RdRϚ]{itp`C>W| ߀NeQ "*P1fCdmk- &V]4AOqn}# p፸慇RlB񱺂qe,HńfvLm%itdPSҌ[8 *jBYJM&`T[=B?XSi J:lRCĮ6]4~w00B5> Ǽ>@T3H غob0Js9x?@r RJa=} * l>/A5؏*%h7VrҘ]bW_e|I1r dɫne]u}d/Iާ=eP~99bN/E + g1kܫth :DU{)xqyzY``e=IefOA0Vs,AXvAIR ^K,=i(]wj "DODLW$ =7`;8 2j_W^22L :M/fgS 3d?2d A-'|##qEHbb$ZABFIȂGGZ OFu7j.`B"?p!OeO~=E aA1 M#Q3ơ>43 BtA*?KøK$}Sri0\9 y3Lc],ϐ Ǥ-'oslF%=WEJ˫_tL&\%YÎSG#s$w96ZWky+`Xt:fO#ߤ9byN>~Z<IyQq@J|`S?%n\F&!+ 4i@\oQXh),=dn3m0ihmtP%PƟ%<včFI`7&)lEcƘuc.DzW~j.I 0\½g@>Gb-gO뽧{뺡k8JRwZ#܎i|o5UAxW's$jm.`JVz Zjaߕᕶ] ?ːfՌ̆>^tbo/{*ri!1$\0jSiu%Y:}/O[2|M[B:FJgL˜:s/|l@][3ʉCotP zݏMqO^67A[-sOm;hTDl 2`~*֍jq0|`ܳ5w4ڃ57 x-cyRe&X;qRS~pVkj{@Oc/anx2X&6tF${5_6orFav1ysI÷qHk'U^ %VȑZCOjyɾPcGEwa+2a<.fjU;YqqΝ.5dO- 8Jۆ`LԤLF 5M-'drEJ fV?a 玷ILn90,= &ħi[dɵJl o:ԢutkyrR3_[j_ģcVf+pjKSڧ#c޿*Y#MԎ9O:kکX+(:9i$„ZNyx-E*䦩whvi#4[8Jb-|˰$fJSI!q[w]ҦO 逗>$+9.)xtIܙ2KDxj)ƘܵQռ'7쌲2yai)zORJM+Oﮈ{#J,+ :P~O¡_7yjUC ٪8R'X:R%{w(w|z_dWoHE0o= /!T^FK+ETyUͼᇝoXwUSZkp[5(^~-:(Ebbq |D&Ę{[6MS)n?xJXdx(/!{)=+ܜ%y<%HXh.jZtU&\r^R5:8j`x2h%[pp{G^@f)'3&g/ϚdgtY8}E: G[^^ۤ0K-?/!_˵7 אifn!xpG;Sٷ#TT٫bR}jsvzΦQH ޻+б~;Q晓ҵL^2yrvwMSsF=2šO5=Eh#IeT$(Aj [9e:v젭w0-ݪFew^ǵVzCU ` /h^GЈ:')ޢQ:Q &^nQy[: IFZL++#?XAډ`A5Eե$jҕc-.]kAL{Xec9+ڲ as Kr27rmFBljwM_Q6бH;ͻ,h}|c)bX7|EׯMrҐ3W0o8Iv$,0+LXs "`r8}\7;ʒeג8>ﱒO; [nF7oFa 0@ITgw|IlEQ#a:YŸFZyt j[fttl=$Ih+IvLkvGM|2gf|WқnW/j1 籅Д< VsJ]aa PkxQ$NVȭL$ʌ8"ccD̞=)]HMngԂQs5ph\tN;D4X .^(U䷵g/هE`L4-@5s(Szk;oyDAͲ)Wl"f!;̞E*ŹzTTq~#W}WM{ 7x03!$Jw* Ck.XA2Ņt4ܮ,C6. tr {_`x,Dqv^jOXuq-CluD[/fv c4Q )Gb`, o:LH 73.svnౘh,Ȩt?C,l6c.Wt/ἄnIlO7rvRJZ{edL=C^<|3JI8 +q5z)51VٵB:Nw'7_$D'i^a13),҃ =GT35R"O-`͗2Y&g^<"z=ar[7le(zAhZe|$W3ՃB*w F٩؏a9s߆PSTV{ a]ۇ!b;m"N&,=.Ջ>ЩG j'zBΝUS}iVB=cy=}l@^ʗg2~μJ8**j]& vy`SC3& c.+Ku5Ǚ?=Eeo?0M6 h. 2>W߶U: r5XUP(!y3ia/ǩ!l*&^a&1'w aN˂bZo/npM9/ɭQ,*¸jtM(}H8Zin<h- b7) {#mN&\[V䔚<_X՟1%XO9!&` l6Jǀx0 y-.˨X(W^Wt`Ş]rʸ)n=3h F:D\F$wlIy hlp^ݲ޼*%t(m`<="&=w9E~~6ˋ/mYEqj1M=ow!f0^z@#mב;yxa =O?#r!V!}v LsfsˍB+82H]a$2Dbn`.{g0=owKPGw#Fu2(yoyJNx/楉. >6|-̗MekUX|Umq'Qޕ-9K޻4') 8]2V;O\{Ogujy;jmN{߽o ._{wס yAVe4$N羻j:Z|ّHS2bd@~2SZØ}HȘIFT N hb5&$pSA-'ט{J-WҼlۖ\QYe( #0))m$k+_-4.41h2JMail-DMARC-1.20240314/share/html/plugins/jquery.tablednd.js.gz000555000765000024 1205214574361234 23041 0ustar00mattstaff000000000000Njquery.tablednd.js[sGv̭r izMAܵ%TVRTc4A x}^ؠl t~;q'rRlz5әXתOL|W}^*I!WBf ɷ‰D=e&JUz+ndyV8Ϫ"O:߉jD/+;bEz8KR|RUUi<.dg4{TH'%>Kĺ$8LpY5[U]HdU_oG['NV,Kʀ4>.t"F0UJH?]I'"Gdb(Ѕ_籖j-$u YM02E^$v lOŢ>+: 'ܦrI$׹hH&RQ E D܌2ȅn=ʡܪX/5p+K[1vkTn&G]YRDNn23iԭYGIm `d)h`]&B0>#MnW8\}q*Ҕxl|k0J(@q'=!ҠGe y8tHCP`DRs h&ϴ 35AYI|LfB<(?pk ` N]ǸQAkOs "=|Mʋ큍̪$V(\DK;l)h6~u1qvY҃;8݆U5Dm!IT m*8'q^%+6 - !nL57!n8'!c R uf ?dJyL9K̩GB8GFgsqvz/ӳ٩x mHu,nxPk~(w险W~}L̢.$Q,I?Xn}X\[d9t ,Dl30.3 v. "A /!%I0W7|Q%+,FCk {_biIﱕW8Y06Ym}RTዋssp([QI[OA%q @ǪN%`-%"To)╉aas u\|um~\ OuAXczb`d{#59i:IʤG#T2&">zMO:8qΗKU;|Mu&6sȴl2$jBvbTޮ)PUM֩0[PHꁈkR/$p3v 1h4ijHcIJ %T 3ְe N} [$}Crc{Lrݏ!#N Bs1jHFKD1a džw%?\|)LuzCEe٢WPTl*T 9Ӳ_]v ZiS4kBpLYDed]s}uwI_p\HtV #ti:-pV\h}%nrK.{p+$@'fPU;EY!X,"dêXbvHbCI1in8s<>;0M!]ro O֐.֥m\ \chCqbBH5l,>q_kҳ6<YT M{. ={y!d5#p aӝ 6%fȇ,܎GUݱ9GD/Ћ&a F]X0lDM'N 9Pc@ Wy܏D#E1 =]?;}Ⱦj n J8΅5fؘ0cn6nJ-ҞƘn'ϭPIu{2E0o j?E ?ˠœ_=԰jd ;3lWtPخ֮W9 0:.qd<7EDPvSB2 />vp|",?+L  OםLp{~8g_햃o! ©⅝25Ksnhs%xv'^ObkHt&c2 Zc#wX,Y KK IT@Rpjpx7LsV0J܎}6L^;WS x$l-dq%ZVbzSQ{ +-4v#`%(t3u0΁Qt3#ۜh.GURlRSBjO! P& 5n95ٛO&7bovq qH̔ś}f=[1`d%a؋UczWq/2A/I %l7zS)m\e>K ۞-OԻ+`3=[[sOșkc>`'TGn U%51͛c2V`w.-|urvzIu' V1 قJ(yZf{o+zȌLb$jd{aZY.U+l+@. % O!S\h#}}VqkAReZcgJI6WRx ^&d6ausԮUr#gxO/P԰#ry4CZxDQB@oG,0E Fa >ݱK6hyBQ0HьJϩif`=Bm&˂U vpKd8=h$=ڇPzwSYuLJ]noe.%d.CFG~ &0xhȟdaL~h.U@yp.Rg*9kC^@{A_w {7۾$%<}V[l1p5 o3 ~#m~b?>Y ˁo)2G bJ6Z3Y |RG~潹-?lA'1ʬGbmoG gݶЫ dNm'wf1X晛iQu .9 ϳ=s?qwُtpUrM~F+u qX5Xq{.굵++sinADq+;{VV,X3ᛇ]Uc@J36[ݶJ#kب~}0<_V۸NN+ݾ}.E6ge /`= (^Е4X1 ݽ ؽQލ+[ݬI߱s Z 2ca}Z(4XXb:HWٜcAi|Gl%;K@c`1ƹOlUG5z=0IFN3Eb~ G7JYGΤ]dx2h3Ty o2۹>"[>6 ϊMf~X+1@3?Aυ'OIl^&C~\)@;tu3̺dI›5`\+'=gJAMJɅFwGn7|:EMW{H|}8X*W8;_!h^c˭.Fי*^b83:3K yoGBA!VgxV6w5hu/ț_D`~niQC)8ObR%1Gi#;QY)elB_j'0b: +| [U\ޙ4Nr\w$k{ln4i`ξ}Nit :]jUwӻ+> vh_e:L2YcmQ^cuFÔYGB;Uӯb \i=4gM ~W/($IG+SݓIks$4x1 k:X_z@,ɤK{ݛ VCG C)*褻Y؇v(x J?sxtm!97 sq~kw?9t{t?2a>`!u4?ޒh̸ux[x 8][\.k8s^dk!3uc u(SlG ' &Go}9٦vmmɔW\D)OX8=hyBJYLU%9Äߐ8UuN|MYVrgK~CvH {g #{@ +8^,I\%>Z(먚vdmh"Do4 ܬ&ʖ7ƄdD#L3Eo  ۝J]lGD1XгNpNpLJ%[M0Lotr EzR3>fr:fv7(o%E>}ϗ$$=!B 5az˛RC =8cy|,P&J 38/X7Z*ѤkFS;dCP?ha(iwZMq@o̖ 1*( ?}58YyR !%[iM`p AwmOਗ1]mmo9ܕ՟¢OL";*4c Q xL?d@<((jIƄ՛0u(vt69jEv%[q>w X7ޭDCЊ YL˵W `d(:o[Zy]n6(2csI<̏Uwԭá5+9] #sB]ɬ} N`䡦[Xùc Ij#x;^ S1xE +OT>b<@F5m}>t# ɖX܃Lɝ% k Z5Gž?`VJ `춑 1-|ĺ%kEѯNكV,POuU9ݶz.ebx uX7[>XIlx~!$Pe,_ɴM۝dفxsOw?.ĩ(>+v],& ʰ@@\ 7 0(yٻl rpצ|o ۘӜV|2O~fA,Vp gjK>=3h >G{w;8Y*h$i^ sZby>~}5oغmӻ>D$pI {45v5"9yTrSN=0SF`CBy$KJgw)c߈^3(>K&iN>J>쿊K^`e3ƛ08i~td1IAB3SS5q󫲩AgBΝ#)Ć'm3dۑ,:ԽuBĭy;zq# MPM6x{N۬=6@Pu %LAY4XivMa&1$'IET>`o U7x+?Q2LS7˜yGOM+T7K*ͭbk{9lC/*p' aZSLMۛ.QvnGXRuM(5.,*as _!f ԋe]ŵu14fExYճhV=vlBԙUDʒ' ΠM]1@BG1.υ<0'џՄ_8;V{3|}"+{XQIf,l?2+_CY6=N݌4b?j e/TtOG}O?~4Q)Mail-DMARC-1.20240314/t000755000765000024 014574361234 13325 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/t/00.Dmarc.t000444000765000024 2530714574361234 15142 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use Test::File::ShareDir -share => { -dist => { 'Mail-DMARC' => 'share' } }; use lib 'lib'; use_ok('Mail::DMARC'); my $dmarc = Mail::DMARC->new(); isa_ok( $dmarc, 'Mail::DMARC' ); my %sample_dmarc = ( config_file => 'mail-dmarc.ini', source_ip => '192.0.1.1', envelope_to => 'example.com', envelope_from => 'cars4you.info', header_from => 'yahoo.com', dkim => [ { domain => 'example.com', selector => 'apr2015', result => 'fail', human_result => 'fail (body has been altered)', } ], spf => [ { domain => 'example.com', scope => 'mfrom', result => 'pass', } ], ); test_new(); test_config_file_first(); test_header_from(); test_setter_values(); test_spf(); test_dkim(); test_zulu_time(); test_report_window(); test_interval_limits(); test_public_suffix_list(); done_testing(); exit; sub test_public_suffix_list { my $data = { 'com' => 1, 'examplebogusdomainname' => 0, 'xn--55qx5d.cn' => 1, 'xn--zfr164b' => 1, }; foreach my $domain ( keys %$data ) { my $result = $dmarc->is_public_suffix( $domain ); is( $result, $data->{ $domain }, "Public Suffix: $domain" ); } } sub test_zulu_time { my $data = [ [ 'Day Start' , 1426032000, 1426032000 ], [ 'Day Middle' , 1426032050, 1426032000 ], [ 'Day End' , 1426118399, 1426032000 ], [ 'Day Next' , 1426118400, 1426118400 ], ]; foreach my $test ( @$data ) { my ( $name, $now, $begin ) = @$test; my $c_begin = $dmarc->get_start_of_zulu_day( $now ); is( $c_begin, $begin, "Zulu Day: $name" ); } } sub test_report_window { my $data = [ [ 'Daily Start' , 86400, 1426032000, 1426032000, 1426118399 ], [ 'Daily Middle' , 86400, 1426032050, 1426032000, 1426118399 ], [ 'Daily End' , 86400, 1426118399, 1426032000, 1426118399 ], [ 'Hourly First Start' , 3600, 1426032000, 1426032000, 1426035599 ], [ 'Hourly First Middle' , 3600, 1426032050, 1426032000, 1426035599 ], [ 'Hourly First End' , 3600, 1426035599, 1426032000, 1426035599 ], [ 'Hourly Second Start' , 3600, 1426035600, 1426035600, 1426039199 ], [ 'Hourly Second Middle' , 3600, 1426035650, 1426035600, 1426039199 ], [ 'Hourly Second End' , 3600, 1426039199, 1426035600, 1426039199 ], [ 'Irregular Interval' , 3604, 1426034321, 1426034321, 1426037924 ], ]; foreach my $test ( @$data ) { my ( $name, $interval, $now, $begin, $end ) = @$test; my ( $c_begin, $c_end ) = $dmarc->get_report_window( $interval, $now ); is_deeply( [ $c_begin, $c_end ] , [ $begin, $end ] , "Report Window: $name" ); } } sub test_interval_limits { my $data = [ [ 'Below Limit' , 3600, 86400, 3000, 3600 ], [ 'Lower Limit' , 3600, 86400, 3600, 3600 ], [ 'Between Limits' , 3600, 86400, 30000, 30000 ], [ 'Upper Limit' , 3600, 86400, 86400, 86400 ], [ 'Above Limit' , 3600, 86400, 87000, 86400 ], ]; my $now = 1426035650; foreach my $test ( @$data ) { my ( $name, $min, $max, $interval, $expect ) = @$test; $dmarc->config->{'report_sending'}->{'min_interval'} = $min; $dmarc->config->{'report_sending'}->{'max_interval'} = $max; my ( $c_begin, $c_end ) = $dmarc->get_report_window( $interval, $now ); is_deeply( ( $c_end - $c_begin ), $expect - 1, "Interval Limit: $name" ); } delete $dmarc->config->{'report_sending'}->{'min_interval'}; delete $dmarc->config->{'report_sending'}->{'max_interval'}; } sub test_dkim { # set DKIM with key=>val pairs $dmarc->{dkim} = undef; my %test_dkim1 = ( domain => 'a.c', result => 'fail', selector => undef, human_result => undef ); my %test_dkim2 = ( domain => 'a.b.c', result => 'pass' ); ok( $dmarc->dkim(%test_dkim1), "dkim, hash set" ); is_deeply($dmarc->dkim, [ \%test_dkim1 ], "dkim, hash set result"); # set with a hashref $dmarc->{dkim} = undef; ok( $dmarc->dkim(\%test_dkim1), "dkim, hashref set" ); is_deeply($dmarc->dkim, [ \%test_dkim1 ], "dkim, hashref set, result"); # set with an arrayref $dmarc->{dkim} = undef; ok( $dmarc->dkim([ \%test_dkim1 ]), "dkim, arrayref set" ); is_deeply($dmarc->dkim, [ \%test_dkim1 ], "dkim, arrayref set result"); # set with arrayref, two values $dmarc->{dkim} = undef; ok( $dmarc->dkim([ \%test_dkim1, \%test_dkim2 ]), "dkim, arrayref set" ); is_deeply($dmarc->dkim, [ \%test_dkim1, \%test_dkim2 ], "dkim, arrayref set result"); # set with hashes, iterative $dmarc->{dkim} = undef; ok( $dmarc->dkim(%test_dkim1), "dkim, hash set 1" ); ok( $dmarc->dkim(%test_dkim2), "dkim, hash set 2" ); is_deeply($dmarc->dkim, [ \%test_dkim1, \%test_dkim2 ], "dkim, iterative hashes"); # set with a Mail::DKIM::Verifier $dmarc->{dkim} = undef; my $dkv = Mail::DKIM::Verifier->new( %test_dkim1 ); $dmarc->dkim( $dkv ); is_deeply( $dmarc->dkim, [ \%test_dkim1 ], "dkim, as Mail::DKIM::Verifier"); # set with a callback $dmarc->{dkim} = undef; my $counter = 0; my $callback = sub { $counter++; [ \%test_dkim1 ] }; ok( $dmarc->dkim($callback), "dkim, arrayref set" ); is($counter, 0, "callback not yet called"); is_deeply($dmarc->dkim, [ \%test_dkim1 ], "dkim, callback-derived result"); is_deeply($dmarc->dkim, [ \%test_dkim1 ], "dkim, callback-cached result"); is($counter, 1, "callback exactly once"); # set DKIM with invalid key=>val pairs eval { $dmarc->dkim( dom => 'foo', 'blah' ) }; chomp $@; ok( $@, "dkim, neg, $@" ); eval { $dmarc->dkim( { domain => 'foo.com', result => 'non-existent' } ) }; chomp $@; ok( $@, "dkim, neg, $@" ); } sub test_spf { # set SPF with key=>val pairs $dmarc->init; my %test_spf = ( domain => 'a.c', scope => 'mfrom', result => 'fail' ); ok( $dmarc->spf(%test_spf), "spf, hash set" ); is_deeply($dmarc->spf, [ \%test_spf ], "spf, hash set result"); # set with a hashref $dmarc->init; ok( $dmarc->spf(\%test_spf), "spf, hashref set" ); is_deeply($dmarc->spf, [ \%test_spf ], "spf, hashref set, result"); # set with an arrayref $dmarc->init; ok( $dmarc->spf([ \%test_spf ]), "spf, arrayref set" ); is_deeply($dmarc->spf, [ \%test_spf ], "spf, arrayref set result"); # set with arrayref, two values $dmarc->init; ok( $dmarc->spf([ \%test_spf, \%test_spf ]), "spf, arrayref set" ); is_deeply($dmarc->spf, [ \%test_spf, \%test_spf ], "spf, arrayref set result"); # set with a callback $dmarc->init; my $counter = 0; my $callback = sub { $counter++; [ \%test_spf ] }; ok( $dmarc->spf($callback), "spf, callback set" ); is($counter, 0, "callback not yet called"); is_deeply($dmarc->spf, [ \%test_spf ], "spf, callback-derived result"); is_deeply($dmarc->spf, [ \%test_spf ], "spf, callback-cached result"); is($counter, 1, "callback exactly once"); # set SPF with invalid key=>val pairs eval { $dmarc->spf( dom => 'foo', 'blah' ) }; chomp $@; ok( $@, "spf, neg, $@" ); } sub test_header_from { my @good_vals = (qw/ spam-example.com bar.com /); foreach my $k (@good_vals) { ok( $dmarc->header_from($k), "header_from, $k" ); } my @bad_vals = (qw/ a.b a@b.c f*ct.org /); foreach my $k (@bad_vals) { eval { $dmarc->header_from($k); }; chomp $@; ok( $@, "header_from, $k, $@" ); } } sub test_setter_values { my %good_vals = ( source_ip => [qw/ 0.0.0.0 1.1.1.1 255.255.255.255 /], envelope_to => [qw/ example.com /], envelope_from => [qw/ example.com /], header_from => [qw/ spam-example.com /], dkim => [ $sample_dmarc{dkim} ], spf => [ $sample_dmarc{spf} ], ); foreach my $k ( keys %good_vals ) { foreach my $t ( @{ $good_vals{$k} } ) { ok( defined $dmarc->$k($t), "$k, $t" ); } } my %bad_vals = ( source_ip => [qw/ 0.257.0.25 255.255.255.256 /], envelope_to => [qw/ 3.a /], envelope_from => [qw/ /], header_from => [qw/ /], dkim => [qw/ /], spf => [qw/ /], ); foreach my $k ( keys %bad_vals ) { foreach my $t ( @{ $bad_vals{$k} } ) { eval { $dmarc->$k($t); }; ok( $@, "neg, $k, $t" ) or diag $dmarc->$k($t); } } } sub test_new { # empty policy my $dmarc = Mail::DMARC->new(); isa_ok( $dmarc, 'Mail::DMARC' ); my $expected = { config_file => 'mail-dmarc.ini' }; is_deeply( $dmarc, $expected, "new, empty" ); # new, one shot request $dmarc = cleanup_obj( Mail::DMARC->new(%sample_dmarc) ); isa_ok( $dmarc, 'Mail::DMARC' ); is_deeply( $dmarc, \%sample_dmarc, "new, one shot" ); # new, individual accessors $dmarc = Mail::DMARC->new(); foreach my $key ( keys %sample_dmarc ) { next if grep {/$key/} qw/ config config_file public_suffixes /; my $val = $sample_dmarc{$key}; $dmarc->$key( $val ) or diag "error running $key with $val arg: $@"; } $dmarc = cleanup_obj($dmarc); is_deeply($dmarc, \%sample_dmarc, "new, individual accessors" ); } sub test_config_file_first { # config file loaded before any other attr initialization my $new_dmarc = Mail::DMARC::Testing->new( config_file => 't/mail-dmarc.ini', assert_ok => 1, ); }; sub cleanup_obj { my $obj = shift; foreach my $k ( qw/ config public_suffixes dkim_ar spf_ar / ) { delete $obj->{$k}; } return $obj; } package Mail::DKIM::Verifier; sub new { my ($class, %args) = @_; my $self = bless { signatures => [] }, $class; $self->signatures(%args); return $self; } sub signatures { my $self = shift; return shift @{ $self->{signatures}} if 0 == scalar @_; push @{ $self->{signatures} }, Mail::DKIM::Signature->new(@_); $self->{signatures}; } 1; package Mail::DKIM::Signature; sub new { my $class = shift; return bless { @_ }, $class; }; sub result { return $_[0]->{result}; } sub domain { return $_[0]->{domain}; } sub selector { return $_[0]->{selector}; } sub result_detail { return $_[0]->{result_detail} || $_[0]->{human_result}; } 1; package Mail::DMARC::Testing; use parent 'Mail::DMARC'; sub assert_ok { my ($self) = @_; Test::More::is( $self->config->{organization}{domain}, 'example-test.com', 'config file is initialized before assert_ok', ); } 1; Mail-DMARC-1.20240314/t/01.Policy.t000444000765000024 3224514574361234 15353 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use Test::Output; use lib 'lib'; use_ok('Mail::DMARC::Policy'); my $pol = Mail::DMARC::Policy->new(); isa_ok( $pol, 'Mail::DMARC::Policy' ); ok( !$pol->v, "policy, version, neg" ); ok( $pol->v('DMARC1'), "policy, set" ); cmp_ok( $pol->v, 'eq', 'DMARC1', "policy, version, pos" ); my $expected_parse_warning = __expected_parse_warning(); test_new(); test_is_valid_p(); test_is_valid_rf(); stderr_is { test_parse() } $expected_parse_warning, 'STDERR yields parse warnings'; test_setter_values(); test_apply_defaults(); test_is_valid(); handles_common_record_errors(); done_testing(); exit; sub __expected_parse_warning { return <<'EO_PARSE_WARN' invalid DMARC record, please post this message to https://github.com/msimerson/mail-dmarc/issues/39 v=DMARC1;p=reject;rua=mailto:dmarc-feedback@theartfarm.com;pct=;ruf=mailto:dmarc-feedback@theartfarm.com invalid DMARC record, please post this message to https://github.com/msimerson/mail-dmarc/issues/39 domain=tnpi.net;v=DMARC1;p=reject;rua=mailto:dmarc-feedback@theartfarm.com;pct=;ruf=mailto:dmarc-feedback@theartfarm.com EO_PARSE_WARN ; } sub test_apply_defaults { # empty policy my $pol = Mail::DMARC::Policy->new(); isa_ok( $pol, 'Mail::DMARC::Policy' ); is_deeply( $pol, {}, "new, empty policy" ); # default policy $pol = Mail::DMARC::Policy->new( v => 'DMARC1', p => 'reject' ); ok( $pol->apply_defaults(), "apply_defaults" ); my $expected = { v => 'DMARC1', p => 'reject', rf => 'afrf', fo => 0, adkim => 'r', aspf => 'r', ri => 86400 }; is_deeply( $pol, $expected, "new, with defaults" ); } sub test_setter_values { my %good_vals = ( p => [qw/ none reject quarantine NONE REJEcT Quarantine /], v => [qw/ DMARC1 dmarc1 /], sp => [qw/ none reject quarantine NoNe REjEcT QuarAntine /], adkim => [qw/ r s R S /], aspf => [qw/ r s R S /], fo => [qw/ 0 1 d s D S 0:d 0:1:d:s /], rua => [ qw{ http://example.com/pub/dmarc!30m mailto:dmarc-feed@example.com!10m } ], ruf => [qw{ https://example.com/dmarc?report!1m }], rf => [qw/ iodef afrf IODEF AFRF /], ri => [ 0, 1, 1000, 4294967295 ], pct => [ 0, 10, 50, 99, 100 ], ); foreach my $k ( keys %good_vals ) { foreach my $t ( @{ $good_vals{$k} } ) { ok( defined $pol->$k($t), "$k, $t" ); } } my %bad_vals = ( p => [qw/ nonense silly example /], v => ['DMARC2'], sp => [qw/ nones rejection quarrantine /], adkim => [qw/ relaxed strict /], aspf => [qw/ relaxed strict /], fo => [qw/ 00 11 dd ss /], rua => [qw{ ftp://example.com/pub torrent://piratebay.net/dmarc }], ruf => [qw{ mail:msimerson@cnap.org }], rf => [qw/ iodef2 rfrf2 rfrf /], ri => [ -1, 'a', 4294967296 ], pct => [ -1, 'f', 101, 1.1, '1.0', '5.f1' ], ); foreach my $k ( keys %bad_vals ) { foreach my $t ( @{ $bad_vals{$k} } ) { eval { $pol->$k($t); }; ok( $@, "neg, $k, $t" ); } } } sub test_new { # empty policy my $pol = Mail::DMARC::Policy->new(); isa_ok( $pol, 'Mail::DMARC::Policy' ); is_deeply( $pol, {}, "new, empty policy" ); # default policy $pol = Mail::DMARC::Policy->new( v => 'DMARC1', p => 'reject', pct => 90, rua => 'mailto:u@d.co' ); isa_ok( $pol, 'Mail::DMARC::Policy' ); is_deeply( $pol, { v => 'DMARC1', p => 'reject', pct => 90, rua => 'mailto:u@d.co' }, "new, with args" ); # text record $pol = Mail::DMARC::Policy->new( 'v=DMARC1; p=reject; rua=mailto:u@d.co; pct=90'); isa_ok( $pol, 'Mail::DMARC::Policy' ); is_deeply( $pol, { v => 'DMARC1', p => 'reject', pct => 90, rua => 'mailto:u@d.co' }, "new, with args" ); } sub test_parse { $pol = $pol->parse( 'v=DMARC1; p=reject; rua=mailto:dmarc@example.co; pct=90'); isa_ok( $pol, 'Mail::DMARC::Policy' ); my $expected = { v => 'DMARC1', p => 'reject', pct => 90, rua => 'mailto:dmarc@example.co', }; is_deeply( $pol, $expected, 'parse'); is_deeply( $pol->parse( 'v=DMARC1;p=reject;rua=mailto:dmarc-feedback@theartfarm.com;pct=;ruf=mailto:dmarc-feedback@theartfarm.com' ), { v => 'DMARC1', p => 'reject', rua => 'mailto:dmarc-feedback@theartfarm.com', ruf => 'mailto:dmarc-feedback@theartfarm.com', }, "parse, warns of invalid DMARC record format" ); is_deeply( $pol->parse( 'domain=tnpi.net;v=DMARC1;p=reject;rua=mailto:dmarc-feedback@theartfarm.com;pct=;ruf=mailto:dmarc-feedback@theartfarm.com' ), { v => 'DMARC1', p => 'reject', domain => 'tnpi.net', rua => 'mailto:dmarc-feedback@theartfarm.com', ruf => 'mailto:dmarc-feedback@theartfarm.com', }, "parse, warns of invalid DMARC record format, with location" ); $pol = $pol->parse('v=DMARC1'); isa_ok( $pol, 'Mail::DMARC::Policy' ); $expected = { v => 'DMARC1' }; is_deeply( $pol, $expected, 'parse'); } sub test_is_valid_p { foreach my $p (qw/ none reject quarantine /) { ok( $pol->is_valid_p($p), "policy->is_valid_p, pos, $p" ); } foreach my $p (qw/ other gibberish non-policy words /) { ok( !$pol->is_valid_p($p), "policy->is_valid_p, neg, $p" ); } } sub test_is_valid_rf { foreach my $f (qw/ afrf iodef /) { ok( $pol->is_valid_rf($f), "policy->is_valid_rf, pos, $f" ); } foreach my $f (qw/ ffrf i0def report /) { ok( !$pol->is_valid_rf($f), "policy->is_valid_rf, neg, $f" ); } } sub test_is_valid { # empty policy my $pol = Mail::DMARC::Policy->new(); eval { $pol->is_valid(); }; chomp $@; ok( $@, "is_valid, $@" ); eval { $pol = Mail::DMARC::Policy->new('v=DMARC1') }; chomp $@; ok( $@, "is_valid, 1.4.1 meaningless, $@" ); eval { $pol = Mail::DMARC::Policy->new('v=DMARC1\; p=reject\;') }; ok( $pol, "is_valid, 1.4.3 extra backslashes" ); eval { $pol = Mail::DMARC::Policy->new('v=DMARC1; p=reject; newtag=unknown') }; ok( $pol, "is_valid, 1.4.4 unknown tag" ); eval { $pol = Mail::DMARC::Policy->new('v=DMARC1; p=bogus') }; chomp $@; ok( $@, "is_valid, 1.4.5 bogus p value, $@" ); # policy, minimum $pol = Mail::DMARC::Policy->new( 'v=DMARC1; p=reject' ); ok( $pol->is_valid, "is_valid, 1.4.2 smallest record" ); # policy, min + defaults $pol->apply_defaults(); ok( $pol->is_valid, "is_valid, pos, w/defaults" ); # 9.6 policy discovery $pol = undef; eval { $pol = Mail::DMARC::Policy->new( v => 'DMARC1' ); }; # or diag $@; ok( !$pol, "is_valid, neg, missing p, no rua" ); eval { $pol = Mail::DMARC::Policy->new( v => 'DMARC1', rua => 'ftp://www.example.com' ); }; # or diag $@; ok( !$pol, "is_valid, neg, missing p, invalid rua" ); $pol = undef; eval { $pol = Mail::DMARC::Policy->new( v => 'DMARC1', rua => 'mailto:test@example.com' ); }; ok( $pol && $pol->is_valid, "is_valid, pos, implicit p=none w/rua" ); } sub handles_common_record_errors { foreach my $d () { chomp $d; my $pol = Mail::DMARC::Policy->new($d); eval { ok( $pol->is_valid(), "policy is valid: $d"); }; } } # unhandled errors #domain=caasco.ca;v=DMARC1;p=none;sp=none;rua=mailto:dmarc_agg@auth.returnpath.net;ruf=mailto:dmarc_afrf@auth.returnpath.net;rf=afrf;pct100 #domain=edm.groceryrun.com.au;v=dmarc1;p=none;rua=mailto:dmarc_feedback@inxmail.de&amp;amp;amp;lt;dmarc_feedback@inxmail.de&amp;amp;amp;gt #domain=reply.myphotobook.de;v=DMARC1;p=reject;adkim=s;aspf=r;rf�rf;pct0 #domain=email.ex.kbhmaui.com;v=DMARC1;p=none;rua=mailto:dmarc-reports@email.ex.kbhmaui.com;pct=100;” __DATA__ domain=accuquote.com;v=DMARC1;p=none;fo:0;adkim=r;aspf=r;sp=none;rua=mailto:accu_postmaster@accuquote.com domain=targetselect.net;v=DMARC1;p=none;rua=mailto:postmaster@targetselect.net;ruf=mailto:postmaster@targetselect.net;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=1105insight.com;v=DMARC1;p=none;rua=mailto:postmaster@1105insight.com;ruf=mailto:postmaster@1105insight.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=borsheims.net;v=DMARC1;p=none;rua=mailto:postmaster@borsheims.net;ruf=mailto:postmaster@borsheims.net;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=consumersilver.com;v=DMARC1;p=none;rua=mailto:postmaster@consumersilver.com;ruf=mailto:postmaster@consumersilver.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=coppermail-usa.com;v=DMARC1;p=none;rua=mailto:postmaster@coppermail-usa.com;ruf=mailto:postmaster@coppermail-usa.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=eglancesender.com;v=DMARC1;p=none;rua=mailto:postmaster@eglancesender.com;ruf=mailto:postmaster@eglancesender.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=egroupconsumer.com;v=DMARC1;p=none;rua=mailto:postmaster@egroupconsumer.com;ruf=mailto:postmaster@egroupconsumer.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=eselectsender.com;v=DMARC1;p=none;rua=mailto:postmaster@eselectsender.com;ruf=mailto:postmaster@eselectsender.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=filemail.com;v=DMARC1;p=none;rua=mailto:admin@filemail.com;ruf=mailto:admin@filemail.com;fo:0;adkim=r;aspf=r domain=fisherprograms.com;v=DMARC1;p=none;rua=mailto:postmaster@fisherprograms.com;ruf=mailto:postmaster@fisherprograms.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=mail-peninsula.com;v=DMARC1;p=none;rua=mailto:umesh@force24.co.uk;ruf=mailto:umesh@force24.co.uk;fo:0;adkim=r;aspf=r;pct=100;rf=afrf;ri=86000;sp=none domain=sendergroup.com;v=DMARC1;p=none;rua=mailto:postmaster@sendergroup.com;ruf=mailto:postmaster@sendergroup.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=targetselection.com;v=DMARC1;p=none;rua=mailto:postmaster@targetselection.com;ruf=mailto:postmaster@targetselection.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=trondheim-redcross.no;v=DMARC1;p=none;rua=mailto:postmaster@trondheim-redcross.no;ruf=mailto:johess@trondheim-redcross.no;fo:0;adkim=r;aspf=r;pct=100;rf=afrf;ri=86400;sp=none domain=vsender-2.com;v=DMARC1;p=none;rua=mailto:postmaster@vsender-2.com;ruf=mailto:postmaster@vsender-2.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=vsender-3.com;v=DMARC1;p=none;rua=mailto:postmaster@vsender-3.com;ruf=mailto:postmaster@vsender-3.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=wfyi.org;v=DMARC1;p=none;rua=mailto:dmarc1630@wfyi.org;ruf=mailto:dmarc1630@wfyi.org;fo:0;adkim=r;aspf=r;pct=100;rf=afrf;ri=86400;sp=none domain=wk1business.com;v=DMARC1;p=none;rua=mailto:postmaster@wk1business.com;ruf=mailto:postmaster@wk1business.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=teogenes.com.br;v=DMARC1;p=quarantine;rua=mailto:retorno@teogenes.com.br;fo:1:d;adkim=r;aspf=r;rf=afrf;sp=quarantine domain=wkcplatnium.com;v=DMARC1;p=none;rua=mailto:postmaster@wkcplatnium.com;ruf=mailto:postmaster@wkcplatnium.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=brightworksgroup.net;v=DMARC1;p=none;0;adkim=r;aspf=r domain=bronzemail-usa.com;v=DMARC1;p=none;rua=mailto:postmaster@bronzemail-usa.com;ruf=mailto:postmaster@bronzemail-usa.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=driveconsumer.com;v=DMARC1;p=none;rua=mailto:postmaster@driveconsumer.com;ruf=mailto:postmaster@driveconsumer.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=econnect1.com;v=DMARC1;p=none;rua=mailto:postmaster@econnect1.com;ruf=mailto:postmaster@econnect1.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=esender1.com;v=DMARC1;p=none;rua=mailto:postmaster@esender1.com;ruf=mailto:postmaster@esender1.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=esender3.com;v=DMARC1;p=none;rua=mailto:postmaster@esender3.com;ruf=mailto:postmaster@esender3.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=lns.com;v=DMARC1;p=none;rua=mailto:dmarc@lns.com;ruf=mailto:dmarc@lns.com;0;adkim=r;aspf=r;pct=100;rf=afrf;ri=86400;sp=none domain=my-dear-lady.com;v=DMARC1;p=none;rua=mailto:postmaster@my-dear-lady.com;ruf=mailto:postmaster@my-dear-lady.com;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=theluxurycloset.info;v=DMARC1;p=reject;adkim=s;aspf=r;rf=afrf;;pct=100 domain=newsletter.ironpony.net;v=DMARC1;p=none;rua=mailto:postmaster@newsletter.ironpony.net;ruf=mailto:postmaster@newsletter.ironpony.net;adkim=r;aspf=r;pct=100;rf:afrf;ri=86400;sp=none domain=cmnet.org;v=DMARC1;p=none;sp=none;rua=mailto:postmaster@cmnet.org!10m;;pct=100;ri=86400 domain=coachingcompass.com;v=DMARC1;p=quarantine;rua=mailto:dan@darau.com;ruf=mailto:dan@darau.com;1:d:s;adkim=r;aspf=r;rf=afrf;sp=quarantine domain=genetex.com;v=DMARC1;p=none;sp-none;rua=mailto:postmaster@genetex.com!1m;ruf=mailto:postmaster@genetex.com!1m;rf=afrf;pct=100;ri=86400 Mail-DMARC-1.20240314/t/03.Base.t000444000765000024 1221114574361234 14757 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Net::DNS::Resolver::Mock; use Test::More; use Test::File::ShareDir -share => { -dist => { 'Mail-DMARC' => 'share' } }; use lib 'lib'; my $mod = 'Mail::DMARC::Base'; use_ok($mod); my $base = $mod->new; isa_ok( $base, $mod ); isa_ok( $base->config, 'Config::Tiny' ); isa_ok( $base->get_resolver(), 'Net::DNS::Resolver' ); # invalid config file $base = $mod->new( config_file => 'no such config' ); eval { $base->config }; chomp $@; ok( $@, "invalid config file" ); # alternate config file $base = $mod->new(); eval { $base->config('t/mail-dmarc.ini'); }; chomp $@; ok( !$@, "alternate config file" ); my $resolver = new Net::DNS::Resolver::Mock(); $base->set_resolver($resolver); __any_inet_to(); __is_public_suffix(); __has_dns_rr(); __is_valid_ip(); __is_valid_domain(); __epoch_to_iso(); __get_prefix(); __get_sharefile(); __psl_cached(); __psl_cached_reload(); done_testing(); exit; #warn Dumper($base); sub __epoch_to_iso { my $iso = $base->epoch_to_iso(time); ok( $iso, "epoch_to_iso, $iso" ); }; sub __any_inet_to { my @test_ips = ( '1.1.1.1', '10.0.1.1', '2002:4c79:6240::1610:9fff:fee5:fb5', '2607:f060:b008:feed::6', ); foreach my $ip (@test_ips) { my $bin = $base->any_inet_pton($ip); ok( $bin, "any_inet_pton, $ip" ); my $pres = $base->any_inet_ntop($bin); ok( $pres, "any_inet_ntop, $ip" ); if ( $pres eq $ip ) { cmp_ok( $pres, 'eq', $ip, "any_inet_ntop, $ip" ); } else { # on some systems, a :: pattern gets a zero inserted. Mimic that my $zero_filled = $ip; $zero_filled =~ s/::/:0:/g; cmp_ok( $pres, 'eq', $zero_filled, "any_inet_ntop, $ip" ) or diag "presentation: $zero_filled\nresult: $pres"; } } } sub __is_valid_ip { # positive tests foreach (qw/ 0.0.0.0 1.1.1.1 255.255.255.255 2607:f060:b008:feed::2 /) { ok( $base->is_valid_ip($_), "is_valid_ip, $_" ); } # negative tests foreach (qw/ 256.1.1.1 a 1.1.1.256 /) { ok( !$base->is_valid_ip($_), "is_valid_ip, neg, $_" ); } } sub __is_valid_domain { # positive tests foreach (qw/ test.sch.uk example.com bbc.co.uk 3.am /) { ok( $base->is_valid_domain($_), "is_valid_domain, $_" ); } # negative tests foreach (qw/ example.m bbc.co.k 3.a /) { ok( !$base->is_valid_domain($_), "is_valid_domain, $_" ); } } sub __has_dns_rr { my %tests = ( 'NS:tnpi.net' => 1, 'NS:fake.mail-dmarc.tnpi.net' => 0, 'A:www.tnpi.net' => 1, 'MX:tnpi.net' => 1, 'MX:gmail.com' => 1, ); foreach my $dom ( keys %tests ) { my $r = $base->has_dns_rr( split /:/, $dom ); # no need to raise test errors for CPAN test systems with unreliable DNS next if !$r && $tests{$dom}; ok( $r >= $tests{$dom}, "has_dns_rr, $dom" ); } } sub __is_public_suffix { my %tests = ( 'www.tnpi.net' => 0, 'tnpi.net' => 0, 'net' => 1, 'com' => 1, 'co.uk' => 1, '*.uk' => 0, 'google.com' => 0, 'a' => 0, 'z' => 0, 'test.sch.uk' => 1, 'sch.uk' => 0, ); foreach my $dom ( keys %tests ) { my $t = $tests{$dom} == 0 ? 'neg' : 'pos'; cmp_ok( $tests{$dom}, '==', $base->is_public_suffix($dom), "is_public_suffix, $t, $dom" ); } } sub __get_prefix { is_deeply( [ $base->get_prefix() ], [ '/usr/local/', '/opt/local/', '/', './' ], "get_prefix: /usr/local/, /opt/local/, /, ./", ); is_deeply( [ $base->get_prefix('etc') ], [ '/usr/local/etc', '/opt/local/etc', '/etc', './etc' ], "get_prefix(etc): /usr/local/etc, /opt/local/etc, /etc, ./etc", ); is_deeply( [ $base->get_prefix('share') ], [ '/usr/local/share', '/opt/local/share', '/share', './share' ], "get_prefix(share): /usr/local/share, /opt/local/share, /share, ./share", ); } sub __get_sharefile { # throws an exception until after 'make install' has been run my $r; eval { $r = $base->get_sharefile('mail-dmarc.ini'); }; SKIP: { skip '"make install" not yet run', 1 if $@; ok($r, "get_sharefile: $r"); }; } sub __psl_cached { no warnings 'once'; cmp_ok($Mail::DMARC::psl_loads, '==', 1, 'Public Suffix List cached'); } sub __psl_cached_reload { no warnings 'once'; cmp_ok($Mail::DMARC::psl_loads, '==', 1, 'Public Suffix List loaded'); my $file = $base->find_psl_file(); my $future = time() + 3600; utime ( $future, $future, $file ); my $check = $base->check_public_suffix_list(); cmp_ok($Mail::DMARC::psl_loads, '==', 2, 'Public Suffix List reloaded'); cmp_ok($check, '==', 1, 'Public Suffix List reloaded true return'); $check = $base->check_public_suffix_list(); cmp_ok($check, '==', 0, 'Public Suffix List reloaded false return'); } Mail-DMARC-1.20240314/t/04.PurePerl.t000444000765000024 3473214574361234 15660 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Net::DNS::Resolver::Mock; use Test::More; use URI; use Test::File::ShareDir -share => { -dist => { 'Mail-DMARC' => 'share' } }; use lib 'lib'; use_ok('Mail::DMARC::PurePerl'); my $resolver = new Net::DNS::Resolver::Mock(); $resolver->zonefile_parse(join("\n", '_dmarc.mail-dmarc.tnpi.net. 600 TXT "v=DMARC1; p=reject; rua=mailto:invalid@theartfarm.com; ruf=mailto:invalid@theartfarm.com; pct=90"', '_dmarc.tnpi.net. 600 TXT "v=DMARC1; p=reject; rua=mailto:dmarc-feedback@theartfarm.com; ruf=mailto:dmarc-feedback@theartfarm.com; pct=100" ', 'tnpi.net. 600 MX 10 mail.theartfarm.com.', 'tnpi.net._report._dmarc.theartfarm.com. 600 TXT "v=DMARC1"', 'cadillac.net._report._dmarc.theartfarm.com. 600 TXT "v=DMARC1"', 'mail-dmarc.tnpi.net._report._dmarc.theartfarm.com. 600 TXT "v=DMARC1; rua=mailto:invalid-test@theartfarm.com;"', '')); my @test_policy = ( 'v', 'DMARC1', # Section 6.2, Formal Definition 'p', 'reject', # the v(ersion) and request(p) are ordered 'rua', 'mailto:invalid@theartfarm.com', 'ruf', 'mailto:invalid@theartfarm.com', 'pct', 90, ); my %test_policy = @test_policy; my $n; my $test_rec = join( '; ', map { $_ . '=' . $test_policy{$_} } grep { !( $n++ % 2 ) } @test_policy ); # extract keys my $dmarc = Mail::DMARC::PurePerl->new; $dmarc->config('t/mail-dmarc.ini'); $dmarc->set_resolver($resolver); isa_ok( $dmarc, 'Mail::DMARC::PurePerl' ); test_get_from_dom(); test_fetch_dmarc_record(); test_get_organizational_domain(); test_exists_in_dns(); test_is_spf_aligned(); test_is_dkim_aligned(); test_is_aligned(); test_is_whitelisted(); test_discover_policy(); test_validate(); test_has_valid_reporting_uri(); test_external_report(); test_verify_external_reporting( 'tnpi.net', 'theartfarm.com', 1 ); test_verify_external_reporting( 'cadillac.net', 'theartfarm.com', 1 ); test_verify_external_reporting( 'mail-dmarc.tnpi.net', 'theartfarm.com', 1 ); _test_reason(); done_testing(); exit; sub _test_reason { $dmarc->init(); $dmarc->source_ip('66.128.51.165'); $dmarc->envelope_to('recipient.example.com'); $dmarc->envelope_from('dmarc-nonexist.tnpi.net'); $dmarc->header_from('mail-dmarc.tnpi.net'); $dmarc->dkim([ { domain => 'tnpi.net', selector => 'jan2015', result => 'fail', human_result=> 'fail (body has been altered)', } ]); $dmarc->spf([ { domain => 'tnpi.net', scope => 'mfrom', result => 'pass', }, { scope => 'helo', domain => 'mail.tnpi.net', result => 'fail', }, ]); my $policy = $dmarc->discover_policy; ok( $policy, "discover_policy" ); my $result = $dmarc->validate($policy); ok( ref $result, "result is a ref"); ok( $result->{result} eq 'pass', "result=pass"); ok( $result->{spf} eq 'pass', "spf=pass"); ok( $result->{disposition} eq 'none', "disposition=none"); $result->disposition('reject'); ok( $result->{disposition} eq 'reject', "disposition changed to reject"); ok( $result->reason( type => 'local_policy' ), "added reason" ); ok( $result->reason( type => 'local_policy', comment => 'testing' ), "added reason 2" ); #warn Data::Dumper::Dumper($result->reason); ok( $dmarc->save_aggregate(), "save aggregate"); } sub test_verify_external_reporting { my ( $dmarc_dom, $dest_dom, $outcome ) = @_; my $ver = 'verify_external_reporting'; my $policy; eval { $policy = $dmarc->policy->parse( "v=DMARC1; p=none; rua=mailto:dmarc-feedback\@$dest_dom"); }; $policy->{domain} = $dmarc_dom; ok( $policy, "new policy" ); $dmarc->result->published($policy); my $uri = URI->new("mailto:test\@$dest_dom"); cmp_ok( $outcome, 'eq', $dmarc->$ver( { uri => $uri } ), "$ver, $dmarc_dom, $dest_dom" ); # a DMARC record with a RUA override return if $dmarc_dom ne 'mail-dmarc.tnpi.net'; my $uri_should_be = $dmarc->report->uri->parse( URI->new("mailto:invalid-test\@theartfarm.com") ); my $uri_via_net = $dmarc->report->uri->parse( $dmarc->result->published->rua ); is_deeply( $uri_via_net->[0], $uri_should_be->[0], "$ver, override rua" ); } sub test_external_report { my @test_doms = qw/ example.com silly.com /; foreach my $dom (@test_doms) { my $policy = $dmarc->policy->parse('v=DMARC1'); $policy->{domain} = $dom; ok( $policy, "new policy" ); $dmarc->result->published($policy); my $uri = URI->new("mailto:test\@$dom"); # warn "path: " . $uri->path; ok( $uri, "new URI" ); ok( !$dmarc->external_report($uri), "external_report, $uri for $dom" ); } foreach my $dom (@test_doms) { my $policy = $dmarc->policy->parse('v=DMARC1'); $policy->{domain} = "$dom.com"; ok( $policy, "new policy" ); $dmarc->result->published($policy); my $uri = URI->new("mailto:test\@$dom"); # warn "path: " . $uri->path; ok( $uri, "new URI" ); ok( $dmarc->external_report($uri), "external_report, $uri for $dom.com" ); } } sub test_has_valid_reporting_uri { my @valid = ( 'mailto:dmarc@example.com', # canonical example 'mailto:dmarc@example.com,http://example.com/dmarc', # two matches 'ftp://dmarc.example.com,http://example.com/dmarc', # http only ); $dmarc->result->published->{domain} = 'example.com'; foreach my $v (@valid) { my $r_ref = $dmarc->has_valid_reporting_uri($v); ok( $r_ref, "has_valid_reporting_uri, $v" ); } my $uris; $uris = $dmarc->has_valid_reporting_uri( 'mailto:invalid@no-premission.example.com' ); ok( 1 == $uris, "has_valid_reporting_uri, single filtered"); # invalid tests my @invalid = ( 'ftp://ftp.example.com', # invalid schemes 'gopher://www.example.com/dmarc', 'scp://secure.example.com', 'http://www.example.com/dmarc', # host doesn't match ); $dmarc->result->published->{domain} = 'example.com'; foreach my $v (@invalid) { my $r = $dmarc->has_valid_reporting_uri($v); ok( !$r, "has_valid_reporting_uri, neg, $v" ) or diag Dumper($r); } my %real = (); # real life tests foreach my $dom ( keys %real ) { $dmarc->result->published->{domain} = $dom; my $r_ref = $dmarc->has_valid_reporting_uri($real{$dom}); ok( $r_ref, "has_valid_reporting_uri, $dom" ); }; } sub test_discover_policy { $dmarc->init(); $dmarc->header_from('mail-dmarc.tnpi.net'); my $policy = $dmarc->discover_policy; ok( $policy, "discover_policy" ) or return diag Data::Dumper::Dumper($dmarc); $policy->apply_defaults; my $expected = { %test_policy, aspf => 'r', # $pol->new adds the defaults that are adkim => 'r', # implied in all DMARC records ri => 86400, rf => 'afrf', fo => 0, domain => 'mail-dmarc.tnpi.net', }; is_deeply( $policy, $expected, 'discover_policy, deeply' ); } sub get_test_headers { return ( 'From: Sample User ' => 'example.com', 'From: Sample Middle User ' => 'example.com', 'From: "Sample User" ' => 'example.com', 'From: "Sample Middle User" ' => 'example.com', 'Sample User ' => 'example.com', 'user@example.com' => 'example.com', '' => 'example.com', ' ' => 'example.com', 'Sample User ,Sample2' => 'example2.com', 'From: "Test 1.1.5"' => '', ); } sub test_is_spf_aligned { ok( $dmarc->header_from('example.com'), "spf, set header_from" ); ok( $dmarc->spf( domain => 'example.COM', scope => 'mfrom', result => 'pass' ), 'spf, set spf' ); ok( $dmarc->is_spf_aligned(), "is_spf_aligned" ); ok( 'strict' eq $dmarc->result->spf_align, "is_spf_aligned, strict" ) or diag Dumper( $dmarc->result ); $dmarc->header_from('mail.example.com'); ok( $dmarc->spf( domain => 'example.com', scope => 'mfrom', result => 'pass' ), 'spf, set spf' ); ok( $dmarc->policy->aspf('r'), "spf alignment->r" ); ok( $dmarc->is_spf_aligned(), "is_spf_aligned, relaxed" ); ok( 'relaxed' eq $dmarc->result->spf_align, "is_spf_aligned, relaxed" ); $dmarc->header_from('mail.exUmple.com'); ok( $dmarc->spf( domain => 'example.com', scope => 'mfrom', result => 'pass' ), 'spf, set spf' ); ok( !$dmarc->is_spf_aligned(), "is_spf_aligned, neg" ); } sub test_is_dkim_aligned { ok( $dmarc->header_from('example.com'), "dkim, set header_from" ); ok( $dmarc->dkim( [ { domain => 'mailing-list.com', selector => 'apr2013', result => 'fail', human_result => 'fail (body has been altered)', }, { domain => 'example.com', selector => 'apr2013', result => 'pass', human_result => 'pass', }, ] ), "dkim, setup" ); ok( $dmarc->is_dkim_aligned(), "is_dkim_aligned, strict" ); ok( $dmarc->header_from('mail.example.com'), "dkim, set header_from" ); ok( $dmarc->is_dkim_aligned(), "is_dkim_aligned, relaxed" ); # negative test ok( $dmarc->header_from('mail.exaNple.com'), "dkim, set header_from" ); ok( !$dmarc->is_dkim_aligned(), "is_dkim_aligned, miss" ); # no DKIM signatures ok( $dmarc->dkim( [] ), "dkim, no signatures" ); ok( !$dmarc->is_dkim_aligned(), "is_dkim_aligned, empty" ); # PSL listed domains ok( $dmarc->dkim( [ { domain => 'net', selector => 'apr2013', result => 'pass', human_result => 'pass', }, ] ), "dkim, setup" ); ok( $dmarc->header_from('net'), "dkim, set header_from" ); ok( $dmarc->is_dkim_aligned(), "is_dkim_aligned, relaxed" ); # negative test ok( $dmarc->header_from('example.net'), "dkim, set header_from" ); ok( !$dmarc->is_dkim_aligned(), "is_dkim_aligned, miss" ); } sub test_is_aligned { $dmarc->result->spf('pass'); $dmarc->result->dkim('pass'); ok( $dmarc->is_aligned(), "is_aligned, both" ); $dmarc->result->dkim('fail'); ok( $dmarc->is_aligned(), "is_aligned, spf" ); $dmarc->result->dkim('pass'); $dmarc->result->spf('fail'); ok( $dmarc->is_aligned(), "is_aligned, dkim" ); $dmarc->result->dkim('fail'); ok( !$dmarc->is_aligned(), "is_aligned, none" ) or diag Data::Dumper::Dumper( $dmarc->is_aligned() ); } sub test_is_whitelisted { my %good = ( '127.0.0.1' => 'local_policy', '127.0.0.3' => 'trusted_forwarder', ); foreach ( keys %good ) { cmp_ok( $dmarc->is_whitelisted($_), 'eq', $good{$_}, "is_whitelisted, $_, $good{$_}"); }; my @bad = qw/ 127.0.0.2 10.0.0.0 /; foreach ( @bad ) { ok( ! $dmarc->is_whitelisted($_), "is_whitelisted, neg, $_"); }; }; sub test_validate { my %sample_dmarc = ( config_file => 'mail-dmarc.ini', source_ip => '192.0.1.1', envelope_to => 'example.com', envelope_from => 'cars4you.info', header_from => 'tnpi.net', dkim => [ { domain => 'example.com', selector => 'apr2013', result => 'fail', human_result => 'fail (body has been altered)', } ], spf => [ { domain => 'tnpi.net', scope => 'mfrom', result => 'pass', } ], ); $dmarc = Mail::DMARC::PurePerl->new(%sample_dmarc); $dmarc->set_resolver($resolver); eval { $dmarc->validate(); }; #print Dumper($dmarc->result); ok($dmarc->is_spf_aligned(), "validate, one-shot, is_spf_aligned, yes" ); ok(!$dmarc->is_dkim_aligned(), "validate, one-shot, is_dkim_aligned, no" ); } sub test_exists_in_dns { my %tests = ( 'tnpi.net' => 1, 'fake.mail-dmarc.tnpi.net' => 1, # organizational name exists 'no-such-made-up-name-should-exist.com.uk.nonsense' => 0, ); foreach my $dom ( keys %tests ) { $dmarc->init; my $r = $dmarc->exists_in_dns($dom); ok( $r >= $tests{$dom}, "exists_in_dns, $dom, $r" ); } } sub test_get_organizational_domain { my %domains = ( 'tnpi.net' => 'tnpi.net', 'www.tnpi.net' => 'tnpi.net', 'plus.google.com' => 'google.com', 'bbc.co.uk' => 'bbc.co.uk', 'www.bbc.co.uk' => 'bbc.co.uk', ); foreach ( keys %domains ) { cmp_ok( $domains{$_}, 'eq', $dmarc->get_organizational_domain($_), "get_organizational_domain, $_" ); } } sub test_fetch_dmarc_record { my ($matches) = $dmarc->fetch_dmarc_record('mail-dmark.tnpi.net'); is_deeply( $matches, [], 'fetch_dmarc_record, non-exist' ); #warn Dumper($matches); ($matches) = $dmarc->fetch_dmarc_record('mail-dmarc.tnpi.net'); is_deeply( $matches, [$test_rec], 'fetch_dmarc_record' ); my $policy; ($matches) = $dmarc->fetch_dmarc_record('com'); is_deeply( $matches, [], 'fetch_dmarc_record, 1.2.4 TLD lookup not allowed' ); } sub test_get_from_dom { my %froms = get_test_headers(); foreach my $h ( keys %froms ) { $dmarc->init; $dmarc->header_from_raw($h); my $s; eval { $s = $dmarc->get_from_dom() }; if ( $froms{$h} ) { ok( $s eq $froms{$h}, "get_from_dom, $s eq $froms{$h}" ); } else { chomp $@; ok( 1, "get_from_dom, $h, $@" ); }; } } Mail-DMARC-1.20240314/t/06.Result.t000444000765000024 2134014574361234 15371 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Net::DNS::Resolver::Mock; use Test::More; use Test::File::ShareDir -share => { -dist => { 'Mail-DMARC' => 'share' } }; use lib 'lib'; use_ok('Mail::DMARC::PurePerl'); use_ok('Mail::DMARC::Result'); my $resolver = new Net::DNS::Resolver::Mock(); $resolver->zonefile_parse(join("\n", 'tnpi.net. 600 A 66.128.51.170', '_dmarc.tnpi.net. 600 TXT "v=DMARC1; p=reject; rua=mailto:dmarc-feedback@theartfarm.com; ruf=mailto:dmarc-feedback@theartfarm.com; pct=100"', 'nodmarcrecord.blogspot.com. 600 A 142.251.37.1', '')); my $pp = Mail::DMARC::PurePerl->new; my $result = Mail::DMARC::Result->new; $pp->set_resolver($resolver); isa_ok( $result, 'Mail::DMARC::Result' ); my $test_dom = 'tnpi.net'; test_published(); test_no_policy(); test_disposition(); test_dkim(); test_dkim_align(); test_spf(); test_result(); test_reason(); test_dkim_meta(); done_testing(); exit; sub _test_pass_strict { $pp->init(); $pp->header_from($test_dom); $pp->dkim( [ { domain => $test_dom, result => 'pass', selector => 'apr2013' } ] ); $pp->spf( { domain => $test_dom, result => 'pass', scope => 'mfrom' } ); $pp->validate(); delete $pp->result->{published}; my $expected = { 'result' => 'pass', 'disposition' => 'none', 'dkim' => 'pass', 'spf' => 'pass', 'spf_align' => 'strict', 'dkim_meta' => { 'domain' => 'tnpi.net', 'identity' => '', 'selector' => 'apr2013', }, 'dkim_align' => 'strict', reason => [], }; is_deeply( $pp->result, $expected, "result, pass, strict, $test_dom") or diag Data::Dumper::Dumper( $pp->result ); } sub _test_pass_relaxed { $pp->init(); $pp->header_from("www.$test_dom"); $pp->dkim( [ { domain => $test_dom, result => 'pass', selector => 'apr2013' } ] ); $pp->spf( { scope => 'mfrom', domain => $test_dom, result => 'pass' } ); $pp->validate(); delete $pp->result->{published}; my $skip_reason; if ( !$pp->result->dkim ) { # typically a DNS failure, $skip_reason = "look like DNS is not working"; } SKIP: { skip $skip_reason, 1 if $skip_reason; my $expected = { 'result' => 'pass', 'dkim' => 'pass', 'spf' => 'pass', 'disposition' => 'none', 'dkim_align' => 'relaxed', 'dkim_meta' => { 'domain' => 'tnpi.net', 'identity' => '', 'selector' => 'apr2013', }, 'spf_align' => 'relaxed', reason => [], }; is_deeply( $pp->result, $expected, "pass, relaxed, $test_dom" ) or diag Data::Dumper::Dumper( $pp->result ); } } sub _test_fail_strict { my $pol = shift || 'reject'; $pp->init(); my $from_dom = "www.$test_dom"; $pp->header_from($from_dom); $pp->dkim( [ { domain => $test_dom, result => 'pass', selector => 'apr2013' } ] ); $pp->spf( { scope => 'mfrom', domain => $test_dom, result => 'pass' } ); my $policy = $pp->policy->parse("v=DMARC1; p=$pol; aspf=s; adkim=s"); $policy->{domain} = $from_dom; $pp->result->published($policy); $pp->{policy} = $policy; $pp->validate($policy); ok( !$pp->is_dkim_aligned, "is_dkim_aligned, neg" ); ok( !$pp->is_spf_aligned, "is_spf_aligned, neg" ); ok( !$pp->is_aligned(), "is_aligned, neg" ); delete $pp->result->{published}; my $expected = { 'disposition' => $pol, 'dkim' => 'fail', 'spf' => 'fail', 'result' => 'fail', reason => [], }; is_deeply( $pp->result, $expected, "result, fail, strict, $test_dom" ) or diag Data::Dumper::Dumper( $pp->result ); } sub _test_fail_sampled_out { my $pol = 'reject'; $pp->init(); my $from_dom = "www.$test_dom"; $pp->header_from($from_dom); $pp->dkim( [ { domain => $test_dom, result => 'pass', selector => 'apr2013' } ] ); $pp->spf( { scope => 'mfrom', domain => $test_dom, result => 'pass' } ); my $policy = $pp->policy->parse("v=DMARC1; p=$pol; aspf=s; adkim=s; pct=0"); $policy->{domain} = $from_dom; $pp->result->published($policy); $pp->{policy} = $policy; $pp->validate($policy); ok( !$pp->is_dkim_aligned, "is_dkim_aligned, neg" ); ok( !$pp->is_spf_aligned, "is_spf_aligned, neg" ); ok( !$pp->is_aligned(), "is_aligned, neg" ); delete $pp->result->{published}; my $expected = { 'disposition' => 'quarantine', 'dkim' => 'fail', 'spf' => 'fail', 'reason' => [{ 'type' => 'sampled_out' }], 'result' => 'fail', }; is_deeply( $pp->result, $expected, "result, fail, strict, sampled out, $test_dom" ) or diag Data::Dumper::Dumper( $pp->result ); } sub _test_fail_nonexist { $pp->init(); $pp->{header_from} = 'host.nonexistent-tld'; # the ->header_from method would validate $pp->validate(); # some test machines return 'interesting' results for queries of non-existent # domains. That's not worth raising a test error. my $skip_reason; if ( ! $pp->result->reason || $pp->result->reason->[0]->comment ne 'host.nonexistent-tld not in DNS' ) { $skip_reason = "DNS returned 'interesting' results for invalid domain"; }; SKIP: { skip $skip_reason, 1 if $skip_reason; is_deeply( $pp->result, { 'result' => 'none', 'disposition' => 'none', 'dkim' => '', 'spf' => '', 'reason' => [{ 'comment' => 'host.nonexistent-tld not in DNS', 'type' => 'other', }], }, "result, none, nonexist" ) or diag Data::Dumper::Dumper( $pp->result ); } } sub test_published { _test_pass_strict(); _test_pass_relaxed(); _test_fail_strict('reject'); _test_fail_strict('none'); _test_fail_strict('quarantine'); _test_fail_sampled_out(); _test_fail_nonexist(); } sub test_no_policy { $pp->init(); $pp->header_from( 'nodmarcrecord.blogspot.com' ); $pp->validate(); my $skip_reason; if ( !$pp->result->reason ) { # typically a DNS failure, $skip_reason = "look like DNS is not working"; }; SKIP: { skip $skip_reason, 1 if $skip_reason; is_deeply( $pp->result, { 'result' => 'none', 'disposition' => 'none', 'dkim' => '', 'spf' => '', 'reason' => [{ 'comment' => 'no policy', 'type' => 'other', }], }, "result, fail, nonexist" ) or diag Data::Dumper::Dumper( $pp->result ); }; } sub test_disposition { # positive tests foreach (qw/ none reject quarantine NONE REJECT QUARANTINE /) { ok( $result->disposition($_), "disposition, $_" ); } # negative tests foreach (qw/ non rejec quarantin NON REJEC QUARANTIN /) { eval { $result->disposition($_) }; chomp $@; ok( $@, "disposition, neg, $_, $@" ); } } sub test_dkim { test_pass_fail('dkim'); } sub test_dkim_align { strict_relaxed('dkim_align'); } sub test_dkim_meta { ok( $result->dkim_meta( { domain => 'test' } ), "dkim_meta" ); } sub test_spf { test_pass_fail('spf'); } sub test_spf_align { strict_relaxed('spf_align'); } sub test_reason { # positive tests foreach ( qw/ forwarded sampled_out trusted_forwarder mailing_list local_policy other / ) { ok( $result->reason( type => $_, comment => "test comment" ), "reason type: $_" ); } # negative tests foreach (qw/ any reason not in above list /) { eval { $result->reason( type => $_ ) }; chomp $@; ok( $@, "reason, $_, $@" ); } } sub test_result { test_pass_fail('result'); } sub test_pass_fail { my $sub = shift; # positive tests foreach (qw/ pass fail PASS FAIL /) { ok( $result->$sub($_), "$sub, $_" ); } # negative tests foreach (qw/ pas fai PAS FAI /) { eval { $result->$sub($_) }; chomp $@; ok( $@, "$sub, neg, $_, $@" ); } } sub strict_relaxed { my $sub = shift; # positive tests foreach (qw/ strict relaxed STRICT RELAXED /) { ok( $result->$sub($_), "$sub, $_" ); } # negative tests foreach (qw/ stric relaxe STRIC RELAXE /) { eval { $result->$sub($_) }; chomp $@; ok( $@, "$sub, neg, $_, $@" ); } } Mail-DMARC-1.20240314/t/09.HTTP.t000444000765000024 424614574361234 14663 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Net::DNS::Resolver::Mock; use Test::More; use Test::File::ShareDir -share => { -dist => { 'Mail-DMARC' => 'share' } }; use lib 'lib'; foreach my $req ( 'CGI', 'DBD::SQLite 1.31', 'JSON', 'Net::Server::HTTP' ) { eval "use $req"; if ($@) { plan( skip_all => "$req not available" ); exit; } }; my $resolver = new Net::DNS::Resolver::Mock(); $resolver->zonefile_parse(join("\n", 'tnpi.net. 600 A 66.128.51.170', '_dmarc.tnpi.net. 600 TXT "v=DMARC1; p=reject; rua=mailto:dmarc-feedback@theartfarm.com; ruf=mailto:dmarc-feedback@theartfarm.com; pct=100"', #'tnpi.net. 600 MX 10 mail.theartfarm.com.', '')); my $mod = 'Mail::DMARC::HTTP'; use_ok($mod); my $http = $mod->new; isa_ok( $http, $mod ); my $cgi = CGI->new(); my $r = Mail::DMARC::HTTP::serve_validator($cgi, $resolver); ok($r eq 'missing POST data', "serve_validator, missing POST data"); $cgi->param('POSTDATA', 'foo'); $r = Mail::DMARC::HTTP::serve_validator($cgi, $resolver); like($r, qr/expected/, "serve_validator, invalid JSON"); $cgi->param('POSTDATA', '{"foo":"bar"}'); $r = Mail::DMARC::HTTP::serve_validator($cgi, $resolver); like($r, qr/no header_from/, "serve_validator, missing header_from"); $cgi->param('POSTDATA', '{"header_from":"tnpi.net"}'); $r = Mail::DMARC::HTTP::serve_validator($cgi, $resolver); like($r, qr/"spf":""/, "serve_validator, missing SPF"); like($r, qr/"dkim":"fail"/, "serve_validator, missing DKIM"); $cgi->param('POSTDATA', '{"header_from":"tnpi.net","spf":[{"domain":"tnpi.net","scope":"mfrom","result":"pass"}]}'); $r = Mail::DMARC::HTTP::serve_validator($cgi, $resolver); like($r, qr/"spf":"pass"/, "serve_validator, pass SPF"); like($r, qr/"dkim":"fail"/, "serve_validator, missing DKIM"); $cgi->param('POSTDATA', '{"header_from":"tnpi.net","dkim":[{"domain":"tnpi.net","selector":"mar2013","result":"pass"}]}'); $r = Mail::DMARC::HTTP::serve_validator($cgi, $resolver); like($r, qr/"spf":""/, "serve_validator, missing SPF"); like($r, qr/"dkim":"pass"/, "serve_validator, pass DKIM"); # this starts up the httpd daemon #$http->dmarc_httpd(); done_testing(); Mail-DMARC-1.20240314/t/10.Report.t000444000765000024 443014574361234 15342 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use IO::Compress::Gzip; use IO::Uncompress::Gunzip qw($GunzipError); #use IO::Compress::Zip; # legacy format #use IO::Uncompress::Unzip qw($UnzipError); use lib 'lib'; eval "use DBD::SQLite 1.31"; if ($@) { plan( skip_all => 'DBD::SQLite not available' ); exit; } my $mod = 'Mail::DMARC::PurePerl'; use_ok($mod); my $dmarc = $mod->new; isa_ok( $dmarc, $mod ); # this is equivalent to: # Mail::DMARC::Report( dmarc => $dmarc ); my $report = $dmarc->report; isa_ok( $report, 'Mail::DMARC::Report' ); isa_ok( $report->sendit, 'Mail::DMARC::Report::Send' ); isa_ok( $report->store, 'Mail::DMARC::Report::Store' ); isa_ok( $report->receive, 'Mail::DMARC::Report::Receive' ); my $test_dom = 'tnpi.net'; test_compress(); #setup_dmarc_result() or die "failed setup\n"; #$dmarc->report->store() or diag Dumper( $dmarc->report ); #unlink $test_db_file; done_testing(); exit; sub setup_dmarc_result { $dmarc->init(); $dmarc->header_from($test_dom); $dmarc->source_ip('192.2.1.1'); $dmarc->dkim( [ { domain => $test_dom, result => 'pass', selector => 'apr2013' } ] ); $dmarc->spf( { domain => $test_dom, scope => 'mfrom', result => 'pass' } ); $dmarc->validate() or diag Dumper($dmarc) and return; delete $dmarc->result->{published}; is_deeply( $dmarc->result, { 'result' => 'pass', 'disposition' => 'none', 'dkim_meta' => { 'domain' => 'tnpi.net', 'identity' => '', 'selector' => 'apr2013', }, 'dkim' => 'pass', 'spf' => 'pass', 'dkim_align' => 'strict', 'spf_align' => 'strict', }, "result, pass, strict, $test_dom" ) or diag Dumper( $dmarc->result ); } sub test_compress { # has to be moderately large to overcome zip format overhead my $xml = '' x 200; my $compressed = $report->compress( \$xml ); ok( length $xml > length $compressed, 'compress_report' ); my $decompressed; IO::Uncompress::Gunzip::gunzip( \$compressed => \$decompressed ) or die "unzip failed: $GunzipError\n"; cmp_ok( $decompressed, 'eq', $xml, "compress_report, extracts" ); } Mail-DMARC-1.20240314/t/11.Report.Store.t000444000765000024 545314574361234 16444 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Net::DNS::Resolver::Mock; use Test::More; use Test::File::ShareDir -share => { -dist => { 'Mail-DMARC' => 'share' } }; use lib 'lib'; eval "use DBD::SQLite 1.31"; if ($@) { plan( skip_all => 'DBD::SQLite not available' ); exit; } my $resolver = new Net::DNS::Resolver::Mock(); $resolver->zonefile_parse(join("\n", 'tnpi.net. 600 A 66.128.51.170', 'tnpi.net. 600 MX 10 mail.theartfarm.com.', '_dmarc.mail-dmarc.tnpi.net. 600 TXT "v=DMARC1; p=reject; rua=mailto:invalid@theartfarm.com; ruf=mailto:invalid@theartfarm.com; pct=90"', '_dmarc.tnpi.net. 600 TXT "v=DMARC1; p=reject; rua=mailto:dmarc-feedback@theartfarm.com; ruf=mailto:dmarc-feedback@theartfarm.com; pct=100"', 'mail-dmarc.tnpi.net. 600 TXT "test zone for Mail::DMARC perl module"', 'mail-dmarc.tnpi.net._report._dmarc.theartfarm.com. 600 TXT "v=DMARC1; rua=mailto:invalid-test@theartfarm.com;"', '')); use_ok('Mail::DMARC::PurePerl'); my $dmarc = Mail::DMARC::PurePerl->new(); $dmarc->set_resolver($resolver); isa_ok( $dmarc, 'Mail::DMARC::PurePerl' ); isa_ok( $dmarc->report, 'Mail::DMARC::Report' ); isa_ok( $dmarc->report->store, 'Mail::DMARC::Report::Store' ); ok( $dmarc->report->store->backend, "selected backend loaded" ); my $test_dom = 'tnpi.net'; # gotta have something to store. Populate a DMARC object setup_dmarc_result() or die "failed setup\n"; # tell storage backend to use test settings $dmarc->report->store->backend->config('t/mail-dmarc.ini'); test_reason(); done_testing(); exit; sub test_reason { ok( $dmarc->result->reason( type => 'other', comment => 'testing' ), "reason"); } sub setup_dmarc_result { $dmarc->init(); ok( $dmarc->header_from($test_dom), "header_from" ); ok( $dmarc->envelope_to('recipient.com'), 'envelope_to' ); ok( $dmarc->source_ip('192.2.1.1'), 'source_ip' ); $dmarc->dkim([ { domain => $test_dom, result => 'pass', selector => 'apr2013' } ]); $dmarc->spf({ domain => $test_dom, scope => 'mfrom', result => 'pass' } ); $dmarc->validate() or diag Dumper($dmarc) and return; my $pub = delete $dmarc->result->{published}; ok( $pub, "pub" ); is_deeply( $dmarc->result, { 'result' => 'pass', 'disposition' => 'none', 'dkim_meta' => { 'domain' => 'tnpi.net', 'identity' => '', 'selector' => 'apr2013', }, 'dkim' => 'pass', 'spf' => 'pass', 'dkim_align' => 'strict', 'spf_align' => 'strict', 'reason' => [], }, "result, pass, strict, $test_dom" ) or diag Dumper( $dmarc->result ); return $dmarc->result->published($pub); } Mail-DMARC-1.20240314/t/12.Report.Store.SQL.t000444000765000024 4473614574361234 17132 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use Test::Output; $Data::Dumper::Sortkeys = 1; use lib 'lib'; require Mail::DMARC::Report; require Mail::DMARC::Policy; my ($report_id, $rr_id, $policy, $reasons); my $begin = time - 10000; my $end = time - 100; my $test_domain = 'example.com'; my $dkim = [ { domain => 'from.com', selector => 'blah1', result => 'pass', human_result => 'yay' }, { domain => 'example.com', selector => 'blah2', result => 'pass', human_result => undef, }, { domain => 'example.com', selector => 'blah3', result => 'pass', }, ]; my $spf = [ { 'domain' => 'from.com', 'result' => 'pass', 'scope' => 'helo' }, { 'domain' => 'from.com', 'result' => 'pass', 'scope' => 'mfrom' }, { 'domain' => 'example.com', 'result' => 'fail', 'scope' => 'mfrom' } ]; my $mod = 'Mail::DMARC::Report::Store::SQL'; use_ok($mod); my $sql = $mod->new; isa_ok( $sql, $mod ); my $backend_dir = './t/backends'; opendir( my $dir, $backend_dir ) || die "Unable to view backends in $backend_dir"; # The general gist of the tests is: # test query mechanisms # build and store an aggregate report, as it would happen In Real Life # retrieve an aggregate report, as if reporting it # validate the consistency of what was stored and retrieved # We need to run the tests for every back-end type. # This includes all Grammars for SQL, but it also could mean other backends # that aren't currently supported. while ( my $file = readdir( $dir ) ) { my ($provider) = $file =~ /mail-dmarc\.sql\.(\w+)\.ini/i; if ( ! $provider ) { next; } eval "use DBD::$provider"; if ($@) { ok( 1, "Skipping $provider, DBD::$provider not available" ); next; } $sql->config( "$backend_dir/$file" ); if ( $provider eq 'Pg' ) { $provider = 'PostgreSQL'; } if ( $provider eq 'mysql' ) { $provider = 'MySQL'; } test_db_connect( $provider ) or do { ok(1, "Skipping $provider, unable to connect"); next; }; test_grammar_loaded( $provider ); test_insert_error( $provider ); test_query_replace(); test_query_update(); test_query_delete(); test_query(); test_query_any(); test_get_report_id(); # creates a test report # we need to run get_report_id before ip_store_and_fetch #  so that ip_store_and_fetch has a report to work with. test_ip_store_and_fetch(); test_insert_policy_published(); test_get_report_policy_published(); test_insert_rr(); test_insert_rr_spf(); test_insert_rr_dkim(); test_insert_rr_reason(); test_retrieve(); test_retrieve_todo(); test_get_author_id(3); test_get_report(); test_get_row_reason(); test_get_row_spf(); test_get_row_dkim(); test_populate_agg_metadata(); test_populate_agg_records(); test_cleanup( $provider ); } closedir( $dir ); done_testing(); exit; sub test_insert_error { my ($provider) = @_; my $msg = "STDERR has expected warning ($provider)"; if ($provider eq 'PostgreSQL') { stderr_is { test_query_insert() } 'DBI error: ERROR: relation "reporting" does not exist LINE 1: INSERT INTO "reporting" ("domain", "begin", "end") VALUES ($... ^ DBI error: ERROR: column "domin" of relation "report" does not exist LINE 1: INSERT INTO "report" ("domin", "begin", "end") VALUES ($1, $... ^ ', $msg; } elsif ($provider eq 'SQLite') { stderr_is { test_query_insert() } 'DBI error: no such table: reporting DBI error: table report has no column named domin ', $msg; } elsif ($provider eq 'MySQL') { stderr_is { test_query_insert() } 'DBI error: Table \'dmarc_report.reporting\' doesn\'t exist DBI error: Unknown column \'domin\' in \'field list\' ', $msg; } } sub test_cleanup { my ($provider) = @_; if ( $provider eq 'PostgreSQL' ) { ok ( $sql->query( 'TRUNCATE author, domain, report, report_error, report_policy_published, report_record, report_record_dkim, report_record_reason, report_record_spf RESTART IDENTITY;' ), 'truncate_testing_pg_database' ); return; } my $reports = $sql->get_report()->{rows}; foreach my $report (@$reports) { # print Dumper($report); $sql->delete_report($report->{rid}); } $reports = $sql->get_report()->{rows}; if (scalar @$reports) { # print Dumper($reports); die "failed to delete reports!\n"; } if ($provider eq 'SQLite') { unlink "t/reports-test.sqlite"; } } sub test_populate_agg_records { my $agg = Mail::DMARC::Report::Aggregate->new(); my $r = $sql->populate_agg_records( \$agg, $report_id ); ok( $r, "populate_agg_records"); # human result is returned undef from SQL, but absent during insertion # delete $r->[0]{auth_results}{dkim}[2]{human_result}; my $expected = Mail::DMARC::Report::Aggregate::Record->new( auth_results => { 'dkim' => $dkim, 'spf' => $spf, }, identifiers => { header_from => 'from.com', envelope_to => 'to.com', envelope_from => 'from.com', }, row => { 'count' => 1, 'policy_evaluated' => { disposition => 'none', dkim => 'pass', spf => 'pass', reason => $reasons, }, 'source_ip' => '192.1.1.1' }, ); $expected->auth_results->dkim->[2]{human_result} = undef; is_deeply( $r, [$expected], "populate_agg_records, deeply") or diag Dumper($r, [$expected]); } sub test_populate_agg_metadata { my $query = $sql->grammar->select_from( [ 'id AS rid', 'begin', 'end' ], 'report' ); $query .= $sql->grammar->and_arg( 'id' ); my $report = $sql->query( $query, [ $report_id ] )->[0]; my $agg = Mail::DMARC::Report::Aggregate->new(); ok( $sql->populate_agg_metadata( \$agg, \$report ), "populate_agg_metadata"); is_deeply( $agg->metadata, { 'config_file' => 'mail-dmarc.ini', 'date_range' => { 'begin' => $report->{begin}, 'end' => $report->{end}, }, 'email' => 'noreply@example.com', 'extra_contact_info' => 'http://www.example.com/dmarc-policy/', 'org_name' => 'My Great Company', 'report_id' => 2, }, "populate_agg_metadata, deeply" ) or diag Dumper($agg); } sub test_get_report_policy_published { my $pp = $sql->get_report_policy_published( $report_id ); $pp->apply_defaults; $pp->domain('recip.example.com'); foreach ( qw/ sp pct / ) { delete $pp->{$_} if ! defined $pp->$_; }; delete $pp->{report_id}; delete $policy->{uri}; delete $pp->{id}; ok( $pp, "get_report_policy_published"); is_deeply( $pp, $policy, "get_report_policy_published, deeply" ) or diag Dumper( $pp, $policy ); } sub test_retrieve { my $r = $sql->retrieve; ok( scalar @$r, "retrieve, " . scalar @$r ); my %tests = ( rid => 2, author => 'Test Company', from_domain => 'recip.example.com', begin => $begin, end => $end, ); foreach ( keys %tests ) { my $r = $sql->retrieve( $_ => $tests{$_} ); ok( @$r, "retrieve, $_, " . scalar @$r ); }; } sub test_retrieve_todo { my $r = $sql->retrieve_todo(); ok( $r, "retrieve_todo"); # warn Dumper($r); # die $r->as_xml; } sub test_get_row_reason { ok( $sql->get_row_reason( $rr_id ), 'get_row_reason'); } sub test_get_row_spf { ok( $sql->get_row_spf( $rr_id ), 'get_row_spf'); } sub test_get_row_dkim { ok( $sql->get_row_dkim( $rr_id ), 'get_row_dkim'); } sub test_get_report { my $reports = $sql->get_report( rid => $report_id )->{rows}; ok( scalar @$reports, "get_report, no limits, " . scalar @$reports ); my $limit = 10; my $r = $sql->get_report( rows => $limit )->{rows}; if ( ! $r || ! scalar @$r || scalar @$r < $limit ) { ok( 1, "skipping author tests" ); return; }; cmp_ok( scalar @$reports, '==', $limit, "get_report, limit $limit" ); my @queries = ( author => 'The Art Farm', author => 'google.com', from_domain => 'theartfarm.com', recipient => 'google.com', recipient => 'yahoo.com', ); while ( my $key = shift @queries ) { my $val = shift @queries; $r = $sql->get_report( $key => $val ); $reports = $r->{rows}; ok( scalar @$reports, "get_report, $key, $val, " . scalar @$reports ); }; $reports = $sql->get_report( rows => 1, sord => 'desc', sidx => 'rid' ); ok( $reports->{rows}, "get_report, multisearch"); } sub test_get_author_id { my $times = shift or return; my %meta = ( org_name => "Test $times Company", email => 'dmarc-reporter@example.com', extra_contact_info => undef, report_id => undef, begin => time, end => time + 10, ); my $report = Mail::DMARC::Report->new(); foreach ( keys %meta ) { next if ! defined $_; next if ! defined $meta{$_}; ok( $report->aggregate->metadata->$_( $meta{$_} ), "meta, $_, set" ); } my $policy = Mail::DMARC::Policy->new("v=DMARC1; p=reject"); ok( $policy->rua( 'mailto:' . $sql->config->{organization}{email} ), "policy, rua, set"); ok( $policy->domain( 'recip.example.com'), "policy, domain, set"); ok( $report->aggregate->policy_published( $policy ), "policy published, set"); # find a matching report, or create a new one my $rid = $sql->get_report_id( $report->aggregate ); ok( $rid, "get_report_id, $rid" ); my $authors = $sql->get_author_id( $report->aggregate->metadata ); test_get_author_id($times - 1); } sub test_get_report_id { my %meta = ( org_name => 'Test Company', email => 'dmarc-reporter@example.com', begin => $begin, end => $end, ); my $report = Mail::DMARC::Report->new(); foreach ( keys %meta ) { ok( $report->aggregate->metadata->$_( $meta{$_} ), "meta, $_, set" ); } $policy = Mail::DMARC::Policy->new("v=DMARC1; p=reject"); $policy->apply_defaults; ok( $policy->rua( 'mailto:' . $sql->config->{organization}{email} ), "policy, rua, set"); ok( $policy->domain( 'recip.example.com'), "policy, domain, set"); ok( $report->aggregate->policy_published( $policy ), "policy published, set"); # find a matching report, or create a new one $report_id = $sql->get_report_id( $report->aggregate ); ok( $report_id, "get_report_id, $report_id" ); } sub test_insert_rr_reason { ok ( $rr_id, "at_test_insert_rr_reason with $rr_id"); my @reasons = qw/ forwarded local_policy mailing_list other sampled_out trusted_forwarder /; $reasons = undef; foreach my $r ( @reasons) { push @$reasons, bless { type => $r, comment => "test $r comment" }, 'Mail::DMARC'; my $rrid = $sql->insert_rr_reason( $rr_id, $r, "test $r comment" ); ok($rrid , "insert_rr_reason, $r" ) or diag Dumper($rrid); } } sub test_insert_rr_dkim { ok ( $rr_id, "at_test_insert_rr_dkim with $rr_id"); ok( $sql->insert_rr_dkim( $rr_id, $dkim->[0] ), 'insert_rr_dkim' ); ok( $sql->insert_rr_dkim( $rr_id, $dkim->[1] ), 'insert_rr_dkim' ); ok( $sql->insert_rr_dkim( $rr_id, $dkim->[2] ), 'insert_rr_dkim' ); } sub test_insert_rr_spf { ok ( $rr_id, "at_test_insert_rr_spf with $rr_id"); foreach ( @$spf ) { ok( $sql->insert_rr_spf( $rr_id, $_ ), 'insert_rr_spf' ); }; } sub test_insert_rr { my $record = Mail::DMARC::Report::Aggregate::Record->new; $record->identifiers( header_from => 'from.com', envelope_to => 'to.com', envelope_from => 'from.com', ); $record->row( source_ip => '192.1.1.1', policy_evaluated => { disposition => 'none', dkim => 'pass', spf => 'pass', } ); $rr_id = $sql->insert_rr( $report_id, $record ); ok( $rr_id, "insert_rr, $rr_id" ); } sub test_insert_policy_published { my $pol = Mail::DMARC::Policy->new('v=DMARC1; p=reject'); $pol->apply_defaults; $pol->rua( 'mailto:' . $sql->config->{organization}{email} ); # warn Dumper($policy); my $r = $sql->insert_policy_published( $report_id, $pol ); ok( $r, 'insert_policy_published' ); } sub test_ip_store_and_fetch { my @test_ips = ( '1.1.1.1', '10.0.1.1', '2002:4c79:6240::1610:9fff:fee5:fb5', '2607:f060:b008:feed::6', ); foreach my $ip (@test_ips) { my $ipbin = $ip; if ( $sql->grammar->language ne 'postgresql' ) { $ipbin = $sql->any_inet_pton($ip); ok( $ipbin, "any_inet_pton, $ip" ); my $pres = $sql->any_inet_ntop($ipbin); ok( $pres, "any_inet_ntop, $ip" ); compare_any_inet_round_trip( $ip, $pres ); } my $r_id = $sql->query( $sql->grammar->insert_into( 'report_record', [ 'report_id', 'source_ip', 'disposition', 'dkim', 'spf', 'header_from_did' ] ), [ $report_id, $ipbin, 'none', 'pass', 'pass', 1 ] ) or die "failed to insert?"; my $rr_ref = $sql->query( $sql->grammar->select_from( [ 'id', 'source_ip' ], 'report_record' ) . $sql->grammar->and_arg('id'), [ $r_id ] ); ok( scalar @$rr_ref, 'records_retrieved' ); if ( $sql->grammar->language eq 'postgresql' ) { compare_any_inet_round_trip( $ip, $rr_ref->[0]{source_ip} ); } else { compare_any_inet_round_trip( $ip, $sql->any_inet_ntop( $rr_ref->[0]{source_ip} ), ); } $sql->query( $sql->grammar->delete_from( 'report_record' ).$sql->grammar->and_arg( 'id' ), [ $r_id ] ); } } sub test_query { ok( $sql->query( $sql->grammar->select_from( [ 'id' ], 'report' ) ), "query" ); } sub test_query_insert { my $end = time + 86400; my $from_did = $sql->query( $sql->grammar->insert_domain, [ 'ignore.test.com' ] ); my $author_id = $sql->query( $sql->grammar->insert_into( 'author', [ 'org_name' ] ), [ 'test' ] ); my $rid = $sql->query( $sql->grammar->insert_into( 'report', [ 'from_domain_id', 'begin', 'end', 'author_id' ] ), [ $from_did, $begin, $end, $author_id ] ); ok( $rid, "query_insert, report, $rid" ); ok( $sql->delete_report($rid), "delete_report, report, $rid"); # negative tests eval { $rid = $sql->query( $sql->grammar->insert_into( 'reporting', [ 'domain', 'begin', 'end' ] ), [ $test_domain, $begin, $end ] ); }; chomp $@; ok( $@, "query_insert, report, neg: $@" ); eval { $rid = $sql->query( $sql->grammar->insert_into( 'report', [ 'domin', 'begin', 'end' ] ), [ 'a' x 257, 'yellow', $end ] ); }; chomp $@; ok( $@, "query_insert, report, neg: $@" ); } sub test_query_replace { my $end = time + 86400; my $snafus = $sql->query( $sql->grammar->select_from( [ 'id' ], 'report' ).$sql->grammar->and_arg('begin'), [ $begin ] ); foreach my $s (@$snafus) { ok( $sql->query( $sql->grammar->replace_into( 'report', [ 'id', 'domain', 'begin', 'end' ] ), [ $s->{id}, $test_domain, $begin, $end ] ), "query_replace" ); } # negative eval { $sql->query( $sql->grammar->replace_into( 'rep0rt', [ 'id', 'domain', 'begin', 'end' ] ), [ 1, 1, 1, 1 ] ); }; chomp $@; ok( $@, "replace, negative, $@" ); } sub test_query_update { my $victims = $sql->query($sql->grammar->select_from( [ 'id' ], 'report' ).$sql->grammar->limit); foreach my $v (@$victims) { my $r = $sql->query( $sql->grammar->update( 'report', [ 'end' ] ).$sql->grammar->and_arg( 'id' ), [ time, $v->{id} ] ); ok( $r, "query_update, $r" ); # negative test eval { $sql->query( $sql->grammar->update( 'report', [ 'ed' ] ).$sql->grammar->and_arg( 'id' ), [ time, $v->{id} ] ); }; ok( $@, "query_update, neg" ); } } sub test_query_delete { my $victims = $sql->query($sql->grammar->select_from( [ 'id' ], 'report' ).$sql->grammar->limit(1)); foreach my $v (@$victims) { # print "test_query_delete victim: $v->{id}\n"; eval { my $r = $sql->delete_report($v->{id}); ok( $r, "query_delete $v->{id}" ); }; warn $@ if ($@); } # neg eval { $sql->query( $sql->grammar->delete_from( 'repor' ).$sql->grammar->and_arg( 'id' ), [ 1 ] ); }; chomp $@; ok( $@, "delete, negative, $@" ); } sub test_query_any { foreach my $table (qw/ report author domain report_record /) { my $r = $sql->query("SELECT id FROM $table LIMIT 1"); ok( $r, "query, select, $table" ); } # negative eval { $sql->query("SELECT id FROM rep0rt LIMIT 1") }; chomp $@; ok( $@, "query, select, negative, $@" ); } sub test_db_connect { my ($grammar) = @_; my $dbh; eval { $dbh = $sql->db_connect(); }; if ($@) { warn $@; return 0; } ok( $dbh, "db_connect: $grammar" ); isa_ok( $dbh, "DBIx::Simple" ); return 1; } sub test_grammar_loaded { my ($grammarName) = @_; isa_ok( $sql->grammar(), "Mail::DMARC::Report::Store::SQL::Grammars::$grammarName" ); } sub compare_any_inet_round_trip { my ( $ip, $pres ) = @_; if ( $pres eq $ip ) { cmp_ok( $pres, 'eq', $ip, "any_inet_ntop, round_trip, $ip" ); } else { # on some systems, a :: pattern gets a zero inserted. Mimic that my $zero_filled = $ip; $zero_filled =~ s/::/:0:/g; cmp_ok( $pres, 'eq', $zero_filled, "any_inet_ntop, round_trip, zero-pad, $ip" ) or diag "presentation: $zero_filled\nresult: $pres"; } } Mail-DMARC-1.20240314/t/13.Report.Aggregate.t000444000765000024 425014574361234 17232 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use lib 'lib'; use Mail::DMARC::Policy; use Mail::DMARC::Report::Aggregate::Record; eval "use DBD::SQLite 1.31"; if ($@) { plan( skip_all => 'DBD::SQLite not available' ); exit; } my $mod = 'Mail::DMARC::Report::Aggregate'; use_ok($mod); my $agg = $mod->new; isa_ok( $agg, $mod ); my $ip = '192.2.1.1'; my $test_r = Mail::DMARC::Report::Aggregate::Record->new( identifiers => { header_from => 'example.com', envelope_from => 'example.com', }, auth_results => { dkim => [ ], spf => [ ] }, row => { source_ip => $ip, count => 1, policy_evaluated => { disposition=>'none', dkim => 'pass', spf=>'pass' }, }, ); test_metadata_isa(); test_record(); test_policy_published(); test_as_xml(); done_testing(); exit; sub test_metadata_isa { isa_ok( $agg->metadata, "Mail::DMARC::Report::Aggregate::Metadata"); }; sub test_policy_published { ok( ! defined $agg->policy_published, "policy_published, empty" ); my $pol = Mail::DMARC::Policy->new(); $pol->apply_defaults; $pol->domain('test.com'); ok( $agg->policy_published($pol), "policy_published, default" ); } sub test_record { is_deeply( $agg->record, [],"Mail::DMARC::Report::Aggregate::Record, empty"); my $r; eval { $r = $agg->record( $test_r ) }; ok( $r, "record, test") or diag Dumper($r); #delete $agg->record->[0]{config_file}; is_deeply( $agg->record, [ $test_r ], "record, deeply"); ok( $agg->record( $test_r ), "record, empty, again"); #delete $agg->record->[1]{config_file}; is_deeply( $agg->record, [ $test_r,$test_r ], "record, deeply, multiple"); }; sub test_as_xml { $agg->metadata->report_id(1); foreach my $m ( qw/ org_name email extra_contact_info error uuid / ) { $agg->metadata->$m("test"); }; foreach my $m ( qw/ begin end / ) { $agg->metadata->$m(time); }; #$agg->record( $test_r ); ok( $agg->metadata->as_xml(), "metadata, as_xml"); ok( $agg->get_policy_published_as_xml(), "policy_published, as_xml"); ok( $agg->get_record_as_xml(), "record, as_xml"); ok( $agg->as_xml(), "as_xml"); }; Mail-DMARC-1.20240314/t/14.Report.Aggregate.Metadata.t000444000765000024 556314574361234 20762 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use lib 'lib'; eval "use DBD::SQLite 1.31"; if ($@) { plan( skip_all => 'DBD::SQLite not available' ); exit; } my $mod = 'Mail::DMARC::Report::Aggregate'; use_ok($mod); my $agg = $mod->new; isa_ok( $agg, $mod ); my $meta = $agg->metadata; isa_ok( $meta, 'Mail::DMARC::Report::Aggregate::Metadata' ); my $start = time; my $end = time + 10; test_org_name(); test_email(); test_extra_contact_info(); test_report_id(); test_date_range(); test_begin(); test_end(); test_error(); test_uuid(); test_as_xml(); done_testing(); exit; sub test_org_name { my $name = 'Test Org'; ok( $meta->org_name($name), "org_name, set"); cmp_ok( $meta->org_name, 'eq', $name, "org_name, get"); }; sub test_email { my $email = 'test@example.com'; ok( $meta->email( $email ), "test_email, set"); cmp_ok( $meta->email, 'eq', $email, "test_email, get"); }; sub test_extra_contact_info { my $eci = 'http://www.example.com/path/to/dmarc.cgi'; ok( $meta->extra_contact_info( $eci ), 'extra_contact_info, set'); cmp_ok( $meta->extra_contact_info, 'eq', $eci, "extra_contact_info, get"); }; sub test_report_id { my $id = '12345566677888@sender.com'; ok( $meta->report_id($id), "report_id, set"); cmp_ok( $meta->report_id, 'eq', $id, "report_id, get"); }; sub test_date_range { my $range_ref = {begin=>$start,end=>$end}; ok( $meta->date_range($range_ref), "date_range, set"); is_deeply( $meta->date_range, $range_ref, "date_range, get"); cmp_ok( $meta->begin, '==', $start, "date_range, get start"); cmp_ok( $meta->end, '==', $end, "date_range, get end"); }; sub test_begin { ok( $meta->begin( $start ), "begin, set"); cmp_ok( $meta->begin, '==', $start, "date_range, get start"); }; sub test_end { ok( $meta->end( $end ), "end, set"); cmp_ok( $meta->end, '==', $end, "date_range, get end"); }; sub test_error { my $test_errors = [ 'error #1 for test', 'error #2 for testing', ]; foreach ( @$test_errors ) { ok( $meta->error( $_ ), "error, $_"); }; is_deeply($meta->error, $test_errors, "error, deeply"); }; sub test_uuid { my $uuid = '1234908748913u41u4-1203847308924-adskfjadslfj-13i41230984'; ok( $meta->uuid($uuid), "uuid, set"); cmp_ok( $meta->uuid, 'eq', $uuid, "uuid, get"); }; sub test_as_xml { my $expected = <<"EO_XML" \t \t\tTest Org \t\ttest\@example.com \t\thttp://www.example.com/path/to/dmarc.cgi \t\t12345566677888\@sender.com \t\t \t\t\t$start \t\t\t$end \t\t \t\terror #1 for test \t\terror #2 for testing \t EO_XML ; chomp $expected; cmp_ok( $meta->as_xml, 'eq', $expected, "as_xml"); }; Mail-DMARC-1.20240314/t/15.Report.Aggregate.Record.t000444000765000024 570514574361234 20457 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use lib 'lib'; my $mod = 'Mail::DMARC::Report::Aggregate::Record'; use_ok($mod); my $rec = $mod->new; isa_ok( $rec, $mod ); my $ip = '192.2.1.1'; test_identifiers(); test_auth_results(); test_row(); done_testing(); exit; sub test_identifiers { my $id = $rec->identifiers; ok( $id->envelope_to( 'to.example.com' ), "envelope_to, set"); ok( $id->envelope_to eq 'to.example.com', "envelope_to, get"); ok( $id->header_from( 'from.example.com' ), "header_from, set"); ok( $id->header_from eq 'from.example.com', "header_from, get"); ok( $id->envelope_from( 'from.example.com' ), "envelope_from, set"); ok( $id->envelope_from eq 'from.example.com', "envelope_from, get"); # one shot $id = $rec->identifiers( envelope_to => 'to.example.com', header_from => 'from.example.com', envelope_from=> 'from.example.com', ); ok( $id->envelope_to eq 'to.example.com', "envelope_to, get"); ok( $id->header_from eq 'from.example.com', "header_from, get"); ok( $id->envelope_from eq 'from.example.com', "envelope_from, get"); }; sub test_auth_results { my $ar = $rec->auth_results; my $expected = bless { dkim => [], spf => [] }, 'Mail::DMARC::Report::Aggregate::Record::Auth_Results'; is_deeply( $ar, $expected, "auth_results, empty"); my $spf1 = { domain => 'first', result => 'none', scope => 'helo' }; $expected = { dkim => [], spf => [ $spf1 ]}; $ar->spf( { domain => 'first', result => 'none', scope => 'helo' } ); is_deeply( $ar, $expected, "auth_results, one SPF"); my $spf2 = { domain => 'second', scope => 'helo', result => 'temperror' }; $expected = { dkim => [], spf => [ $spf1, $spf2 ] }; $ar->spf( { domain => 'second', result => 'temperror', scope => 'helo' } ); is_deeply( $ar, $expected, "auth_results, two SPF"); my $dkim1 = { domain => 'first', result => 'none' }; $expected = { dkim => [ $dkim1 ], spf => [ $spf1, $spf2 ] }; $ar->dkim( $dkim1 ); is_deeply( $ar, $expected, "auth_results, two SPF, one DKIM"); my $dkim2 = { domain => 'second', result => 'none' }; $expected = { dkim => [ $dkim1, $dkim2 ], spf => [ $spf1, $spf2 ] }; $ar->dkim( $dkim2 ); is_deeply( $ar, $expected, "auth_results, two SPF, two DKIM"); }; sub test_row { my $ar = $rec->row; my $expected = bless {}, 'Mail::DMARC::Report::Aggregate::Record::Row'; is_deeply( $ar, $expected, "row, empty"); $ar->source_ip( $ip ); $expected = { source_ip => $ip }; is_deeply( $ar, $expected, "row, source_ip"); $ar->count( 1 ); $expected = { count => 1, source_ip => $ip }; is_deeply( $ar, $expected, "row, count"); my $pe = { disposition => 'none', spf => 'fail', dkim => 'fail' }; $ar->policy_evaluated( $pe ); $pe->{reason} = []; $expected = { policy_evaluated => $pe, count => 1, source_ip => $ip }; is_deeply( $ar, $expected, "row, policy_evaluated"); }; Mail-DMARC-1.20240314/t/16.Report.Aggregate.Record.Auth_Results.t000444000765000024 301114574361234 23065 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use lib 'lib'; my $mod = 'Mail::DMARC::Report::Aggregate::Record::Auth_Results'; use_ok($mod); my $ar = $mod->new; _auth_results(); _spf(); _dkim(); done_testing(); exit; sub _auth_results { isa_ok( $ar, $mod ); }; sub _spf { is_deeply( $ar->spf, [], "spf, empty"); my %spf_res = ( domain => 'test.com', result => 'pass', scope => 'mfrom', ); $ar->spf( %spf_res ); is_deeply( $ar->spf, [ \%spf_res ], "spf, hash"); $ar->spf( %spf_res ); is_deeply( $ar->spf, [ \%spf_res, \%spf_res ], "spf, hashref"); $ar = $mod->new; $ar->spf([ \%spf_res, \%spf_res ]); is_deeply( $ar->spf, [ \%spf_res, \%spf_res ], "spf, arrayref of hashref"); #warn Dumper($ar->spf); } sub _dkim { is_deeply( $ar->dkim, [], "dkim"); my %dkim_res = ( domain => 'tnpi.net', selector => 'jan2015', result => 'fail', human_result=> 'fail (body has been altered)', ); $ar->dkim( %dkim_res ); is_deeply( $ar->dkim, [ \%dkim_res ], "dkim, as hash"); $ar->dkim( \%dkim_res ); is_deeply( $ar->dkim, [ \%dkim_res, \%dkim_res ], "dkim, as hashref"); $ar->dkim( \%dkim_res ); is_deeply( $ar->dkim, [ \%dkim_res, \%dkim_res, \%dkim_res ], "dkim, as hashref again"); $ar = $mod->new; $ar->dkim([ \%dkim_res, \%dkim_res ]); is_deeply( $ar->dkim, [ \%dkim_res, \%dkim_res ], "dkim, as arrayref of hashrefs"); #warn Dumper($ar->dkim); } Mail-DMARC-1.20240314/t/17.Report.Aggregate.Schema.t000444000765000024 604114574361234 20435 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Net::DNS::Resolver::Mock; use Test::Exception; use Test::More; use Test::File::ShareDir -share => { -dist => { 'Mail-DMARC' => 'share' } }; use lib 'lib'; use Mail::DMARC::PurePerl; use Mail::DMARC::Report; eval "use DBD::SQLite 1.31"; if ($@) { plan( skip_all => 'DBD::SQLite not available' ); exit; } eval "use XML::SAX::ParserFactory;"; if ($@) { plan( skip_all => 'XML::SAX::ParserFactory not available' ); exit; } eval "use XML::Validator::Schema;"; if ($@) { plan( skip_all => 'XML::Validator::Schema not available' ); exit; } my $resolver = new Net::DNS::Resolver::Mock(); $resolver->zonefile_parse(join("\n", 'tnpi.net. 600 A 66.128.51.170', 'tnpi.net. 600 MX 10 mail.theartfarm.com.', '_dmarc.mail-dmarc.tnpi.net. 600 TXT "v=DMARC1; p=reject; rua=mailto:invalid@theartfarm.com; ruf=mailto:invalid@theartfarm.com; pct=90"', '_dmarc.tnpi.net. 600 TXT "v=DMARC1; p=reject; rua=mailto:dmarc-feedback@theartfarm.com; ruf=mailto:dmarc-feedback@theartfarm.com; pct=100"', 'mail-dmarc.tnpi.net. 600 TXT "test zone for Mail::DMARC perl module"', 'mail-dmarc.tnpi.net._report._dmarc.theartfarm.com. 600 TXT "v=DMARC1; rua=mailto:invalid-test@theartfarm.com;"', '')); my $dmarc = Mail::DMARC::PurePerl->new(); $dmarc->set_resolver($resolver); my $store = $dmarc->report->store; $store->config('t/mail-dmarc.ini'); $store->backend->config('t/mail-dmarc.ini'); die 'Not using test store' if $store->backend->config->{'report_store'}->{'dsn'} ne 'dbi:SQLite:dbname=t/reports-test.sqlite'; $dmarc->source_ip('66.128.51.165'); $dmarc->envelope_to('recipient.example.com'); $dmarc->envelope_from('dmarc-nonexist.tnpi.net'); $dmarc->header_from('mail-dmarc.tnpi.net'); $dmarc->dkim([ { domain => 'tnpi.net', selector => 'jan2015', result => 'fail', human_result=> 'fail (body has been altered)', } ]); $dmarc->spf([ { domain => 'tnpi.net', scope => 'mfrom', result => 'pass', }, { scope => 'helo', domain => 'mail.tnpi.net', result => 'fail', }, ]); my $policy = $dmarc->discover_policy; my $result = $dmarc->validate($policy); my $report_id = $dmarc->save_aggregate(); ok( $report_id, "saved report $report_id"); my $a = $store->backend->query('UPDATE report SET begin=begin-86400, end=end-86400 WHERE id=1'); $a = $store->backend->query('INSERT INTO report_error(report_id,error,time) VALUES(1," Test error & encoding",100)'); my $agg = $store->retrieve_todo()->[0]; test_against_schema(); done_testing(); exit; sub test_against_schema { $agg->metadata->report_id(1); my $xml = $agg->as_xml(); lives_ok( sub{ my $validator = XML::Validator::Schema->new(file => 'share/rua-schema.xsd'); my $parser = XML::SAX::ParserFactory->parser(Handler => $validator); $parser->parse_string( $xml ); }, 'Check schema' ); # print $xml; } Mail-DMARC-1.20240314/t/20.Report.URI.t000444000765000024 200214574361234 15772 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use lib 'lib'; my $mod = 'Mail::DMARC::Report::URI'; use_ok($mod); my $uri = $mod->new; isa_ok( $uri, $mod ); test_get_size_limit(); test_parse(); done_testing(); exit; sub test_get_size_limit { my %tests = ( '51m' => 53477376, '20k' => 20480, '5m' => 5242880, '10m' => 10485760, '1g' => 1073741824, '500' => 500, ); foreach my $t ( keys %tests ) { cmp_ok( $uri->get_size_limit($t), '==', $tests{$t}, "get_size_limit, $tests{$t}" ); } } sub test_parse { my @good = ( 'http://www.example.com/dmarc-feedback', 'https://www.example.com/dmarc-feedback', 'mailto:dmarc@example.com', 'mailto:dmarc-feedback@example.com,mailto:tld-test@thirdparty.example.net!10m', ); foreach (@good) { my $uris = $uri->parse($_); ok( $uris, "parse, $_" ); ok( scalar @$uris, "parse, count " . scalar @$uris ); } } Mail-DMARC-1.20240314/t/21.Report.Send.t000444000765000024 213514574361234 16234 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use Test::File::ShareDir -share => { -dist => { 'Mail-DMARC' => 'share' } }; use lib 'lib'; my $mod = 'Mail::DMARC::Report::Send'; use_ok($mod); my $send = $mod->new; isa_ok( $send, $mod ); isa_ok( $send->smtp, 'Mail::DMARC::Report::Send::SMTP' ); isa_ok( $send->http, 'Mail::DMARC::Report::Send::HTTP' ); my $body = $send->too_big_report( { uri => 'mailto:matt@example.com', report_bytes => 500000, report_id => 1, report_domain=> 'destination.com', } ); ok( $body, 'too_big_report'); #cmp_ok( $body, 'eq', sample_too_big(), 'too_big_report: content'); done_testing(); exit; sub sample_too_big { return <<'EO_TOO_BIG' This is a \'too big\' DMARC notice. The aggregate report was NOT delivered. Report-Date: Wed, 14 Aug 2013 22:15:04 -0700 Report-Domain: destination.com Report-ID: 1 Report-Size: 500000 Submitter: example.com Submitting-URI: mailto:matt@example.com Submitted by My Great Company Generated with Mail::DMARC EO_TOO_BIG ; }; Mail-DMARC-1.20240314/t/22.Report.Send.SMTP.t000444000765000024 703614574361234 17024 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Net::DNS::Resolver::Mock; use Test::More; use IO::Compress::Gzip; use lib 'lib'; use Mail::DMARC::Policy; use Mail::DMARC::Report::Aggregate; use Mail::DMARC::Report::Aggregate::Record; my $resolver = new Net::DNS::Resolver::Mock(); $resolver->zonefile_parse(join("\n", 'tnpi.net. 600 MX 10 mail.theartfarm.com.', '')); my $mod = 'Mail::DMARC::Report::Send::SMTP'; use_ok($mod); my $smtp = $mod->new; $smtp->set_resolver($resolver); isa_ok( $smtp, $mod ); $smtp->config('t/mail-dmarc.ini'); open my $REP, '<', 'share/rua-schema.xsd' or die "unable to open: $!"; my $report = join( '', <$REP> ); close $REP; my $zipped; IO::Compress::Gzip::gzip( \$report, \$zipped ) or die "unable to compress"; my $agg = Mail::DMARC::Report::Aggregate->new; my $pol = Mail::DMARC::Policy->new; $pol->domain('they.com'); $agg->policy_published( $pol ); $agg->metadata->begin( time - 10000 ); $agg->metadata->end( time - 100 ); $agg->metadata->report_id( '2013.06.01.6789' ); test_get_subject(); test_get_domain_mx(); test_get_smtp_hosts(); test_human_summary(); test_get_filename(); test_get_timestamp_rfc2822(); test_get_helo_hostname(); test_assemble_message(); done_testing(); exit; sub test_get_subject { my $subject = $smtp->get_subject( \$agg ); ok( $subject, "get_subject, $subject" ); }; sub test_get_helo_hostname { my $helo = $smtp->get_helo_hostname(); ok( $helo, "get_helo_hostname, $helo" ); }; sub test_get_timestamp_rfc2822 { my $r = $smtp->get_timestamp_rfc2822(); ok( $r, "get_timestamp_rfc2822, $r"); }; sub test_get_domain_mx { my %tests = ( 'tnpi.net' => [ { 'pref' => 10, 'addr' => 'mail.theartfarm.com' } ], ); foreach my $dom ( keys %tests ) { my $r = $smtp->get_domain_mx( $dom ); if (!$r || $r eq 'Does not exist') { print "it appears your DNS is not working.\n"; next; } ok( $r, "get_domain_mx, $dom"); is_deeply( $r, $tests{$dom}, "get_domain_mx, $dom, deeply"); # print Dumper($r); }; }; sub test_human_summary { my $record = Mail::DMARC::Report::Aggregate::Record->new( auth_results => { spf => [] }, identifiers => { header_from => 'they.com', }, row => { source_ip => '192.2.0.1', policy_evaluated => { disposition=>'none', dkim => 'pass', spf => 'fail' } } ); $agg->record( $record ); $record->row->policy_evaluated->dkim('fail'); $record->row->policy_evaluated->spf('pass'); $agg->record( $record ); $record->row->policy_evaluated->dkim('fail'); $record->row->policy_evaluated->spf('fail'); $agg->record( $record ); my $sum = $smtp->human_summary( \$agg ); ok( $sum, 'human_summary' ); # print $sum; } sub test_get_filename { my $name = $smtp->get_filename(\$agg); ok( $name, "get_filename, $name"); }; sub test_assemble_message { my $mess = $smtp->assemble_message_object( \$agg, 'matt@example.com', $zipped )->as_string; ok( $mess, "assemble_message_object" ); #warn print $mess; } sub test_get_smtp_hosts { my $initial_smarthost = $smtp->config->{smtp}{smarthost}; $smtp->config->{smtp}{smarthost} = undef; my $tnpi_expected = [ 'mail.theartfarm.com', 'tnpi.net' ]; my @hosts = $smtp->get_smtp_hosts('tnpi.net'); is_deeply( \@hosts, $tnpi_expected, "get_smtp_hosts, tnpi.net"); # print Dumper(\@hosts); $smtp->config->{smtp}{smarthost} = $initial_smarthost; } Mail-DMARC-1.20240314/t/23.Report.Send.HTTP.t000444000765000024 53514574361234 16776 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use lib 'lib'; foreach my $req ( 'Net::HTTP' ) { eval "use $req"; if ($@) { plan( skip_all => "$req not available" ); exit; } }; my $mod = 'Mail::DMARC::Report::Send::HTTP'; use_ok($mod); my $http = $mod->new; isa_ok( $http, $mod ); done_testing(); exit; Mail-DMARC-1.20240314/t/25.Report.Receive.t000444000765000024 417714574361234 16741 0ustar00mattstaff000000000000use strict; use warnings; use Data::Dumper; use Test::More; use IO::Compress::Gzip; use IO::Compress::Zip; use lib 'lib'; my $mod = 'Mail::DMARC::Report::Receive'; use_ok($mod); my $recv = $mod->new; isa_ok( $recv, $mod ); $recv->config('t/mail-dmarc.ini'); test_from_email_msg(); test_get_submitter_from_subject(); test_from_imap(); done_testing(); exit; sub test_from_imap { my $skip_reason = ''; eval "require Net::IMAP::Simple"; $skip_reason .= "Net::IMAP::Simple not installed" if $@; my $c = $recv->config->{imap}; if ( !$c->{server} || !$c->{user} || !$c->{pass} ) { $skip_reason .= " and \n" if $skip_reason; $skip_reason .= "imap not configured in mail-dmarc.ini"; } SKIP: { skip $skip_reason, 1 if $skip_reason; ok( $recv->from_imap(), "from_imap" ); } } sub test_get_submitter_from_subject { my %subjects = ( 'aol.com' => 'Subject: Report Domain:theartfarm.com Submitter:aol.com Report-ID:theartfarm.com_1366084800', 'ivenue.com' => 'Subject: Report Domain: tnpi.net Submitter: Ivenue.com Report-ID: tnpi.net-1366977854@Ivenue.com', 'hotmail.com' => 'Subject: =?utf-8?B?UmVwb3J0IERvbWFpbjogc2ltZXJzb24ubmV0IFN1Ym1pdHRlcjogaG90bWFpbC5jb20gUmVwb3J0LUlEOiA8YTY2YWVmZWIzZjI3NGNhYmJmZGM2MWMwMTVlNTg2N2VAaG90bWFpbC5jb20+?=', 'google.com' => 'Subject: Report domain: timbersmart.com Submitter: google.com Report-ID: 6022178961730607282', 'hotmail.com' => 'Subject: =?utf-8?B?UmVwb3J0IERvbWFpbjogbHluYm95ZXIuY29tIFN1Ym1pdHRlcjogaG90bWFpbC5jb20gUmVwb3J0LUlEOiA8MDJjNTM5YWY0ZjE2NGFlZGE3ZGQxZTdhYWJhOTc1MWJAaG90bWFpbC5jb20+?=', 'yahoo.com' => 'Subject: Report Domain: timbersmart.com Submitter: yahoo.com Report-ID: <1368868092.438744>', ); foreach my $dom ( keys %subjects ) { my $subject = $subjects{$dom}; cmp_ok( $recv->get_submitter_from_subject($subject), 'eq', $dom, "get_submitter_from_subject, $dom" ); } } sub test_from_email_msg { if ( -f 'report.msg' ) { ok( $recv->from_email_msg('report.msg'), 'from_email_msg' ); } } Mail-DMARC-1.20240314/t/26.Report.Sender.t000555000765000024 1110114574361234 16604 0ustar00mattstaff000000000000use strict; use warnings; use Test::More; use Net::DNS::Resolver::Mock; $ENV{MAIL_DMARC_CONFIG_FILE} = 't/mail-dmarc.ini'; use lib 'lib'; use Mail::DMARC::PurePerl; use Test::File::ShareDir -share => { -dist => { 'Mail-DMARC' => 'share' } }; use Mail::DMARC::Test::Transport; use Email::Sender::Transport::Failable; use Email::Sender::Transport::Test; my $resolver = new Net::DNS::Resolver::Mock(); $resolver->zonefile_parse(join("\n", 'fastmaildmarc.com. 600 MX 10 in1-smtp.messagingengine.com.', '_dmarc.fastmaildmarc.com. 600 TXT "v=DMARC1; p=reject; rua=mailto:rua@fastmaildmarc.com"', '')); # We test both method and object type callbacks foreach my $callback_type ( qw{ method object fail fallback } ) { subtest $callback_type => sub{ unlink 't/reports-test.sqlite' if -e 't/reports-test.sqlite'; # Clear test database for each run my $dmarc = Mail::DMARC::PurePerl->new; $dmarc->set_resolver($resolver); $dmarc->set_fake_time( time-86400); $dmarc->init(); $dmarc->source_ip('66.128.51.165'); $dmarc->envelope_to('fastmaildmarc.com'); $dmarc->envelope_from('fastmaildmarc.com'); $dmarc->header_from('fastmaildmarc.com'); $dmarc->dkim([ { domain => 'tnpi.net', selector => 'jan2015', result => 'fail', human_result=> 'fail (body has been altered)', } ]); $dmarc->spf([ { domain => 'tnpi.net', scope => 'mfrom', result => 'pass', }, { scope => 'helo', domain => 'mail.tnpi.net', result => 'fail', }, ]); my $policy = $dmarc->discover_policy; my $result = $dmarc->validate($policy); $dmarc->save_aggregate; $dmarc->set_fake_time( time+86400); use Mail::DMARC::Report::Sender; my $sender = Mail::DMARC::Report::Sender->new; my @deliveries; if ( $callback_type eq 'method' ) { my $transport = Email::Sender::Transport::Test->new; $sender->set_transports_method( sub{ my @transports; push @transports, $transport; return @transports; }); $sender->run; @deliveries = $transport->deliveries; } elsif ( $callback_type eq 'object' ) { my $transports = Mail::DMARC::Test::Transport->new; $sender->set_transports_object( $transports ); $sender->run; @deliveries = $transports->get_test_transport->deliveries; } elsif ( $callback_type eq 'fail' ) { my $transport = Email::Sender::Transport::Test->new; my $transport_fail = Email::Sender::Transport::Failable->new( transport => $transport, failure_conditions => [ sub{ return 1 } ], ); $sender->set_transports_method( sub{ my @transports; push @transports, $transport_fail; return @transports; }); $sender->run; @deliveries = $transport_fail->transport->deliveries; } elsif ( $callback_type eq 'fallback' ) { my $transport = Email::Sender::Transport::Test->new; my $transport_fail = Email::Sender::Transport::Failable->new( transport => $transport, failure_conditions => [ sub{ return 1 } ], ); $sender->set_transports_method( sub{ my @transports; push @transports, $transport_fail; push @transports, $transport; return @transports; }); $sender->run; @deliveries = $transport->deliveries; } else { die 'Unknown callback type in test'; } if ( $callback_type eq 'fail' ) { is( scalar @deliveries, 0, 'Email send fails' ); } else { is( scalar @deliveries, 1, '1 Email sent' ); is( $deliveries[0]->{envelope}->{to}->[0], 'rua@fastmaildmarc.com', 'Sent to correct address' ); my $body = ${$deliveries[0]->{email}->[0]->{body}}; is( $body =~ /This is a DMARC aggregate report for fastmaildmarc.com/, 1, 'Human readable description' ); is( $body =~ /1 records.\n0 passed.\n1 failed./, 1, 'Human readable summary'); is( $body =~ /Content-Type: application\/gzip/, 1, 'Gzip attachment' ); } }; } done_testing; Mail-DMARC-1.20240314/t/mail-dmarc.ini000444000765000024 274214574361234 16176 0ustar00mattstaff000000000000; This is YOU. DMARC reports include information about the reports. Enter it here. [organization] domain = example-test.com org_name = My Great Company email = noreply@example.com extra_contact_info = http://www.example.com/dmarc-policy/ ; aggregate DMARC reports need to be stored somewhere. Any database ; with a DBI module (MySQL, SQLite, DBD, etc.) should work. ; SQLite and MySQL and Postgresql are supported. ; Default is sqlite. [report_store] backend = SQL dsn = dbi:SQLite:dbname=t/reports-test.sqlite ;dsn = dbi:mysql:database=dmarc_report;host=db;port=3306 ;dsn = dbi:Pg:database=dmarc_report;host=db;port=5432 user = pass = ; backend can be perl or libopendmarc [dmarc] backend = perl [dns] timeout = 5 public_suffix_list = share/public_suffix_list [smtp] ; hostname is the external FQDN of this MTA hostname = mail.example.com cc = set.this@for.a.while.example.com whitelist = t/whitelist transports = Mail::DMARC::Test::Transport ; By default, we attempt to email directly to the report recipient. ; Set these to relay via a SMTP smart host. smarthost = smartuser = smartpass = [imap] server = mail.example.com user = pass = ; the imap folder where new dmarc messages will be found folder = dmarc ; the folders to store processed reports (a=aggregate, f=forensic) f_done = dmarc.forensic a_done = dmarc.aggregate [http] port = 8080 [https] port = 8443 ssl_crt = ssl_key = Mail-DMARC-1.20240314/t/whitelist000444000765000024 45014574361234 15400 0ustar00mattstaff000000000000# Format: IP *whitespace* type *whitespace* comment # Reason types: forwarded sampled_out trusted_forwarder mailing_list local_policy other # any IP without a type specified will default to 'other' # Comment is a free form entry 127.0.0.3 trusted_forwarder Test Comment 127.0.0.1 local_policy Mail-DMARC-1.20240314/t/backends000755000765000024 014574361234 15077 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/t/backends/mail-dmarc.sql.Pg.ini000444000765000024 251414574361234 21110 0ustar00mattstaff000000000000; This is YOU. DMARC reports include information about the reports. Enter it here. [organization] domain = example-test.com org_name = My Great Company email = noreply@example.com extra_contact_info = http://www.example.com/dmarc-policy/ ; aggregate DMARC reports need to be stored somewhere. Any database ; with a DBI module (MySQL, SQLite, DBD, etc.) should work. ; SQLite and MySQL and Postgresql are supported. ; Default is sqlite. [report_store] backend = SQL dsn = dbi:Pg:database=dmarc_report;port=5432 user = postgres pass = ; backend can be perl or libopendmarc [dmarc] backend = perl [dns] timeout = 5 public_suffix_list = share/public_suffix_list [smtp] ; hostname is the external FQDN of this MTA hostname = mail.example.com cc = set.this@for.a.while.example.com whitelist = t/whitelist ; By default, we attempt to email directly to the report recipient. ; Set these to relay via a SMTP smart host. smarthost = smartuser = smartpass = [imap] server = mail.example.com user = pass = ; the imap folder where new dmarc messages will be found folder = dmarc ; the folders to store processed reports (a=aggregate, f=forensic) f_done = dmarc.forensic a_done = dmarc.aggregate [http] port = 8080 [https] port = 8443 ssl_crt = ssl_key = Mail-DMARC-1.20240314/t/backends/mail-dmarc.sql.SQLite.ini000444000765000024 255314574361234 21706 0ustar00mattstaff000000000000; This is YOU. DMARC reports include information about the reports. Enter it here. [organization] domain = example-test.com org_name = My Great Company email = noreply@example.com extra_contact_info = http://www.example.com/dmarc-policy/ ; aggregate DMARC reports need to be stored somewhere. Any database ; with a DBI module (MySQL, SQLite, DBD, etc.) should work. ; SQLite and MySQL and Postgresql are supported. ; Default is sqlite. [report_store] backend = SQL dsn = dbi:SQLite:dbname=t/reports-test.sqlite ; dsn = dbi:SQLite:dbname=:memory: user = pass = ; backend can be perl or libopendmarc [dmarc] backend = perl [dns] timeout = 5 public_suffix_list = share/public_suffix_list [smtp] ; hostname is the external FQDN of this MTA hostname = mail.example.com cc = set.this@for.a.while.example.com whitelist = t/whitelist ; By default, we attempt to email directly to the report recipient. ; Set these to relay via a SMTP smart host. smarthost = smartuser = smartpass = [imap] server = mail.example.com user = pass = ; the imap folder where new dmarc messages will be found folder = dmarc ; the folders to store processed reports (a=aggregate, f=forensic) f_done = dmarc.forensic a_done = dmarc.aggregate [http] port = 8080 [https] port = 8443 ssl_crt = ssl_key = Mail-DMARC-1.20240314/t/backends/mail-dmarc.sql.mysql.ini000444000765000024 251214574361234 21705 0ustar00mattstaff000000000000; This is YOU. DMARC reports include information about the reports. Enter it here. [organization] domain = example-test.com org_name = My Great Company email = noreply@example.com extra_contact_info = http://www.example.com/dmarc-policy/ ; aggregate DMARC reports need to be stored somewhere. Any database ; with a DBI module (MySQL, SQLite, DBD, etc.) should work. ; SQLite and MySQL and Postgresql are supported. ; Default is sqlite. [report_store] backend = SQL dsn = dbi:mysql:database=dmarc_report;port=3306 user = root pass = ; backend can be perl or libopendmarc [dmarc] backend = perl [dns] timeout = 5 public_suffix_list = share/public_suffix_list [smtp] ; hostname is the external FQDN of this MTA hostname = mail.example.com cc = set.this@for.a.while.example.com whitelist = t/whitelist ; By default, we attempt to email directly to the report recipient. ; Set these to relay via a SMTP smart host. smarthost = smartuser = smartpass = [imap] server = mail.example.com user = pass = ; the imap folder where new dmarc messages will be found folder = dmarc ; the folders to store processed reports (a=aggregate, f=forensic) f_done = dmarc.forensic a_done = dmarc.aggregate [http] port = 8080 [https] port = 8443 ssl_crt = ssl_key = Mail-DMARC-1.20240314/xt000755000765000024 014574361234 13515 5ustar00mattstaff000000000000Mail-DMARC-1.20240314/xt/author-critic.t000444000765000024 67414574361234 16603 0ustar00mattstaff000000000000#!perl BEGIN { unless ($ENV{AUTHOR_TESTING}) { require Test::More; Test::More::plan(skip_all => 'these tests are for testing by the author'); } } use strict; use warnings; use Test::More; use English qw(-no_match_vars); eval "use Test::Perl::Critic"; plan skip_all => 'Test::Perl::Critic required to criticise code' if $@; Test::Perl::Critic->import( -profile => "xt/perlcritic.rc" ) if -e "xt/perlcritic.rc"; all_critic_ok(); Mail-DMARC-1.20240314/xt/perlcritic.rc000444000765000024 77714574361234 16333 0ustar00mattstaff000000000000severity = stern ; gentle stern harsh cruel brutal verbose = [%p] %m at line %l, column %c. %e. (Severity: %s)\n [-Documentation::RequirePodSections] [-TestingAndDebugging::RequireUseStrict] [-TestingAndDebugging::RequireUseWarnings] ; in general, I agree, but I disagree in mail::critic::result::evaluated [-Modules::ProhibitMultiplePackages] [Subroutines::RequireArgUnpacking] short_subroutine_statements = 5 [RegularExpressions::RequireExtendedFormatting] minimum_regex_length_to_complain_about = 8

Mail::DMARC::HTTP.