| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "helpers.h" |
| |
| #include <iostream> |
| #include <string> |
| |
| #include <arpa/inet.h> |
| #include <error.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <netdb.h> |
| |
| #include <base/strings/strcat.h> |
| |
| bool ConvertIppToHttp(std::string& url) { |
| auto pos = url.find("://"); |
| if (pos == std::string::npos) { |
| std::cerr << "Incorrect URL: " << url << ".\n"; |
| std::cerr << "You have to set url parameter, e.g.:"; |
| std::cerr << " --url=ipp://10.11.12.13/ipp/print." << std::endl; |
| return false; |
| } |
| const auto protocol = url.substr(0, pos); |
| if (protocol == "http" || protocol == "https") { |
| return true; |
| } |
| std::string default_port; |
| if (protocol == "ipp") { |
| default_port = "631"; |
| } else if (protocol == "ipps") { |
| default_port = "443"; |
| } else { |
| std::cerr << "Incorrect URL protocol: " << protocol << ".\n"; |
| std::cerr << "Supported protocols: http, https, ipp, ipps." << std::endl; |
| return false; |
| } |
| url = "htt" + url.substr(2); |
| pos += 4; |
| pos = url.find_first_of(":/?#", pos); |
| if (pos == std::string::npos) { |
| url += ":" + default_port; |
| } else if (url[pos] != ':') { |
| url = url.substr(0, pos) + ":" + default_port + url.substr(pos); |
| } |
| return true; |
| } |
| |
| bool ResolveZeroconfHostname(std::string& url, ResolveFunc resolver) { |
| if (!resolver) { |
| resolver = &getaddrinfo; |
| } |
| |
| auto host_start = url.find("://"); |
| if (host_start == std::string::npos || host_start < 3) { |
| std::cerr << "URL missing protocol: " << url << ".\n"; |
| return false; |
| } |
| host_start += 3; |
| |
| auto host_end = url.find_first_of(":/", host_start); |
| if (host_end == std::string::npos) { |
| std::cerr << "URL missing end of hostname: " << url << ".\n"; |
| return false; |
| } |
| |
| std::string hostname = url.substr(host_start, host_end - host_start); |
| if (!hostname.ends_with(".local")) { |
| return true; |
| } |
| |
| struct addrinfo hints; |
| memset(&hints, 0, sizeof(hints)); |
| hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; |
| hints.ai_family = AF_UNSPEC; |
| hints.ai_socktype = SOCK_STREAM; |
| hints.ai_protocol = 0; |
| struct addrinfo* res = nullptr; |
| int err = resolver(hostname.c_str(), NULL, &hints, &res); |
| if (err != 0) { |
| std::cerr << "Failed to look up hostname " << hostname << ": " |
| << gai_strerror(err); |
| return false; |
| } |
| |
| std::string new_host; |
| switch (res->ai_family) { |
| case AF_INET: { |
| struct sockaddr_in* ip4 = (struct sockaddr_in*)res->ai_addr; |
| char ip4_str[INET_ADDRSTRLEN]; |
| if (!inet_ntop(res->ai_family, &(ip4->sin_addr), ip4_str, |
| INET_ADDRSTRLEN)) { |
| std::cerr << "Failed to convert address to text: " << strerror(errno); |
| return false; |
| } |
| new_host = ip4_str; |
| freeaddrinfo(res); |
| break; |
| } |
| case AF_INET6: { |
| struct sockaddr_in6* ip6 = (struct sockaddr_in6*)res->ai_addr; |
| char ip6_str[INET6_ADDRSTRLEN]; |
| if (!inet_ntop(res->ai_family, &(ip6->sin6_addr), ip6_str, |
| INET6_ADDRSTRLEN)) { |
| std::cerr << "Failed to convert address to text: " << strerror(errno); |
| return false; |
| } |
| new_host = base::StrCat({"[", ip6_str, "]"}); |
| freeaddrinfo(res); |
| break; |
| } |
| default: |
| std::cerr << "Unknown address family " << res->ai_family; |
| freeaddrinfo(res); |
| return false; |
| } |
| |
| url.replace(url.begin() + host_start, url.begin() + host_end, new_host); |
| return true; |
| } |