blob: a5edadc7cdfc03437e85d514e5a876f08e77c426 [file] [log] [blame] [edit]
// 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;
}