1 #include "utilities/utils.h"
15 #include <archive_entry.h>
16 #include <arpa/inet.h>
20 #include <netinet/in.h>
22 #include <sys/types.h>
24 #include <boost/algorithm/string.hpp>
25 #include <boost/archive/iterators/base64_from_binary.hpp>
26 #include <boost/archive/iterators/binary_from_base64.hpp>
27 #include <boost/archive/iterators/remove_whitespace.hpp>
28 #include <boost/archive/iterators/transform_width.hpp>
29 #include <boost/filesystem.hpp>
30 #include <boost/uuid/random_generator.hpp>
31 #include <boost/uuid/uuid_io.hpp>
33 #include "aktualizr_version.h"
34 #include "logging/logging.h"
36 const char *adverbs[] = {
37 "adorable",
"acidic",
"ample",
"aromatic",
"artistic",
"attractive",
"basic",
"beautiful",
38 "best",
"blissful",
"bubbly",
"celebrated",
"cheap",
"chilly",
"cloudy",
"colorful",
39 "colossal",
"complete",
"conventional",
"costly",
"creamy",
"crisp",
"dense",
"double",
40 "dry",
"easy",
"every",
"exotic",
"expert",
"fake",
"fabulous",
"fast",
41 "fine",
"firm",
"first",
"flaky",
"flat",
"fluffy",
"frozen",
"generous",
42 "giant",
"glass",
"glorious",
"golden",
"good",
"grand",
"great",
"half",
43 "happy",
"hard",
"healthy",
"heavy",
"hot",
"huge",
"humble",
"ideal",
44 "icy",
"incredible",
"interesting",
"joyous",
"juicy",
"jumbo",
"large",
"late",
45 "lavish",
"leafy",
"lean",
"light",
"lovely",
"marvelous",
"mature",
"modern",
46 "modest",
"neat",
"new",
"nice",
"nifty",
"nutty",
"oily",
"ornate",
47 "perfect",
"plain",
"posh",
"pretty",
"prime",
"proper",
"pure",
"quick",
48 "raw",
"real",
"rich",
"ripe",
"safe",
"salty",
"several",
"short",
49 "simple",
"slim",
"slow",
"smooth",
"soft",
"solid",
"speedy",
"spotless",
50 "strong",
"stylish",
"subtle",
"super",
"sweet",
"tasty",
"tempting",
"tender",
51 "terrific",
"thick",
"thin",
"tidy",
"tiny",
"twin",
"ultimate",
"unique",
52 "uniform",
"unusual",
"valuable",
"vast",
"warm",
"wavy",
"wet",
"whole",
53 "wide",
"wild",
"wooden",
"young"};
55 const char *names[] = {
"Allerlei",
141 "Prinzregententorte",
163 "Sonnenblumenkernbrot",
184 typedef boost::archive::iterators::base64_from_binary<
185 boost::archive::iterators::transform_width<std::string::const_iterator, 6, 8> >
188 typedef boost::archive::iterators::transform_width<
189 boost::archive::iterators::binary_from_base64<
190 boost::archive::iterators::remove_whitespace<std::string::const_iterator> >,
194 std::string Utils::fromBase64(std::string base64_string) {
195 int64_t paddingChars = std::count(base64_string.begin(), base64_string.end(),
'=');
196 std::replace(base64_string.begin(), base64_string.end(),
'=',
'A');
197 std::string
result(base64_to_bin(base64_string.begin()), base64_to_bin(base64_string.end()));
202 std::string Utils::toBase64(
const std::string &tob64) {
203 std::string b64sig(base64_text(tob64.begin()), base64_text(tob64.end()));
204 b64sig.append((3 - tob64.length() % 3) % 3,
'=');
209 std::string Utils::stripQuotes(
const std::string &value) {
210 std::string res = value;
211 res.erase(std::remove(res.begin(), res.end(),
'\"'), res.end());
216 std::string Utils::addQuotes(
const std::string &value) {
return "\"" + value +
"\""; }
218 std::string Utils::extractField(
const std::string &in,
unsigned int field_id) {
220 auto it = in.begin();
223 for (; it != in.end() && (isspace(*it) != 0); it++) {
226 for (
unsigned int k = 0; k < field_id; k++) {
228 for (; it != in.end() && (isspace(*it) == 0); it++) {
232 throw std::runtime_error(std::string(
"No such field ").append(std::to_string(field_id)));
234 for (; it != in.end() && (isspace(*it) != 0); it++) {
239 for (; it != in.end() && (isspace(*it) == 0); it++) {
245 Json::Value Utils::parseJSON(
const std::string &json_str) {
246 std::istringstream strs(json_str);
247 Json::Value json_value;
248 parseFromStream(Json::CharReaderBuilder(), strs, &json_value,
nullptr);
252 Json::Value Utils::parseJSONFile(
const boost::filesystem::path &filename) {
253 std::ifstream path_stream(filename.c_str());
254 std::string content((std::istreambuf_iterator<char>(path_stream)), std::istreambuf_iterator<char>());
255 return Utils::parseJSON(content);
258 std::string Utils::genPrettyName() {
259 std::random_device urandom;
261 std::uniform_int_distribution<> adverbs_dist(0, (
sizeof(adverbs) /
sizeof(
char *)) - 1);
262 std::uniform_int_distribution<> nouns_dist(0, (
sizeof(names) /
sizeof(
char *)) - 1);
263 std::uniform_int_distribution<> digits(0, 999);
264 std::stringstream pretty_name;
265 pretty_name << adverbs[adverbs_dist(urandom)];
267 pretty_name << names[nouns_dist(urandom)];
269 pretty_name << digits(urandom);
270 std::string res = pretty_name.str();
271 std::transform(res.begin(), res.end(), res.begin(), ::tolower);
275 std::string Utils::readFile(
const boost::filesystem::path &filename,
const bool trim) {
276 boost::filesystem::path tmpFilename = filename;
277 tmpFilename +=
".new";
282 if (boost::filesystem::exists(tmpFilename)) {
283 LOG_WARNING << tmpFilename <<
" was found on FS, removing";
284 boost::filesystem::remove(tmpFilename);
286 std::ifstream path_stream(filename.c_str());
287 std::string content((std::istreambuf_iterator<char>(path_stream)), std::istreambuf_iterator<char>());
290 boost::trim_if(content, boost::is_any_of(
" \t\r\n"));
295 static constexpr
size_t BSIZE = 20 * 512;
300 std::array<char, BSIZE> buf{};
303 static ssize_t read_cb(
struct archive *a,
void *client_data,
const void **buffer) {
304 auto s = reinterpret_cast<archive_state *>(client_data);
306 archive_set_error(a, -1,
"unable to read from stream");
312 s->is.read(s->buf.data(), BSIZE);
313 if (!s->is.eof() && s->is.fail()) {
314 archive_set_error(a, -1,
"unable to read from stream");
317 *buffer = s->buf.data();
319 return s->is.gcount();
322 void Utils::writeFile(
const boost::filesystem::path &filename,
const std::string &content,
bool create_directories) {
323 if (create_directories) {
324 boost::filesystem::create_directories(filename.parent_path());
326 Utils::writeFile(filename, content.c_str(), content.size());
329 void Utils::writeFile(
const boost::filesystem::path &filename,
const char *content,
size_t size) {
332 boost::filesystem::path tmpFilename = filename;
333 tmpFilename +=
".new";
335 std::ofstream file(tmpFilename.c_str());
337 throw std::runtime_error(std::string(
"Error opening file ") + tmpFilename.string());
339 file.write(content, static_cast<std::streamsize>(size));
342 boost::filesystem::rename(tmpFilename, filename);
345 void Utils::writeFile(
const boost::filesystem::path &filename,
const Json::Value &content,
bool create_directories) {
346 Utils::writeFile(filename, jsonToStr(content), create_directories);
349 std::string Utils::jsonToStr(
const Json::Value &json) {
350 std::stringstream ss;
355 std::string Utils::jsonToCanonicalStr(
const Json::Value &json) {
356 static Json::StreamWriterBuilder wbuilder = []() {
357 Json::StreamWriterBuilder w;
358 wbuilder[
"indentation"] =
"";
361 return Json::writeString(wbuilder, json);
364 Json::Value Utils::getHardwareInfo() {
366 const int exit_code = shell(
"lshw -json", &
result);
368 if (exit_code != 0) {
369 LOG_WARNING <<
"Could not execute lshw (is it installed?).";
370 return Json::Value();
372 const Json::Value parsed = Utils::parseJSON(
result);
373 return (parsed.isArray()) ? parsed[0] : parsed;
376 Json::Value Utils::getNetworkInfo() {
378 std::ifstream path_stream(
"/proc/net/route");
379 std::string route_content((std::istreambuf_iterator<char>(path_stream)), std::istreambuf_iterator<char>());
382 std::string name = std::string();
383 std::string ip = std::string();
384 std::string mac = std::string();
386 std::istringstream route_stream(route_content);
387 std::array<char, 200> line{};
390 route_stream.getline(&line[0], line.size());
391 while (route_stream.getline(&line[0], line.size())) {
392 std::string itfn = Utils::extractField(&line[0], 0);
393 std::string droute = Utils::extractField(&line[0], 1);
394 if (droute ==
"00000000") {
401 if (itf.name !=
"") {
404 StructGuard<struct ifaddrs> ifaddrs(
nullptr, freeifaddrs);
407 if (getifaddrs(&ifa) < 0) {
408 LOG_ERROR <<
"getifaddrs: " << std::strerror(errno);
413 if (ifaddrs !=
nullptr) {
414 for (
struct ifaddrs *ifa = ifaddrs.get(); ifa !=
nullptr; ifa = ifa->ifa_next) {
415 if (itf.name == ifa->ifa_name) {
416 if (ifa->ifa_addr ==
nullptr) {
419 if (ifa->ifa_addr->sa_family != AF_INET) {
422 const struct sockaddr_storage *sa = reinterpret_cast<struct sockaddr_storage *>(ifa->ifa_addr);
424 itf.ip = Utils::ipDisplayName(*sa);
431 std::ifstream mac_stream(
"/sys/class/net/" + itf.name +
"/address");
432 std::string m((std::istreambuf_iterator<char>(mac_stream)), std::istreambuf_iterator<char>());
433 itf.mac = std::move(m);
434 boost::trim_right(itf.mac);
438 Json::Value network_info;
439 network_info[
"local_ipv4"] = itf.ip;
440 network_info[
"mac"] = itf.mac;
441 network_info[
"hostname"] = Utils::getHostname();
446 std::string Utils::getHostname() {
448 if (gethostname(hostname, 200) < 0) {
454 std::string Utils::randomUuid() {
455 std::random_device urandom;
456 boost::uuids::basic_random_generator<std::random_device> uuid_gen(urandom);
457 return boost::uuids::to_string(uuid_gen());
461 void Utils::copyDir(
const boost::filesystem::path &from,
const boost::filesystem::path &to) {
462 boost::filesystem::remove_all(to);
464 boost::filesystem::create_directories(to);
465 boost::filesystem::directory_iterator it(from);
467 for (; it != boost::filesystem::directory_iterator(); it++) {
468 if (boost::filesystem::is_directory(it->path())) {
469 copyDir(it->path(), to / it->path().filename());
471 boost::filesystem::copy_file(it->path(), to / it->path().filename());
476 std::string Utils::readFileFromArchive(std::istream &as,
const std::string &filename,
const bool trim) {
477 struct archive *a = archive_read_new();
479 LOG_ERROR <<
"archive error: could not initialize archive object";
480 throw std::runtime_error(
"archive error");
482 archive_read_support_filter_all(a);
483 archive_read_support_format_all(a);
484 auto state = std_::make_unique<archive_state>(std::ref(as));
485 int r = archive_read_open(a, reinterpret_cast<void *>(state.get()),
nullptr, read_cb,
nullptr);
486 if (r != ARCHIVE_OK) {
487 LOG_ERROR <<
"archive error: " << archive_error_string(a);
488 archive_read_free(a);
489 throw std::runtime_error(
"archive error");
493 std::stringstream out_stream;
494 struct archive_entry *entry;
495 while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
496 if (filename != archive_entry_pathname(entry)) {
497 archive_read_data_skip(a);
506 r = archive_read_data_block(a, reinterpret_cast<const void **>(&buff), &size, &offset);
507 if (r == ARCHIVE_EOF) {
511 if (r != ARCHIVE_OK) {
512 LOG_ERROR <<
"archive error: " << archive_error_string(a);
515 if (size > 0 && buff !=
nullptr) {
516 out_stream.write(buff, static_cast<ssize_t>(size));
521 r = archive_read_free(a);
522 if (r != ARCHIVE_OK) {
523 LOG_ERROR <<
"archive error: " << archive_error_string(a);
527 throw std::runtime_error(
"could not extract " + filename +
" from archive");
530 std::string
result = out_stream.str();
532 boost::trim_if(
result, boost::is_any_of(
" \t\r\n"));
537 static ssize_t write_cb(
struct archive *a,
void *client_data,
const void *buffer,
size_t length) {
538 auto s = reinterpret_cast<std::ostream *>(client_data);
539 s->write(reinterpret_cast<const char *>(buffer), static_cast<ssize_t>(length));
541 archive_set_error(a, -1,
"unable to write in stream");
545 return static_cast<ssize_t>(length);
548 void Utils::writeArchive(
const std::map<std::string, std::string> &entries, std::ostream &as) {
549 struct archive *a = archive_write_new();
551 LOG_ERROR <<
"archive error: could not initialize archive object";
552 throw std::runtime_error(
"archive error");
554 archive_write_set_format_pax(a);
555 archive_write_add_filter_gzip(a);
557 int r = archive_write_open(a, reinterpret_cast<void *>(&as),
nullptr, write_cb,
nullptr);
558 if (r != ARCHIVE_OK) {
559 LOG_ERROR <<
"archive error: " << archive_error_string(a);
560 archive_write_free(a);
561 throw std::runtime_error(
"archive error");
564 struct archive_entry *entry = archive_entry_new();
565 for (
const auto &el : entries) {
566 archive_entry_clear(entry);
567 archive_entry_set_filetype(entry, AE_IFREG);
568 archive_entry_set_perm(entry, S_IRWXU | S_IRWXG | S_IRWXO);
569 archive_entry_set_size(entry, static_cast<ssize_t>(el.second.size()));
570 archive_entry_set_pathname(entry, el.first.c_str());
571 if (archive_write_header(a, entry) != 0) {
572 LOG_ERROR <<
"archive error: " << archive_error_string(a);
573 archive_entry_free(entry);
574 archive_write_free(a);
575 throw std::runtime_error(
"archive error");
577 if (archive_write_data(a, el.second.c_str(), el.second.size()) < 0) {
578 LOG_ERROR <<
"archive error: " << archive_error_string(a);
579 archive_entry_free(entry);
580 archive_write_free(a);
581 throw std::runtime_error(
"archive error");
584 archive_entry_free(entry);
585 r = archive_write_free(a);
586 if (r != ARCHIVE_OK) {
587 LOG_ERROR <<
"archive error: " << archive_error_string(a);
591 sockaddr_storage Utils::ipGetSockaddr(
int fd) {
592 sockaddr_storage ss{};
593 socklen_t len =
sizeof(ss);
594 if (getsockname(fd, reinterpret_cast<sockaddr *>(&ss), &len) < 0) {
595 throw std::runtime_error(std::string(
"Could not get sockaddr: ") + std::strerror(errno));
601 std::string Utils::ipDisplayName(
const sockaddr_storage &saddr) {
602 char ipstr[INET6_ADDRSTRLEN];
604 switch (saddr.ss_family) {
606 const auto *sa = reinterpret_cast<const sockaddr_in *>(&saddr);
607 inet_ntop(AF_INET, &sa->sin_addr, ipstr,
sizeof(ipstr));
608 return std::string(ipstr);
611 const auto *sa = reinterpret_cast<const sockaddr_in6 *>(&saddr);
612 inet_ntop(AF_INET6, &sa->sin6_addr, ipstr,
sizeof(ipstr));
613 return std::string(ipstr);
620 int Utils::ipPort(
const sockaddr_storage &saddr) {
622 if (saddr.ss_family == AF_INET) {
623 const auto *sa = reinterpret_cast<const sockaddr_in *>(&saddr);
625 }
else if (saddr.ss_family == AF_INET6) {
626 const auto *sa = reinterpret_cast<const sockaddr_in6 *>(&saddr);
635 int Utils::shell(
const std::string &command, std::string *output,
bool include_stderr) {
637 std::string full_command(command);
638 if (include_stderr) {
639 full_command +=
" 2>&1";
641 FILE *pipe = popen(full_command.c_str(),
"r");
642 if (pipe ==
nullptr) {
643 *output =
"popen() failed!";
646 while (feof(pipe) == 0) {
647 if (fgets(buffer, 128, pipe) !=
nullptr) {
651 int exitcode = pclose(pipe);
652 return WEXITSTATUS(exitcode);
655 boost::filesystem::path Utils::absolutePath(
const boost::filesystem::path &root,
const boost::filesystem::path &file) {
656 if (file.is_absolute() || root.empty()) {
659 return (root / file);
664 std::vector<boost::filesystem::path> Utils::getDirEntriesByExt(
const boost::filesystem::path &dir_path,
665 const std::string &ext) {
666 std::vector<boost::filesystem::path> entries;
667 boost::filesystem::directory_iterator entryItEnd, entryIt(dir_path);
668 for (; entryIt != entryItEnd; ++entryIt) {
669 auto &entry_path = entryIt->path();
670 if (!boost::filesystem::is_directory(*entryIt) && entry_path.extension().string() == ext) {
671 entries.push_back(entry_path);
674 std::sort(entries.begin(), entries.end());
678 void Utils::createDirectories(
const boost::filesystem::path &path, mode_t mode) {
679 boost::filesystem::path parent = path.parent_path();
680 if (!parent.empty() && !boost::filesystem::exists(parent)) {
681 Utils::createDirectories(parent, mode);
683 if (mkdir(path.c_str(), mode) == -1) {
684 throw std::runtime_error(std::string(
"could not create directory: ").append(path.native()));
686 std::cout <<
"created: " << path.native() <<
"\n";
689 bool Utils::createSecureDirectory(
const boost::filesystem::path &path) {
690 if (mkdir(path.c_str(), S_IRWXU) == 0) {
697 int ret = stat(path.c_str(), &st);
702 return (ret >= 0 && ((st.st_mode & S_IFDIR) == S_IFDIR) && (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == S_IRWXU &&
703 (st.st_uid == getuid()));
706 std::string Utils::urlEncode(
const std::string &input) {
709 for (
char c : input) {
710 if ((c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z') || (c >=
'0' && c <=
'9') || c ==
'-' || c ==
'.' ||
711 c ==
'_' || c ==
'~' || c ==
'/') {
715 auto nib = static_cast<char>(((c >> 4) & 0x0F));
716 res.push_back(static_cast<char>((nib < 10) ? nib +
'0' : nib - 10 +
'A'));
717 nib = static_cast<char>(c & 0x0F);
718 res.push_back(static_cast<char>((nib < 10) ? nib +
'0' : nib - 10 +
'A'));
724 CURL *Utils::curlDupHandleWrapper(CURL *
const curl_in,
const bool using_pkcs11) {
725 CURL *curl = curl_easy_duphandle(curl_in);
732 curlEasySetoptWrapper(curl, CURLOPT_SSLENGINE,
"pkcs11");
743 static boost::filesystem::path &Get() {
751 boost::filesystem::path prefix = Utils::getStorageRootPath();
752 if (prefix.empty()) {
753 prefix = boost::filesystem::temp_directory_path();
755 boost::filesystem::path p = prefix / boost::filesystem::unique_path(
"aktualizr-%%%%-%%%%-%%%%-%%%%");
756 if (mkdir(p.c_str(), S_IRWXU) == -1) {
757 throw std::runtime_error(std::string(
"could not create temporary directory root: ").append(p.native()));
760 path = boost::filesystem::path(p);
764 boost::filesystem::remove_all(path);
770 boost::filesystem::path path;
773 std::string Utils::storage_root_path_;
775 void Utils::setStorageRootPath(
const std::string &storage_root_path) { storage_root_path_ = storage_root_path; }
777 boost::filesystem::path Utils::getStorageRootPath() {
return storage_root_path_; }
779 void Utils::setUserAgent(std::string user_agent) { user_agent_ = std::move(user_agent); }
781 const char *Utils::getUserAgent() {
782 if (user_agent_.empty()) {
783 user_agent_ = (std::string(
"Aktualizr/") + aktualizr_version());
785 return user_agent_.c_str();
788 std::string Utils::user_agent_;
790 void Utils::setCaPath(boost::filesystem::path path) { ca_path_ = std::move(path); }
792 const char *Utils::getCaPath() {
return ca_path_.c_str(); }
794 boost::filesystem::path Utils::ca_path_{
"/etc/ssl/certs"};
796 TemporaryFile::TemporaryFile(
const std::string &hint)
797 : tmp_name_(
SafeTempRoot::Get() / boost::filesystem::unique_path(std::string(
"%%%%-%%%%-").append(hint))) {}
799 TemporaryFile::~TemporaryFile() { boost::filesystem::remove(tmp_name_); }
801 void TemporaryFile::PutContents(
const std::string &contents) {
802 mode_t mode = S_IRUSR | S_IWUSR;
803 int fd = open(Path().c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode);
805 throw std::runtime_error(std::string(
"Could not write content to file: ") + Path().
string() +
": " +
806 std::strerror(errno));
808 ssize_t written = write(fd, contents.c_str(), contents.size());
810 if (written < 0 || static_cast<size_t>(written) != contents.size()) {
811 throw std::runtime_error(std::string(
"Could not write content to file: ") + Path().
string());
815 boost::filesystem::path TemporaryFile::Path()
const {
return tmp_name_; }
817 std::string TemporaryFile::PathString()
const {
return Path().string(); }
819 TemporaryDirectory::TemporaryDirectory(
const std::string &hint)
820 : tmp_name_(
SafeTempRoot::Get() / boost::filesystem::unique_path(std::string(
"%%%%-%%%%-").append(hint))) {
821 Utils::createDirectories(tmp_name_, S_IRWXU);
824 TemporaryDirectory::~TemporaryDirectory() { boost::filesystem::remove_all(tmp_name_); }
826 boost::filesystem::path TemporaryDirectory::Path()
const {
return tmp_name_; }
828 boost::filesystem::path TemporaryDirectory::operator/(
const boost::filesystem::path &subdir)
const {
829 return (tmp_name_ / subdir);
832 std::string TemporaryDirectory::PathString()
const {
return Path().string(); }
834 void Utils::setSocketPort(sockaddr_storage *addr, in_port_t port) {
835 if (addr->ss_family == AF_INET) {
836 reinterpret_cast<sockaddr_in *>(addr)->sin_port = port;
837 }
else if (addr->ss_family == AF_INET6) {
838 reinterpret_cast<sockaddr_in6 *>(addr)->sin6_port = port;
842 bool operator<(
const sockaddr_storage &left,
const sockaddr_storage &right) {
843 if (left.ss_family == AF_INET) {
844 throw std::runtime_error(
"IPv4 addresses are not supported");
846 const unsigned char *left_addr = reinterpret_cast<const sockaddr_in6 *>(&left)->sin6_addr.s6_addr;
847 const unsigned char *right_addr = reinterpret_cast<const sockaddr_in6 *>(&right)->sin6_addr.s6_addr;
848 int res = memcmp(left_addr, right_addr, 16);
854 socket_fd_ = socket(AF_INET, SOCK_STREAM, 0);
855 if (-1 == socket_fd_) {
856 throw std::system_error(errno, std::system_category(),
"socket");
860 Socket::~Socket() { ::close(socket_fd_); }
862 std::string Socket::toString() {
863 auto saddr = Utils::ipGetSockaddr(socket_fd_);
864 return Utils::ipDisplayName(saddr) +
":" + std::to_string(Utils::ipPort(saddr));
867 void Socket::bind(in_port_t port,
bool reuse) {
869 memset(&sa, 0,
sizeof(sa));
870 sa.sin_family = AF_INET;
871 sa.sin_port = htons(port);
872 sa.sin_addr.s_addr = htonl(INADDR_ANY);
874 int reuseaddr = reuse ? 1 : 0;
875 if (-1 == setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
sizeof(reuseaddr))) {
876 throw std::system_error(errno, std::system_category(),
"socket");
879 if (-1 == ::bind(socket_fd_, reinterpret_cast<const sockaddr *>(&sa),
sizeof(sa))) {
880 throw std::system_error(errno, std::system_category(),
"socket");
884 ListenSocket::ListenSocket(in_port_t port) : _port(port) {
888 auto ephemeral_port = Utils::ipPort(Utils::ipGetSockaddr(socket_fd_));
889 if (-1 != ephemeral_port) {
890 _port = static_cast<in_port_t>(ephemeral_port);
895 ConnectionSocket::ConnectionSocket(
const std::string &ip, in_port_t port, in_port_t bind_port)
896 : remote_sock_address_{} {
897 memset(&remote_sock_address_, 0,
sizeof(remote_sock_address_));
898 remote_sock_address_.sin_family = AF_INET;
899 if (-1 == inet_pton(AF_INET, ip.c_str(), &(remote_sock_address_.sin_addr))) {
900 throw std::system_error(errno, std::system_category(),
"socket");
902 remote_sock_address_.sin_port = htons(port);
909 ConnectionSocket::~ConnectionSocket() { ::shutdown(socket_fd_, SHUT_RDWR); }
911 int ConnectionSocket::connect() {
912 return ::connect(socket_fd_, reinterpret_cast<const struct sockaddr *>(&remote_sock_address_),
913 sizeof(remote_sock_address_));
916 CurlEasyWrapper::CurlEasyWrapper() {
917 handle = curl_easy_init();
918 if (handle ==
nullptr) {
919 throw std::runtime_error(
"Could not initialize curl handle");
921 curlEasySetoptWrapper(handle, CURLOPT_USERAGENT, Utils::getUserAgent());
924 CurlEasyWrapper::~CurlEasyWrapper() {
925 if (handle !=
nullptr) {
926 curl_easy_cleanup(handle);