format_url.cc 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. // This binary takes a list of domain names in ASCII or unicode, passes them
  5. // through the IDN decoding algorithm and prints out the result. The list can be
  6. // passed as a text file or via stdin. In both cases, the output is printed as
  7. // (input_domain, output_domain, spoof_check_result) tuples on separate lines.
  8. // spoof_check_result is the string representation of IDNSpoofChecker::Result
  9. // enum with an additional kTopDomainLookalike value.
  10. #include <cstdlib>
  11. #include <fstream>
  12. #include <iostream>
  13. #include <string>
  14. #include "base/command_line.h"
  15. #include "base/i18n/icu_util.h"
  16. #include "base/logging.h"
  17. #include "base/notreached.h"
  18. #include "base/strings/string_util.h"
  19. #include "base/strings/utf_string_conversions.h"
  20. #include "components/url_formatter/spoof_checks/idn_spoof_checker.h"
  21. #include "components/url_formatter/url_formatter.h"
  22. #include "url/gurl.h"
  23. using url_formatter::IDNConversionResult;
  24. using url_formatter::IDNSpoofChecker;
  25. void PrintUsage(const char* process_name) {
  26. std::cout << "Usage:" << std::endl;
  27. std::cout << process_name << " <file>" << std::endl;
  28. std::cout << std::endl;
  29. std::cout << "<file> is a text file with one hostname per line." << std::endl;
  30. std::cout << "Hostnames can be ASCII or unicode. Internationalized domain "
  31. "can (IDN) be encoded in unicode or punycode."
  32. << std::endl;
  33. std::cout << "Each hostname is converted to unicode, if safe. Otherwise, "
  34. << "ASCII hostnames are printed unchanged and unicode hostnames "
  35. << "are printed in punycode." << std::endl;
  36. }
  37. std::string SpoofCheckResultToString(IDNSpoofChecker::Result result) {
  38. switch (result) {
  39. case IDNSpoofChecker::Result::kNone:
  40. return "kNone";
  41. case IDNSpoofChecker::Result::kSafe:
  42. return "kSafe";
  43. case IDNSpoofChecker::Result::kICUSpoofChecks:
  44. return "kICUSpoofChecks";
  45. case IDNSpoofChecker::Result::kDeviationCharacters:
  46. return "kDeviationCharacters";
  47. case IDNSpoofChecker::Result::kTLDSpecificCharacters:
  48. return "kTLDSpecificCharacters";
  49. case IDNSpoofChecker::Result::kUnsafeMiddleDot:
  50. return "kUnsafeMiddleDot";
  51. case IDNSpoofChecker::Result::kWholeScriptConfusable:
  52. return "kWholeScriptConfusable";
  53. case IDNSpoofChecker::Result::kDigitLookalikes:
  54. return "kDigitLookalikes";
  55. case IDNSpoofChecker::Result::kNonAsciiLatinCharMixedWithNonLatin:
  56. return "kNonAsciiLatinCharMixedWithNonLatin";
  57. case IDNSpoofChecker::Result::kDangerousPattern:
  58. return "kDangerousPattern";
  59. default:
  60. NOTREACHED();
  61. };
  62. return std::string();
  63. }
  64. // Returns the spoof check result as a string. |ascii_domain| must contain
  65. // ASCII characters only. |unicode_domain| is the IDN conversion result
  66. // according to url_formatter. It can be either punycode or unicode.
  67. std::string GetSpoofCheckResult(const std::string& ascii_domain,
  68. const std::u16string& unicode_domain) {
  69. IDNConversionResult result =
  70. url_formatter::UnsafeIDNToUnicodeWithDetails(ascii_domain);
  71. std::string spoof_check_result =
  72. SpoofCheckResultToString(result.spoof_check_result);
  73. if (result.spoof_check_result == IDNSpoofChecker::Result::kNone) {
  74. // Input was not punycode.
  75. return spoof_check_result;
  76. }
  77. if (result.spoof_check_result != IDNSpoofChecker::Result::kSafe) {
  78. return spoof_check_result;
  79. }
  80. // If the domain passed all spoof checks but |unicode_domain| is still in
  81. // punycode, the domain must be a lookalike of a top domain.
  82. if (base::ASCIIToUTF16(ascii_domain) == unicode_domain) {
  83. return "kTopDomainLookalike";
  84. }
  85. return spoof_check_result;
  86. }
  87. void Convert(std::istream& input) {
  88. base::i18n::InitializeICU();
  89. for (std::string line; std::getline(input, line);) {
  90. CHECK(
  91. !base::StartsWith(line,
  92. "http:", base::CompareCase::INSENSITIVE_ASCII) &&
  93. !base::StartsWith(line, "https:", base::CompareCase::INSENSITIVE_ASCII))
  94. << "This binary only accepts hostnames" << line;
  95. const std::string ascii_hostname =
  96. base::IsStringASCII(line) ? line : GURL("https://" + line).host();
  97. // Convert twice, first with spoof checks on, then with spoof checks
  98. // ignored inside GetSpoofCheckResult(). This is because only the call to
  99. // UnsafeIDNToUnicodeWithDetails returns information about spoof check
  100. // results (a quirk of the url_formatter interface).
  101. const std::u16string converted_hostname =
  102. url_formatter::IDNToUnicode(ascii_hostname);
  103. const std::string spoof_check_result =
  104. GetSpoofCheckResult(ascii_hostname, converted_hostname);
  105. std::cout << ascii_hostname << ", " << converted_hostname << ", "
  106. << spoof_check_result << std::endl;
  107. }
  108. }
  109. int main(int argc, char* argv[]) {
  110. base::CommandLine::Init(argc, argv);
  111. base::CommandLine* cmd = base::CommandLine::ForCurrentProcess();
  112. if (cmd->HasSwitch("help")) {
  113. PrintUsage(argv[0]);
  114. return 0;
  115. }
  116. if (argc > 1) {
  117. const std::string filename = argv[1];
  118. std::ifstream input(filename);
  119. if (!input.good()) {
  120. LOG(ERROR) << "Could not open file " << filename;
  121. return -1;
  122. }
  123. Convert(input);
  124. } else {
  125. Convert(std::cin);
  126. }
  127. return EXIT_SUCCESS;
  128. }