20 |
23 | {{ i18n.templates.evalNotif.title }}
21 | 22 |
24 |
25 |
26 | ├── .eslintignore
├── .eslintrc.js
├── .github
├── ISSUE_TEMPLATE
│ ├── help.md
│ ├── report-a-bug.md
│ └── request-a-feature.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── do-spaces-workflow.yml
│ ├── gh-pages-workflow.yml
│ └── test-workflow.yml
├── .gitignore
├── .nvmrc
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── package-lock.json
├── package.json
├── posthtml.config.js
├── src
├── dns-lookup
│ ├── data
│ │ ├── dmarc.ts
│ │ ├── ns_regexp.ts
│ │ ├── record_key_help.ts
│ │ ├── record_tutorials
│ │ │ ├── cloudflare.ts
│ │ │ ├── digitalocean.ts
│ │ │ ├── godaddy.ts
│ │ │ ├── googledomains.ts
│ │ │ ├── index.ts
│ │ │ ├── namecheap.ts
│ │ │ ├── namecheap_registrar.ts
│ │ │ ├── network_solutions.ts
│ │ │ └── porkbun.ts
│ │ ├── records.ts
│ │ ├── registrar_regexp.ts
│ │ ├── txt.ts
│ │ └── vueified_records.ts
│ ├── i18n
│ │ ├── en
│ │ │ ├── common.ts
│ │ │ ├── data
│ │ │ │ ├── dmarc.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── record_key_help.ts
│ │ │ │ ├── records.ts
│ │ │ │ └── txt.ts
│ │ │ ├── index.ts
│ │ │ └── templates
│ │ │ │ ├── app.ts
│ │ │ │ ├── clipboard_modal.ts
│ │ │ │ ├── dmarc_explainer.ts
│ │ │ │ ├── dns_diff.ts
│ │ │ │ ├── dodns.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── propagation_modal.ts
│ │ │ │ ├── record_selection_modal.ts
│ │ │ │ ├── records.ts
│ │ │ │ ├── truncated_record.ts
│ │ │ │ └── whois.ts
│ │ └── index.ts
│ ├── index.html
│ ├── mount.js
│ ├── plain_text_reports.ts
│ ├── scss
│ │ └── style.scss
│ ├── standardise_records.ts
│ ├── table.ts
│ ├── templates
│ │ ├── app.vue
│ │ ├── clipboard_modal.vue
│ │ ├── dmarc_explainer_modal.vue
│ │ ├── dns_diff.vue
│ │ ├── dodns.vue
│ │ ├── propagation_modal.vue
│ │ ├── record.vue
│ │ ├── record_base.vue
│ │ ├── record_jumps.vue
│ │ ├── record_selection_modal.vue
│ │ ├── skeletons
│ │ │ ├── dodns.vue
│ │ │ ├── record.vue
│ │ │ └── record_jumps.vue
│ │ ├── truncated_record.vue
│ │ └── whois.vue
│ └── utils
│ │ ├── geoJS.ts
│ │ ├── googleDNS.ts
│ │ └── whoisJS.ts
├── shared
│ ├── assets
│ │ ├── dns-bottom.svg
│ │ ├── dns-top.svg
│ │ ├── spf-bottom.svg
│ │ └── spf-top.svg
│ ├── i18n
│ │ ├── en
│ │ │ ├── common.ts
│ │ │ ├── index.ts
│ │ │ └── templates
│ │ │ │ ├── error_modal.ts
│ │ │ │ ├── footer.ts
│ │ │ │ └── index.ts
│ │ └── index.ts
│ ├── templates
│ │ ├── error_modal.vue
│ │ ├── footer.vue
│ │ ├── header.vue
│ │ └── landing.vue
│ └── utils
│ │ ├── backoffFetch.ts
│ │ ├── cfDNS.ts
│ │ ├── sanitize.ts
│ │ ├── skeletonStyle.ts
│ │ └── validateDomain.ts
├── spf-explainer
│ ├── data
│ │ ├── explanations.ts
│ │ └── long_descriptions.ts
│ ├── i18n
│ │ ├── en
│ │ │ ├── data
│ │ │ │ ├── explanations.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── long_descriptions.ts
│ │ │ ├── index.ts
│ │ │ └── templates
│ │ │ │ ├── app.ts
│ │ │ │ ├── eval_notif.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── no_spf_records.ts
│ │ │ │ ├── part_explanation.ts
│ │ │ │ └── spf_base.ts
│ │ └── index.ts
│ ├── index.html
│ ├── mount.js
│ ├── scss
│ │ └── style.scss
│ ├── templates
│ │ ├── all_part_explanations.vue
│ │ ├── app.vue
│ │ ├── eval_notif.vue
│ │ ├── no_spf_records.vue
│ │ ├── part_explanation.vue
│ │ ├── skeletons
│ │ │ ├── record.vue
│ │ │ └── small_spf_skeleton.vue
│ │ ├── spf.vue
│ │ └── spf_base.vue
│ └── utils
│ │ ├── line_generator.ts
│ │ ├── line_spawn.ts
│ │ ├── spf_records.ts
│ │ └── spf_sandbox.ts
└── static
│ ├── README.md
│ ├── dns-lookup.png
│ ├── dns-lookup.svg
│ ├── robots.txt
│ ├── spf-explainer.png
│ └── spf-explainer.svg
├── tsconfig.json
└── user-flow-dns-lookup.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.js
2 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | "plugin:@typescript-eslint/recommended",
4 | "plugin:vue/recommended",
5 | ],
6 | parserOptions: {
7 | ecmaVersion: 2018,
8 | parser: "@typescript-eslint/parser",
9 | sourceType: "module",
10 | },
11 | rules: {
12 | "linebreak-style": ["error", "unix"],
13 | semi: ["error", "never"],
14 | "@typescript-eslint/no-non-null-assertion": 0,
15 | "@typescript-eslint/no-explicit-any": 0,
16 | "@typescript-eslint/explicit-function-return-type": 0,
17 | "@typescript-eslint/explicit-module-boundary-types": 0,
18 | "require-atomic-updates": 0,
19 | "no-undef": 0,
20 | "vue/require-v-for-key": 0,
21 | "vue/require-default-prop": 0,
22 | "vue/no-v-html": 0,
23 | "vue/max-attributes-per-line": 0,
24 | "vue/html-self-closing": 0,
25 | "vue/html-indent": ["error", 4],
26 | "vue/script-indent": ["error", 4, {
27 | baseIndent: 1,
28 | }],
29 | "@typescript-eslint/indent": 0,
30 | "vue/no-unused-vars": 0,
31 | "vue/multi-word-component-names": 0,
32 | "vue/no-reserved-component-names": 0,
33 | "@typescript-eslint/no-object-literal-type-assertion": 0,
34 | },
35 | }
36 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/help.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Help ❓
3 | about: Encountered a problem with the tool?
4 | ---
5 |
6 |
15 |
16 | ## Tool relating to this issue
17 |
18 |
19 | ## Information
20 |
22 |
23 | ## Help request
24 |
25 | ### Problem
26 |
27 |
28 | ### What I have tried
29 |
30 |
31 | ### Screenshots
32 |
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/report-a-bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Report a bug 🐛
3 | about: Report a bug with the tool. Only use this if you're 100% sure there's something wrong, otherwise, try "Help".
4 | ---
5 |
6 |
19 |
20 | ## Tool relating to this issue
21 |
22 |
23 | ## Information
24 |
26 |
27 | ## Details
28 |
29 | ### Description
30 |
31 |
32 | ### Steps to reproduce
33 |
34 |
35 | ### Expected behavior
36 |
37 |
38 | ### Screenshots
39 |
40 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/request-a-feature.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Request a feature 🆕
3 | about: Suggest a new feature that you would like in the tool!
4 | ---
5 |
6 |
31 |
32 | ## Tool relating to this issue
33 |
34 |
35 | ## Feature request
36 |
37 | ### Feature description
38 |
39 |
40 | ### How the feature is useful
41 |
42 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Type of Change
2 |
3 |
4 | - **Build Scripts:**
5 | - **Shared Source:**
6 | - **Tool Source:**
7 | - **Something else:**
8 |
9 | ## What issue does this relate to?
10 |
11 |
12 | ### What should this PR do?
13 |
14 |
15 | ### What are the acceptance criteria?
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.github/workflows/do-spaces-workflow.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to DigitalOcean Spaces
2 |
3 | on: push
4 |
5 | permissions:
6 | contents: write
7 |
8 | jobs:
9 | deploy-spaces:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v3
15 |
16 | - name: Use Node.js
17 | uses: actions/setup-node@v3
18 | with:
19 | node-version-file: .nvmrc
20 | cache: npm
21 |
22 | - name: Install dependencies, test, and build
23 | run: |
24 | npm ci
25 | npm test
26 | npm run build
27 |
28 | - name: Deploy DNS lookup commit to DigitalOcean Spaces
29 | run: aws s3 sync ./dist/dns-lookup s3://${{ secrets.SPACES_BUCKET }}/commits/dns-lookup/${{ github.sha }} --endpoint=https://${{ secrets.SPACES_REGION }}.digitaloceanspaces.com --acl public-read --content-encoding utf8
30 | env:
31 | AWS_ACCESS_KEY_ID: ${{ secrets.SPACES_ACCESS_KEY_ID }}
32 | AWS_SECRET_ACCESS_KEY: ${{ secrets.SPACES_SECRET_ACCESS_KEY }}
33 | AWS_DEFAULT_REGION: ${{ secrets.SPACES_REGION }}
34 |
35 | - name: Deploy SPF explainer commit to DigitalOcean Spaces
36 | run: aws s3 sync ./dist/spf-explainer s3://${{ secrets.SPACES_BUCKET }}/commits/spf-explainer/${{ github.sha }} --endpoint=https://${{ secrets.SPACES_REGION }}.digitaloceanspaces.com --acl public-read --content-encoding utf8
37 | env:
38 | AWS_ACCESS_KEY_ID: ${{ secrets.SPACES_ACCESS_KEY_ID }}
39 | AWS_SECRET_ACCESS_KEY: ${{ secrets.SPACES_SECRET_ACCESS_KEY }}
40 | AWS_DEFAULT_REGION: ${{ secrets.SPACES_REGION }}
41 |
42 | - name: Leave a comment on commit
43 | run: npm run deploy:spaces:comment
44 | env:
45 | REPO_NAME: ${{ github.repository }}
46 | COMMIT_SHA: ${{ github.sha }}
47 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48 | SPACES_REGION: ${{ secrets.SPACES_REGION }}
49 | SPACES_BUCKET: ${{ secrets.SPACES_BUCKET }}
50 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages-workflow.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | permissions:
9 | contents: write
10 |
11 | concurrency:
12 | group: gh-pages-workflow
13 | cancel-in-progress: true
14 |
15 | jobs:
16 | deploy-pages:
17 | runs-on: ubuntu-latest
18 |
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v3
22 |
23 | - name: Use Node.js
24 | uses: actions/setup-node@v3
25 | with:
26 | node-version-file: .nvmrc
27 | cache: npm
28 |
29 | - name: Install dependencies, test, and build
30 | run: |
31 | npm ci
32 | npm test
33 | npm run build
34 |
35 | - name: Deploy master to GitHub Pages
36 | uses: JamesIves/github-pages-deploy-action@v4
37 | with:
38 | folder: dist
39 | clean: true
40 | single-commit: true
41 |
--------------------------------------------------------------------------------
/.github/workflows/test-workflow.yml:
--------------------------------------------------------------------------------
1 | name: Test and Build
2 |
3 | on: pull_request
4 |
5 | permissions:
6 | contents: read
7 |
8 | jobs:
9 | test-build:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v3
15 |
16 | - name: Use Node.js
17 | uses: actions/setup-node@v3
18 | with:
19 | node-version-file: .nvmrc
20 | cache: npm
21 |
22 | - name: Install dependencies
23 | run: npm ci
24 |
25 | - name: Run tests
26 | run: npm test
27 |
28 | - name: Build tool
29 | run: npm run build
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .cache/
3 | dist/
4 | dev/
5 | .idea/
6 | .vscode/
7 | build/base.html
8 | build/svg/
9 | .DS_Store
10 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v20.9.0
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Pull Requests
4 |
5 | ### Creating a Pull Request
6 |
7 | This application has been designed so that people can easily expand it.
8 | To request us to review code that you create, you will need to create a pull request.
9 | Creating a pull request is described in
10 | [this tutorial](https://www.digitalocean.com/community/tutorials/how-to-create-a-pull-request-on-github).
11 |
12 | ### Linting
13 |
14 | Before creating a pull request to this application, you will want to lint it first.
15 | This is because linting is a check that is ran when a pull request is made and cannot be merged in if it fails.
16 |
17 | To lint, simply run `npm test`. This will lint all the TS, Vue & SCSS files within the app.
18 |
19 | If there are any errors that can be automatically be fixed with the TS & Vue files, you can execute
20 | `npm run test:ts-vue:fix` to automatically do that.
21 |
22 | This project enforces LF line styles, 4 spaces and no semi-colons.
23 | The linting will fail if this is not followed.
24 |
25 | ### File Location/Types
26 |
27 | Please see [README: Source Structure](README.md#source-structure) for information on how files should be organised.
28 |
29 | ## Issue Creation
30 |
31 | In the event that you have a issue using the tool or have a suggest for a change but don't want to contribute code,
32 | we are more than happy to help.
33 | Make sure that when you create your issue, it follows the format for the type of issue you select
34 | (it has individual templates for each issue type).
35 |
36 | Issue template types include the following:
37 | - Bug Reporting
38 | - Feature Requests
39 | - Help Requests
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2019 DigitalOcean
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Community DNS Tools
2 |
3 | A set of browser-based DNS tools for DigitalOcean Community.
4 |
5 | ---
6 |
7 |
8 | A DMARC record is a normal TXT DNS records that is created at An example of a DMARC TXT record would be:
9 |
10 |
11 | ## DNS Lookup
12 |
13 | A simple browser-based tool to perform DNS lookups. Type a domain, search, and instantly get results.
14 |
15 | ### [➡️ Use now](https://www.digitalocean.com/community/tools/dns)
16 |
17 | ---
18 |
19 |
20 |
21 |
22 |
23 | ## SPF Explainer
24 |
25 | A tool that explains a domain's SPF records. Search a domain and either explore its records or evaluate an IP for mail sending.
26 |
27 | ### [➡️ Use now](https://www.digitalocean.com/community/tools/spf)
28 |
29 | ## Development/Building
30 |
31 | To setup the build/develop environment, you will need to run `npm i` with Node 12+ installed. This will install the
32 | dependencies to allow you to build the project.
33 |
34 | To develop for the DNS tool run `npm run dev:tools:dns-lookup`, and to develop for the SPF explainer run
35 | `npm run dev:tools:spf-explainer`.\
36 | This will start a development server that will automatically reload the codebase when changes occur.
37 |
38 | If you wish to host these tools on a service, simply run `npm run build`. This will run all the necessary build scripts
39 | automatically to build all the tools present in the source folder.\
40 | You can then take the `dist` folder and put it on your web server/bucket. The `dist` folder will contain the folders
41 | `dns-lookup` and `spf-explainer` which will each have their respective tools inside.
42 |
43 | GitHub Actions is setup to do this automatically for this repository to deploy to gh-pages.
44 | It is also configured to deploy each PR commit to DigitalOcean Spaces for PR previews.
45 |
46 | ## Source Structure
47 |
48 | ### [`src`](./src)
49 |
50 | All the source for the tools is located within the [`src`](./src) directory.
51 |
52 | In this directory, there is the [`src/shared`](./src/shared) directory which contains centralised assets and source for
53 | the tools, such as the main Community styling which is located in [`src/shared/scss`](./src/shared/scss) and the
54 | generic templates used by all tools in [`src/shared/templates`](./src/shared/templates).
55 |
56 | Within this directory are also the main tool source directories ([`src/dns-lookup`](./src/dns-lookup) &
57 | [`src/spf-explainer`](./src/spf-explainer)).\
58 | These directories contain the specific source for that tool, which includes custom templates and style inheritance from
59 | the centralised styles.
60 |
61 | Anything that is data which is used in a tool should be stored in `src/
DMARC1
.`,
3 | ruf: "This is a comma separated list of e-mails where forensic reports should be sent.",
4 | rua: "This is a comma separated list of e-mails where aggregate reports should be sent.",
5 | pct: "This defines the percentage of e-mail which will have this policy applied.",
6 | p: `This defines how domain administrators want e-mail treated on the primary domain that fail the DMARC validation checks. The options are none
(treat the same as usual), quarantine
(mark as spam) or reject
(reject the message).`,
7 | aspf: `If this is set to strict s
mode the domain which e-mails are being sent from must exactly pass SPF record validation. If not, it will default to relaxed r
mode where any sub-domain can inherit the root domain's SPF records.`,
8 | adkim: `By default this is set to relaxed r
mode where any sub-domain can inherit the root domain's DKIM records. If this is set to strict s
mode, the domain which e-mails are being sent from must exactly pass DKIM record validation.`,
9 | sp: `This defines how domain administrators want e-mail treated on sub-domains that fail the DMARC validation checks. The options are none
(treat the same as usual), quarantine
(mark as spam) or reject
(reject the message).`,
10 | fo: `This option allows you to configure when failure reports should be sent. The available options are 0
which is the default and specifies that a report should be sent if an email fails SPF and DKIM checks, 1
to send a report if SPF or DKIM checks fail, d
to send a report for only DKIM failures and s
to send a report for only SPF failures.`,
11 | rf: `This is the format reports are sent in. It currently only has one supported value, afrf
, which is the default.`,
12 | ri: `The requested interval between DMARC reports being sent in seconds. This defaults to 86400
.`,
13 | } as {[key: string]: string}
14 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/data/index.ts:
--------------------------------------------------------------------------------
1 | import txt from "./txt"
2 | import dmarc from "./dmarc"
3 | import records from "./records"
4 | import recordKeyHelp from "./record_key_help"
5 |
6 | export default {
7 | txt, records, recordKeyHelp, dmarc,
8 | } as {[key: string]: {[key: string]: string}}
9 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/data/record_key_help.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | TTL: "Time-to-live is a value in seconds that indicates how long a record should be cached for before being checked again",
3 | } as {[key: string]: string}
4 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/data/records.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | A: "A records are the most basic type of DNS record and are used to point a domain or subdomain to an IPv4 address.\nThese do NOT act as MX records, to receive e-mail, you need to set a MX record.",
3 | TXT: "TXT records are a type of DNS record that contains text information for sources outside of your domain.",
4 | MX: "A mail exchanger record (MX record) specifies the mail server responsible for accepting email messages on behalf of a domain name.",
5 | AAAA: "AAAA records behave the same as A records but for IPv6.\nThey are used to point a domain or subdomain to a IPv6 address.",
6 | CNAME: "CNAME records are a DNS record that allows one domain to be mapped as an alias to another canonical domain name.",
7 | CAA: "CAA records allow domain owners to specify which Certificate Authorities (CAs) are permitted to issue certificates.",
8 | NS: "NS stands for \"name server\" and this record indicates which DNS server is authoritative for that domain (which server contains the actual DNS records).\nA domain will often have multiple NS records which can indicate primary and backup name servers for that domain.",
9 | SRV: "A Service record (SRV record) is a specification of data in the Domain Name System defining the location, i.e. the hostname and port number, of servers for specified services.",
10 | DMARC: "The goal of DMARC is to build on the system of senders and receivers collaborating to improve mail authentication practices of senders and enable receivers to reject unauthenticated messages.",
11 | SSHFP: "This is used to identify which SSH keys belong to the domain.",
12 | TLSA: "TLSA records are used to specify the keys used in a domain's TLS servers.",
13 | } as {[key: string]: string}
14 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/data/txt.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | globalsign: "This TXT record is used so that GlobalSign can verify that they are issuing certificates to the domain owner.",
3 | o365: "This TXT record is used for Office 365 domain verification.",
4 | keybase: "This TXT record is commonly used to verify that a Keybase user is in ownership of a domain.",
5 | spf: "This is a SPF record which is used to identify trusted sources for transmitted e-mail.\nThis makes it easier for receivers to tell if a e-mail is spam.",
6 | loaderio: "This is used so that Loader.io can verify the domain owner.",
7 | yandex: "This is used so that Yandex can verify the domain owner.",
8 | facebook: "This is used so that Facebook can verify the domain owner.",
9 | logmein: "This is used so that LogMeIn can verify the domain owner.",
10 | segment: "This is used so that Segment.com can verify the domain owner.",
11 | statuspage: "This is used so that Statuspage.io can verify the domain owner.",
12 | bugcrowd: "This TXT record is used so that Bugcrowd can verify the domain owner.",
13 | mailru: "This TXT record is used so that mail.ru can verify the domain owner.",
14 | google: "This TXT record is used so that Google can verify the domain owner.",
15 | } as {[key: string]: string}
16 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/index.ts:
--------------------------------------------------------------------------------
1 | import templates from "./templates"
2 | import data from "./data"
3 | import common from "./common"
4 |
5 | export default { templates, data, common } as any
6 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/templates/app.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | title: "DNS Lookup",
3 | description: `A simple browser-based tool to perform DNS lookups.
Type a domain, search, and instantly get results.`,
4 | searchButton: "Search DNS Records",
5 | textRecords: "Download/Copy Records",
6 | domain: "Domain name",
7 | } as {[key: string]: string}
8 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/templates/clipboard_modal.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | clipboardResult: "Clipboard Result",
3 | thisHasBeenCopied: "Copied to your clipboard!",
4 | } as {[key: string]: string}
5 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/templates/dmarc_explainer.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | title: "DMARC Explanations",
3 | intro: `_dmarc.hostname.
instead of just the root hostname.
4 | This record can contain any of the following "tags" formatted as tag=value
and separated by semi-colons._dmarc.hostname. 300 IN TXT "v=DMARC1;p=reject;pct=100;rua=mailto:postmaster@hostname"
ipconfig /flushdns
in the box and press OK. You should see a brief flash of a black box. Your cache should then be invalidated.
12 | sudo systemd-resolve --flush-caches
.
14 | sudo killall -HUP mDNSResponder
.
16 | _<{sub}>._<protocol>.name.
`,
9 | dmarcMechanisms: "What are the mechanisms supported in DMARC records?",
10 | } as {[key: string]: string}
11 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/templates/truncated_record.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | showMore: "Show more...",
3 | showLess: "Show less...",
4 | } as {[key: string]: string}
5 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/en/templates/whois.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | loading: "Loading WHOIS data...",
3 | abuseContact: "Abuse Contact",
4 | owner: "Owner",
5 | notSpecified: "Not Specified",
6 | } as {[key: string]: string}
7 |
--------------------------------------------------------------------------------
/src/dns-lookup/i18n/index.ts:
--------------------------------------------------------------------------------
1 | import en from "./en"
2 |
3 | const lang = "en"
4 | const packs = { en } as any
5 |
6 | export default packs[lang]
7 |
--------------------------------------------------------------------------------
/src/dns-lookup/index.html:
--------------------------------------------------------------------------------
1 |
16 |
17 | 20 | 21 | {{ i18n.templates.clipboardModal.thisHasBeenCopied }} 22 | 23 |
24 |{{ textReport }}
25 | 22 | {{ key }}: 23 |
24 |
26 | {{ i18n.templates.dmarcExplainer.learnMore }}
27 |
{{ i18n.templates.dnsDiff.host }} | 26 |{{ i18n.templates.dnsDiff.cfDns }} | 27 |{{ i18n.templates.dnsDiff.gDns }} | 28 |
---|---|---|
33 | {{ value ? value : i18n.common.none }} 34 | |
35 |
26 | | 27 | | 28 | |
20 | 21 |
22 |{{ truncated }} {{ i18n.templates.truncatedRecord.showMore }}
20 |{{ value }} {{ i18n.templates.truncatedRecord.showLess }}
23 |20 | 21 | {{ i18n.templates.whois.owner }}: {{ netname }} 22 | 28 | 29 |
30 |32 | 33 | ASN: {{ asn }} 34 | 35 |
36 |37 | 38 | CIDR: {{ cidr }} 39 | 40 |
41 |42 | 43 | {{ i18n.templates.whois.abuseContact }}: {{ abuse }} 44 | 45 |
46 |50 | 51 | {{ i18n.templates.whois.loading }} 52 | 53 |
54 |${sanitize(json.Comment.toString())}`
46 | return [null, msg]
47 | }
48 |
49 | // It's legit
50 | return [text, null]
51 | }
52 |
--------------------------------------------------------------------------------
/src/spf-explainer/data/explanations.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 DigitalOcean
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | import i18n from "../i18n"
18 |
19 | const explanationMap = new Map
20 |
42 | {{ i18n.templates.app.whatDoTheyDo }}
43 |
34 |
46 | This includes the IP addresses
47 |
48 |
21 | {{ i18n.templates.spfBase.results.replace("{hostname}", hostname) }}
22 | {1}
:`,
25 | ip4: `This means that IPv4 addresses in the range??s?? of {R}
are allowed to send e-mails from any domains using this record.`,
26 | ip6: `This means that IPv6 addresses in the range??s?? of {R}
are allowed to send e-mails from any domains using this record.`,
27 | redirect: `This is used when 2 websites have the exact same infrastructure and the administrator wants all requests directed to one domain. In this case, that would be {1}
.`,
28 | exists: `This is used to check if a IP address is allowed to send e-mails by checking if there is a NULL record at <IP address>.{1}
.`,
29 | ptrRoot: ptrStart + "this domain to see if the IP address is allowed to send e-mails.",
30 | ptrNotRoot: ptrStart + `{1}
to see if the IP address is allowed to send e-mails.`,
31 | aRoot: aStart + "this domain is allowed to send e-mails.",
32 | aNotRoot: aStart + `{1}
is allowed to send e-mails.`,
33 | mxRoot: mxStart + "this domain is allowed to send e-mails.",
34 | mxNotRoot: mxStart + `{1}
is allowed to send e-mails.`,
35 | hardFail: "This means only the IP's here or in inherited records are allowed to send e-mails.",
36 | softFail: "If the IP address sending the e-mail is not in this record, the e-mail should be accepted but marked with a warning. This means that the e-mail will likely be marked as spam.",
37 | noFail: "This tag allows any server to send e-mails from your domain.",
38 | neutral: "This will neither pass or fail emails, just act like the record is not there. Therefore, this doesn't affect the ability to send.",
39 | } as {[key: string]: string}
40 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/en/data/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 DigitalOcean
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | import explanations from "./explanations"
18 | import longDescriptions from "./long_descriptions"
19 |
20 | export default { explanations, longDescriptions } as {[key: string]: {[key: string]: string}}
21 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/en/data/long_descriptions.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 DigitalOcean
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | export default {
18 | ip4: "This means that any IPv4 addresses specified in this mechanism will be accepted and not marked as spam.",
19 | ip6: "This means that any IPv6 addresses specified in this mechanism will be accepted and not marked as spam.",
20 | a: "This means that any A records specified in this mechanism (or the A records for the current record if none are specified) will be accepted and not marked as spam.",
21 | v: `This just defines the record version. This is commonly just spf1
.`,
22 | mx: "This means that any MX records specified in this mechanism (or the MX records for the current record if none are specified) will be accepted and not marked as spam.",
23 | include: "This will include the records at the hostname given. This can be recursive so it can go down multiple levels. Anytime that a mechanism is used that would be bound to the domain in the included record, it will be bound to the domain of the included record.",
24 | ptr: "The PTR record is deprecated since it is slow and relies on the name servers inbetween the domains to work properly.",
25 | } as {[key: string]: string}
26 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/en/index.ts:
--------------------------------------------------------------------------------
1 | import templates from "./templates"
2 | import data from "./data"
3 |
4 | export default { templates, data } as any
5 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/en/templates/app.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | title: "SPF Explainer",
3 | description: `A tool that explains a domain’s SPF records. Search a domain and either explore its records or evaluate an IP for mail sending.`,
4 | eval: "Evaluate",
5 | whatDoTheyDo: "What are all the SPF mechanisms and what do they all do?",
6 | fetchError: "An error occurred whilst attempting to fetch the SPF records.",
7 | } as {[key: string]: string}
8 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/en/templates/eval_notif.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | title: "Evaluation Result",
3 | goThrough: "E-mails will be able to send from {{IP}} successfully, as this domain.",
4 | ignored: "E-mails sent from {{IP}}, as this domain, should be ignored.",
5 | softFail: "E-mails sent from {{IP}}, as this domain, will be accepted but will likely be marked as spam since they will be flagged.",
6 | neutral: "The SPF record will neither pass or fail emails, just act as if the record is not there. Therefore, this won't affect the ability to send.",
7 | } as {[key: string]: string}
8 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/en/templates/index.ts:
--------------------------------------------------------------------------------
1 | import app from "./app"
2 | import spfBase from "./spf_base"
3 | import partExplanation from "./part_explanation"
4 | import noSpfRecords from "./no_spf_records"
5 | import evalNotif from "./eval_notif"
6 |
7 | export default { app, spfBase, partExplanation, noSpfRecords, evalNotif } as {[key: string]: {[key: string]: string}}
8 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/en/templates/no_spf_records.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | title: "No SPF Records Found",
3 | description: "No SPF records were found on the domain specified. This means that e-mails will only be allowed from the specified MX records on this domain.",
4 | } as {[key: string]: string}
5 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/en/templates/part_explanation.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | mechanism: "SPF Mechanism ($)",
3 | allMechanisms: "All SPF Mechanisms",
4 | } as {[key: string]: string}
5 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/en/templates/spf_base.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | results: "SPF records for {hostname}:",
3 | } as {[key: string]: string}
4 |
--------------------------------------------------------------------------------
/src/spf-explainer/i18n/index.ts:
--------------------------------------------------------------------------------
1 | import en from "./en"
2 |
3 | const lang = "en"
4 | const packs = { en } as any
5 |
6 | export default packs[lang]
7 |
--------------------------------------------------------------------------------
/src/spf-explainer/index.html:
--------------------------------------------------------------------------------
1 |
16 |
17 | {{ key }}
:
21 |
19 |
86 |
87 |
88 |
212 |
--------------------------------------------------------------------------------
/src/spf-explainer/templates/eval_notif.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
27 |
28 |
29 |
63 |
--------------------------------------------------------------------------------
/src/spf-explainer/templates/no_spf_records.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
33 |
67 |
68 |
69 |
77 |
78 |
79 |
80 |
81 |
82 |
19 |
20 |
49 |
50 |
51 |
65 |
--------------------------------------------------------------------------------
/src/spf-explainer/templates/skeletons/small_spf_skeleton.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
21 |
22 |
23 |
24 |
33 |
25 |
26 |
32 |
27 |
30 |
31 |
28 |
29 |
34 |
48 |
35 |
36 |
37 |
38 |
47 |
39 |
40 |
46 |
41 |
44 |
45 |
42 |
43 |
19 |
30 |
31 |
32 |
75 |
--------------------------------------------------------------------------------
/src/spf-explainer/templates/spf.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
20 |
29 |
21 |
22 |
28 |
23 |
26 |
27 |
24 |
25 |
19 |
64 |
65 |
66 |
276 |
--------------------------------------------------------------------------------
/src/spf-explainer/templates/spf_base.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
20 |
21 |
22 | {{ key }}
23 |
24 | {{ " " }}
25 |
26 |
27 |
28 |
30 |
31 |
62 |
32 |
33 |
61 | {{ part[0] }}
35 | {{ " " }}
36 |
40 |
41 |
42 | :
43 | {{ p }}
49 | ,
50 | .
51 |
55 |
59 |
60 |
56 |
58 |
19 |
34 |
35 |
36 |
73 |
--------------------------------------------------------------------------------
/src/spf-explainer/utils/line_generator.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 DigitalOcean
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Defines the main gap size.
18 | const mainGapSize = 8
19 |
20 | // The line multiplier.
21 | const lineMultiplier = 0.05
22 |
23 | // Defines the line color.
24 | const lineColor = "#000000"
25 |
26 | // Check if 2 numbers are in the range of x (a/b are compared).
27 | const within = (x: number, a: number, b: number) => {
28 | let larger, smaller
29 | if (a > b) {
30 | larger = a
31 | smaller = b
32 | } else {
33 | larger = b
34 | smaller = a
35 | }
36 | return x >= (larger - smaller)
37 | }
38 |
39 | // Adds the offset.
40 | const addOffset = (y: number) => {
41 | return y + window.pageYOffset
42 | }
43 |
44 | // Renders a claw for the final part. It should look a bit like this:
45 | // |-- (line 20px)
46 | // |
47 | // | (line is height of object)
48 | // |
49 | // |-- (line 20px)
50 | const drawClaw = (x: number, top: number, bottom: number, generator: LineGenerator, svg: SVGSVGElement) => {
51 | // Draw the core middle line.
52 | generator.drawLine(svg, lineColor, x, x, top, bottom)
53 |
54 | // Draw the claws.
55 | generator.drawLine(svg, lineColor, x, x + 20, top, top)
56 | generator.drawLine(svg, lineColor, x, x + 20, bottom, bottom)
57 | }
58 |
59 | // A lock. Only one line generator can run at once.
60 | // While initialising a line generator does block, it's also doing a bunch of things where there is a opportunity for another function to be ran inbetween, causing a race condition.
61 | let lock = false
62 |
63 | // The main line generator class.
64 | export default class LineGenerator {
65 | public a!: HTMLElement
66 | public b!: HTMLElement
67 | public lineDiv!: HTMLElement
68 | public destroyed: boolean
69 | public downX: number | undefined
70 | public visible: boolean
71 |
72 | public constructor(a: HTMLElement, b: HTMLElement) {
73 | this.a = a
74 | this.b = b
75 | this.downX = Math.floor(window.innerWidth * lineMultiplier)
76 | this.destroyed = false
77 | this.visible = false
78 | const f = () => {
79 | if (!lock) this.setup()
80 | else setTimeout(f, 5)
81 | }
82 | f()
83 | }
84 |
85 | private _createContainer() {
86 | const div = document.createElement("div")
87 | div.style.position = "absolute"
88 | div.style.top = "0"
89 | div.style.left = "0"
90 | div.style.overflow = "show"
91 | div.style.pointerEvents = "none"
92 | document.body.appendChild(div)
93 | return div
94 | }
95 |
96 | private _createInnerSvg(container: HTMLDivElement) {
97 | const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
98 | svg.setAttribute("overflow", "visible")
99 | container.appendChild(svg)
100 | return svg
101 | }
102 |
103 | public drawLine(svg: SVGSVGElement, stroke: string, x1: number, x2: number, y1: number, y2: number) {
104 | const line = document.createElementNS("http://www.w3.org/2000/svg", "line")
105 | line.setAttribute("stroke", stroke)
106 | line.setAttribute("stroke-width", "1")
107 | line.setAttribute("x1", x1.toString())
108 | line.setAttribute("x2", x2.toString())
109 | line.setAttribute("y1", addOffset(y1).toString())
110 | line.setAttribute("y2", addOffset(y2).toString())
111 | svg.appendChild(line)
112 | }
113 |
114 | public setup() {
115 | // Locks the initialiser.
116 | lock = true
117 |
118 | // Gets the positions of A/B.
119 | const aRect = this.a.getBoundingClientRect() as DOMRect
120 | const bRect = this.b.getBoundingClientRect()
121 | const aTop = aRect.top
122 | const bTop = bRect.top + Math.floor(bRect.height / 2)
123 |
124 | // Makes the div container.
125 | const container = this._createContainer()
126 | this.lineDiv = container
127 |
128 | // Makes the inner SVG.
129 | const svg = this._createInnerSvg(container)
130 |
131 | // Gets the left side of B.
132 | const bLeft = bRect.left - mainGapSize
133 |
134 | try {
135 | // If bLeft is less than 0, return.
136 | if (0 > bLeft) return
137 |
138 | // Gets the best pathway to point A.
139 | const aParentRect = this.a.parentElement!.getBoundingClientRect() as DOMRect
140 | let aBestY = aTop - 4
141 | let aTouch = aTop
142 | let top = true
143 | if (!within(3, aParentRect.y, aRect.y) || aRect.width > 1000) {
144 | aTouch = aRect.bottom
145 | aBestY = aRect.bottom + 4
146 | top = false
147 | }
148 |
149 | // The best X for A.
150 | const aBestX = aRect.x + (aRect.width > 1000 ? 20 : Math.floor(aRect.width / 2))
151 |
152 | // Draw a line from "aBestY" to "aTouch".
153 | this.drawLine(svg, lineColor, aBestX, aBestX, top ? aBestY : aTouch, top ? aTouch : aBestY)
154 |
155 | // Draw a line from "this.downX" to "aBestX" using "aBestY".
156 | this.drawLine(svg, lineColor, this.downX!, aBestX, aBestY, aBestY)
157 |
158 | // Draws the claw.
159 | drawClaw(bLeft, bRect.top - mainGapSize, bRect.bottom + mainGapSize, this, svg)
160 |
161 | // bLeft being greater than bTop means there isn't enough room. Return and destroy.
162 | if (this.downX! > bLeft) return this.destroy()
163 |
164 | // Draw a line from "bTop" to "bLeft".
165 | this.drawLine(svg, lineColor, this.downX!, bLeft, bTop, bTop)
166 |
167 | // Draw the down line.
168 | this.drawLine(svg, lineColor, this.downX!, this.downX!, aBestY, bTop)
169 | } finally {
170 | // Unlocks the generator.
171 | lock = false
172 | }
173 | }
174 |
175 | public destroy() {
176 | if (this.destroyed) return
177 | if (this.lineDiv) this.lineDiv.remove()
178 | this.destroyed = true
179 | this.downX = undefined
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/src/spf-explainer/utils/line_spawn.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 DigitalOcean
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Imports the line generator.
18 | import LineGenerator from "./line_generator"
19 |
20 | // Defines the active line.
21 | let activeLine: LineGenerator | undefined
22 |
23 | // Freezes line spawing.
24 | let freezeSpawning = false
25 | export const setFreezeSpawning = (t: boolean) => { freezeSpawning = t }
26 |
27 | // Destroys the line.
28 | const destroy = () => {
29 | if (activeLine) activeLine.destroy()
30 | activeLine = undefined
31 | }
32 |
33 | // Spawns the line.
34 | export const spawnLine = (lineRef: HTMLElement[] | undefined) => {
35 | if (freezeSpawning) return
36 | destroy()
37 | if (lineRef) activeLine = new LineGenerator(lineRef[0], lineRef[1])
38 | }
39 |
40 | // Despawn the line on resize.
41 | window.addEventListener("resize", destroy)
42 |
--------------------------------------------------------------------------------
/src/spf-explainer/utils/spf_records.ts:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024 DigitalOcean
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | import cfDNS from "../../shared/utils/cfDNS"
18 |
19 | const getSPFRecords = async (domain: string): Promise
20 |
25 |
24 |
26 |
28 |
29 |
30 |
33 |
32 |
9 |
10 |
11 | ## DNS Lookup
12 |
13 | A simple browser-based tool to perform DNS lookups. Type a domain, search, and instantly get results.
14 |
15 | ### [➡️ Use now](https://www.digitalocean.com/community/tools/dns)
16 |
17 | ---
18 |
19 |
20 |
21 |
22 |
23 | ## SPF Explainer
24 |
25 | A tool that explains a domain's SPF records. Search a domain and either explore its records or evaluate an IP for mail sending.
26 |
27 | ### [➡️ Use now](https://www.digitalocean.com/community/tools/spf)
28 |
--------------------------------------------------------------------------------
/src/static/dns-lookup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/do-community/dns-tool/abf1f57932cf0d064d7bd61cbe667210d9f1be69/src/static/dns-lookup.png
--------------------------------------------------------------------------------
/src/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/src/static/spf-explainer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/do-community/dns-tool/abf1f57932cf0d064d7bd61cbe667210d9f1be69/src/static/spf-explainer.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es2018",
5 | "allowJs": true,
6 | "esModuleInterop": true,
7 | "noImplicitAny": true,
8 | "noEmit": false,
9 | "strict": true
10 | },
11 | "include": [
12 | "src/**/*"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/user-flow-dns-lookup.md:
--------------------------------------------------------------------------------
1 | # User Flow (DNS Lookup)
2 |
3 | The goal for this website is to allow users to lookup DNS records. For example, if a user wants to lookup a A record to check that users can access their droplet from their domain properly, they would go to the DNS lookup page, search the record, get the record and close the page. If the user wanted to share a record type with other people, they could click the link button next to the record type and then copy the URL to their current page. When someone was linked it, they would be pointed to that exact record type for that domain.
4 |
5 | When you visit the page, you should be directed to the page with the textbox active if the domain is not part of the URL query argument. This is so that you can easily just start typing:
6 |
7 | 
8 |
9 | It should be clear to the user that it will only search the (sub-)domain they type in. It will not search other sub-domains. This is by design due to how DNS servers return results.
10 |
11 | Pressing the "Search DNS Records" button or hitting ENTER in the text box will call a JavaScript function which will allow for the searching of records. If the user has a domain as part of the URL query, the textbox should not be focused, the textbox should be populated with the HTTP query and it should act like the "Search DNS Records" button has been clicked.
12 |
13 | Currently, before the user searches, there is a skeleton of what the page will look like. This gets replaced with the actual values after a search:
14 |
15 | 
16 |
17 | When searching, the button should go into a loading state. This makes it obvious to the user that the page is loading:
18 |
19 | 
20 |
21 | If the domain is invalid, a error of some description should be displayed and stop the operation. This should be very visible for the user, for example by using an alert prompt:
22 |
23 | 
24 |
25 | When the user searches, results should feel instant. Since we are using Cloudflare DNS, the only bottleneck here should be the speed of the connection which the user is on. For parts which can be slower such as WHOIS, we should have it say `Loading