1 #include "utilities/utils.h"
16 #include <archive_entry.h>
17 #include <arpa/inet.h>
21 #include <netinet/in.h>
23 #include <sys/types.h>
25 #include <boost/algorithm/string.hpp>
26 #include <boost/archive/iterators/base64_from_binary.hpp>
27 #include <boost/archive/iterators/binary_from_base64.hpp>
28 #include <boost/archive/iterators/remove_whitespace.hpp>
29 #include <boost/archive/iterators/transform_width.hpp>
30 #include <boost/filesystem.hpp>
31 #include <boost/uuid/random_generator.hpp>
32 #include <boost/uuid/uuid_io.hpp>
34 #include "aktualizr_version.h"
35 #include "logging/logging.h"
37 static const std::array<const char *, 132> adverbs = {
38 "adorable",
"acidic",
"ample",
"aromatic",
"artistic",
"attractive",
"basic",
"beautiful",
39 "best",
"blissful",
"bubbly",
"celebrated",
"cheap",
"chilly",
"cloudy",
"colorful",
40 "colossal",
"complete",
"conventional",
"costly",
"creamy",
"crisp",
"dense",
"double",
41 "dry",
"easy",
"every",
"exotic",
"expert",
"fake",
"fabulous",
"fast",
42 "fine",
"firm",
"first",
"flaky",
"flat",
"fluffy",
"frozen",
"generous",
43 "giant",
"glass",
"glorious",
"golden",
"good",
"grand",
"great",
"half",
44 "happy",
"hard",
"healthy",
"heavy",
"hot",
"huge",
"humble",
"ideal",
45 "icy",
"incredible",
"interesting",
"joyous",
"juicy",
"jumbo",
"large",
"late",
46 "lavish",
"leafy",
"lean",
"light",
"lovely",
"marvelous",
"mature",
"modern",
47 "modest",
"neat",
"new",
"nice",
"nifty",
"nutty",
"oily",
"ornate",
48 "perfect",
"plain",
"posh",
"pretty",
"prime",
"proper",
"pure",
"quick",
49 "raw",
"real",
"rich",
"ripe",
"safe",
"salty",
"several",
"short",
50 "simple",
"slim",
"slow",
"smooth",
"soft",
"solid",
"speedy",
"spotless",
51 "strong",
"stylish",
"subtle",
"super",
"sweet",
"tasty",
"tempting",
"tender",
52 "terrific",
"thick",
"thin",
"tidy",
"tiny",
"twin",
"ultimate",
"unique",
53 "uniform",
"unusual",
"valuable",
"vast",
"warm",
"wavy",
"wet",
"whole",
54 "wide",
"wild",
"wooden",
"young"};
56 static const std::array<const char *, 128> names = {
"Allerlei",
142 "Prinzregententorte",
164 "Sonnenblumenkernbrot",
185 typedef boost::archive::iterators::base64_from_binary<
186 boost::archive::iterators::transform_width<std::string::const_iterator, 6, 8> >
189 typedef boost::archive::iterators::transform_width<
190 boost::archive::iterators::binary_from_base64<
191 boost::archive::iterators::remove_whitespace<std::string::const_iterator> >,
195 std::string Utils::fromBase64(std::string base64_string) {
196 int64_t paddingChars = std::count(base64_string.begin(), base64_string.end(),
'=');
197 std::replace(base64_string.begin(), base64_string.end(),
'=',
'A');
198 std::string
result(base64_to_bin(base64_string.begin()), base64_to_bin(base64_string.end()));
203 std::string Utils::toBase64(
const std::string &tob64) {
204 std::string b64sig(base64_text(tob64.begin()), base64_text(tob64.end()));
205 b64sig.append((3 - tob64.length() % 3) % 3,
'=');
210 std::string Utils::stripQuotes(
const std::string &value) {
211 std::string res = value;
212 res.erase(std::remove(res.begin(), res.end(),
'\"'), res.end());
217 std::string Utils::addQuotes(
const std::string &value) {
return "\"" + value +
"\""; }
219 std::string Utils::extractField(
const std::string &in,
unsigned int field_id) {
221 auto it = in.begin();
224 for (; it != in.end() && (isspace(*it) != 0); it++) {
227 for (
unsigned int k = 0; k < field_id; k++) {
229 for (; it != in.end() && (isspace(*it) == 0); it++) {
233 throw std::runtime_error(std::string(
"No such field ").append(std::to_string(field_id)));
235 for (; it != in.end() && (isspace(*it) != 0); it++) {
240 for (; it != in.end() && (isspace(*it) == 0); it++) {
246 Json::Value Utils::parseJSON(
const std::string &json_str) {
247 std::istringstream strs(json_str);
248 Json::Value json_value;
249 parseFromStream(Json::CharReaderBuilder(), strs, &json_value,
nullptr);
253 Json::Value Utils::parseJSONFile(
const boost::filesystem::path &filename) {
254 std::ifstream path_stream(filename.c_str());
255 std::string content((std::istreambuf_iterator<char>(path_stream)), std::istreambuf_iterator<char>());
256 return Utils::parseJSON(content);
259 std::string Utils::genPrettyName() {
260 std::random_device urandom;
262 std::uniform_int_distribution<size_t> adverbs_dist(0, adverbs.size() - 1);
263 std::uniform_int_distribution<size_t> nouns_dist(0, names.size() - 1);
264 std::uniform_int_distribution<size_t> digits(0, 999);
265 std::stringstream pretty_name;
266 pretty_name << adverbs[adverbs_dist(urandom)];
268 pretty_name << names[nouns_dist(urandom)];
270 pretty_name << digits(urandom);
271 std::string res = pretty_name.str();
272 std::transform(res.begin(), res.end(), res.begin(), ::tolower);
276 std::string Utils::readFile(
const boost::filesystem::path &filename,
const bool trim) {
277 boost::filesystem::path tmpFilename = filename;
278 tmpFilename +=
".new";
283 if (boost::filesystem::exists(tmpFilename)) {
284 LOG_WARNING << tmpFilename <<
" was found on FS, removing";
285 boost::filesystem::remove(tmpFilename);
287 std::ifstream path_stream(filename.c_str());
288 std::string content((std::istreambuf_iterator<char>(path_stream)), std::istreambuf_iterator<char>());
291 boost::trim_if(content, boost::is_any_of(
" \t\r\n"));
296 static constexpr
size_t BSIZE = 20 * 512;
301 std::array<char, BSIZE> buf{};
304 static ssize_t read_cb(
struct archive *a,
void *client_data,
const void **buffer) {
305 auto *s = reinterpret_cast<archive_state *>(client_data);
307 archive_set_error(a, -1,
"unable to read from stream");
313 s->is.read(s->buf.data(), BSIZE);
314 if (!s->is.eof() && s->is.fail()) {
315 archive_set_error(a, -1,
"unable to read from stream");
318 *buffer = s->buf.data();
320 return s->is.gcount();
323 void Utils::writeFile(
const boost::filesystem::path &filename,
const std::string &content,
bool create_directories) {
324 if (create_directories) {
325 boost::filesystem::create_directories(filename.parent_path());
327 Utils::writeFile(filename, content.c_str(), content.size());
330 void Utils::writeFile(
const boost::filesystem::path &filename,
const char *content,
size_t size) {
333 boost::filesystem::path tmpFilename = filename;
334 tmpFilename +=
".new";
336 std::ofstream file(tmpFilename.c_str());
338 throw std::runtime_error(std::string(
"Error opening file ") + tmpFilename.string());
340 file.write(content, static_cast<std::streamsize>(size));
343 boost::filesystem::rename(tmpFilename, filename);
346 void Utils::writeFile(
const boost::filesystem::path &filename,
const Json::Value &content,
bool create_directories) {
347 Utils::writeFile(filename, jsonToStr(content), create_directories);
350 std::string Utils::jsonToStr(
const Json::Value &json) {
351 std::stringstream ss;
356 std::string Utils::jsonToCanonicalStr(
const Json::Value &json) {
357 static Json::StreamWriterBuilder wbuilder = []() {
358 Json::StreamWriterBuilder w;
359 wbuilder[
"indentation"] =
"";
362 return Json::writeString(wbuilder, json);
365 Json::Value Utils::getHardwareInfo() {
367 const int exit_code = shell(
"lshw -json", &
result);
369 if (exit_code != 0) {
370 LOG_WARNING <<
"Could not execute lshw (is it installed?).";
371 return Json::Value();
373 const Json::Value parsed = Utils::parseJSON(
result);
374 return (parsed.isArray()) ? parsed[0] : parsed;
377 Json::Value Utils::getNetworkInfo() {
379 std::ifstream path_stream(
"/proc/net/route");
380 std::string route_content((std::istreambuf_iterator<char>(path_stream)), std::istreambuf_iterator<char>());
383 std::string name = std::string();
384 std::string ip = std::string();
385 std::string mac = std::string();
387 std::istringstream route_stream(route_content);
388 std::array<char, 200> line{};
391 route_stream.getline(&line[0], line.size());
392 while (route_stream.getline(&line[0], line.size())) {
393 std::string itfn = Utils::extractField(&line[0], 0);
394 std::string droute = Utils::extractField(&line[0], 1);
395 if (droute ==
"00000000") {
402 if (itf.name !=
"") {
405 StructGuard<struct ifaddrs> ifaddrs(
nullptr, freeifaddrs);
408 if (getifaddrs(&ifa) < 0) {
409 LOG_ERROR <<
"getifaddrs: " << std::strerror(errno);
414 if (ifaddrs !=
nullptr) {
415 for (
struct ifaddrs *ifa = ifaddrs.get(); ifa !=
nullptr; ifa = ifa->ifa_next) {
416 if (itf.name == ifa->ifa_name) {
417 if (ifa->ifa_addr ==
nullptr) {
420 if (ifa->ifa_addr->sa_family != AF_INET) {
423 const struct sockaddr_storage *sa = reinterpret_cast<struct sockaddr_storage *>(ifa->ifa_addr);
425 itf.ip = Utils::ipDisplayName(*sa);
432 std::ifstream mac_stream(
"/sys/class/net/" + itf.name +
"/address");
433 std::string m((std::istreambuf_iterator<char>(mac_stream)), std::istreambuf_iterator<char>());
434 itf.mac = std::move(m);
435 boost::trim_right(itf.mac);
439 Json::Value network_info;
440 network_info[
"local_ipv4"] = itf.ip;
441 network_info[
"mac"] = itf.mac;
442 network_info[
"hostname"] = Utils::getHostname();
447 std::string Utils::getHostname() {
448 std::array<char, 200> hostname{};
449 if (gethostname(hostname.data(), hostname.size()) < 0) {
452 return std::string(hostname.data());
455 std::string Utils::randomUuid() {
456 std::random_device urandom;
457 boost::uuids::basic_random_generator<std::random_device> uuid_gen(urandom);
458 return boost::uuids::to_string(uuid_gen());
462 void Utils::copyDir(
const boost::filesystem::path &from,
const boost::filesystem::path &to) {
463 boost::filesystem::remove_all(to);
465 boost::filesystem::create_directories(to);
466 boost::filesystem::directory_iterator it(from);
468 for (; it != boost::filesystem::directory_iterator(); it++) {
469 if (boost::filesystem::is_directory(it->path())) {
470 copyDir(it->path(), to / it->path().filename());
472 boost::filesystem::copy_file(it->path(), to / it->path().filename());
477 std::string Utils::readFileFromArchive(std::istream &as,
const std::string &filename,
const bool trim) {
478 struct archive *a = archive_read_new();
480 LOG_ERROR <<
"archive error: could not initialize archive object";
481 throw std::runtime_error(
"archive error");
483 archive_read_support_filter_all(a);
484 archive_read_support_format_all(a);
485 auto state = std_::make_unique<archive_state>(std::ref(as));
486 int r = archive_read_open(a, reinterpret_cast<void *>(state.get()),
nullptr, read_cb,
nullptr);
487 if (r != ARCHIVE_OK) {
488 LOG_ERROR <<
"archive error: " << archive_error_string(a);
489 archive_read_free(a);
490 throw std::runtime_error(
"archive error");
494 std::stringstream out_stream;
495 struct archive_entry *entry;
496 while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
497 if (filename != archive_entry_pathname(entry)) {
498 archive_read_data_skip(a);
507 r = archive_read_data_block(a, reinterpret_cast<const void **>(&buff), &size, &offset);
508 if (r == ARCHIVE_EOF) {
512 if (r != ARCHIVE_OK) {
513 LOG_ERROR <<
"archive error: " << archive_error_string(a);
516 if (size > 0 && buff !=
nullptr) {
517 out_stream.write(buff, static_cast<ssize_t>(size));
522 r = archive_read_free(a);
523 if (r != ARCHIVE_OK) {
524 LOG_ERROR <<
"archive error: " << archive_error_string(a);
528 throw std::runtime_error(
"could not extract " + filename +
" from archive");
531 std::string
result = out_stream.str();
533 boost::trim_if(
result, boost::is_any_of(
" \t\r\n"));
538 static ssize_t write_cb(
struct archive *a,
void *client_data,
const void *buffer,
size_t length) {
539 auto *s = reinterpret_cast<std::ostream *>(client_data);
540 s->write(reinterpret_cast<const char *>(buffer), static_cast<ssize_t>(length));
542 archive_set_error(a, -1,
"unable to write in stream");
546 return static_cast<ssize_t>(length);
549 void Utils::writeArchive(
const std::map<std::string, std::string> &entries, std::ostream &as) {
550 struct archive *a = archive_write_new();
552 LOG_ERROR <<
"archive error: could not initialize archive object";
553 throw std::runtime_error(
"archive error");
555 archive_write_set_format_pax(a);
556 archive_write_add_filter_gzip(a);
558 int r = archive_write_open(a, reinterpret_cast<void *>(&as),
nullptr, write_cb,
nullptr);
559 if (r != ARCHIVE_OK) {
560 LOG_ERROR <<
"archive error: " << archive_error_string(a);
561 archive_write_free(a);
562 throw std::runtime_error(
"archive error");
565 struct archive_entry *entry = archive_entry_new();
566 for (
const auto &el : entries) {
567 archive_entry_clear(entry);
568 archive_entry_set_filetype(entry, AE_IFREG);
569 archive_entry_set_perm(entry, S_IRWXU | S_IRWXG | S_IRWXO);
570 archive_entry_set_size(entry, static_cast<ssize_t>(el.second.size()));
571 archive_entry_set_pathname(entry, el.first.c_str());
572 if (archive_write_header(a, entry) != 0) {
573 LOG_ERROR <<
"archive error: " << archive_error_string(a);
574 archive_entry_free(entry);
575 archive_write_free(a);
576 throw std::runtime_error(
"archive error");
578 if (archive_write_data(a, el.second.c_str(), el.second.size()) < 0) {
579 LOG_ERROR <<
"archive error: " << archive_error_string(a);
580 archive_entry_free(entry);
581 archive_write_free(a);
582 throw std::runtime_error(
"archive error");
585 archive_entry_free(entry);
586 r = archive_write_free(a);
587 if (r != ARCHIVE_OK) {
588 LOG_ERROR <<
"archive error: " << archive_error_string(a);
592 sockaddr_storage Utils::ipGetSockaddr(
int fd) {
593 sockaddr_storage ss{};
594 socklen_t len =
sizeof(ss);
595 if (getsockname(fd, reinterpret_cast<sockaddr *>(&ss), &len) < 0) {
596 throw std::runtime_error(std::string(
"Could not get sockaddr: ") + std::strerror(errno));
602 std::string Utils::ipDisplayName(
const sockaddr_storage &saddr) {
603 std::array<char, INET6_ADDRSTRLEN> ipstr{};
605 switch (saddr.ss_family) {
607 const auto *sa = reinterpret_cast<const sockaddr_in *>(&saddr);
608 inet_ntop(AF_INET, &sa->sin_addr, ipstr.data(), ipstr.size());
609 return std::string(ipstr.data());
612 const auto *sa = reinterpret_cast<const sockaddr_in6 *>(&saddr);
613 inet_ntop(AF_INET6, &sa->sin6_addr, ipstr.data(), ipstr.size());
614 return std::string(ipstr.data());
621 int Utils::ipPort(
const sockaddr_storage &saddr) {
623 if (saddr.ss_family == AF_INET) {
624 const auto *sa = reinterpret_cast<const sockaddr_in *>(&saddr);
626 }
else if (saddr.ss_family == AF_INET6) {
627 const auto *sa = reinterpret_cast<const sockaddr_in6 *>(&saddr);
636 int Utils::shell(
const std::string &command, std::string *output,
bool include_stderr) {
638 std::string full_command(command);
639 if (include_stderr) {
640 full_command +=
" 2>&1";
642 FILE *pipe = popen(full_command.c_str(),
"r");
643 if (pipe ==
nullptr) {
644 *output =
"popen() failed!";
647 while (feof(pipe) == 0) {
648 if (fgets(buffer, 128, pipe) !=
nullptr) {
652 int exitcode = pclose(pipe);
653 return WEXITSTATUS(exitcode);
656 boost::filesystem::path Utils::absolutePath(
const boost::filesystem::path &root,
const boost::filesystem::path &file) {
657 if (file.is_absolute() || root.empty()) {
660 return (root / file);
665 std::vector<boost::filesystem::path> Utils::getDirEntriesByExt(
const boost::filesystem::path &dir_path,
666 const std::string &ext) {
667 std::vector<boost::filesystem::path> entries;
668 boost::filesystem::directory_iterator entryItEnd, entryIt(dir_path);
669 for (; entryIt != entryItEnd; ++entryIt) {
670 const auto &entry_path = entryIt->path();
671 if (!boost::filesystem::is_directory(*entryIt) && entry_path.extension().string() == ext) {
672 entries.push_back(entry_path);
675 std::sort(entries.begin(), entries.end());
679 void Utils::createDirectories(
const boost::filesystem::path &path, mode_t mode) {
680 boost::filesystem::path parent = path.parent_path();
681 if (!parent.empty() && !boost::filesystem::exists(parent)) {
682 Utils::createDirectories(parent, mode);
684 if (mkdir(path.c_str(), mode) == -1) {
685 throw std::runtime_error(std::string(
"could not create directory: ").append(path.native()));
687 std::cout <<
"created: " << path.native() <<
"\n";
690 bool Utils::createSecureDirectory(
const boost::filesystem::path &path) {
691 if (mkdir(path.c_str(), S_IRWXU) == 0) {
698 int ret = stat(path.c_str(), &st);
703 return (ret >= 0 && ((st.st_mode & S_IFDIR) == S_IFDIR) && (st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) == S_IRWXU &&
704 (st.st_uid == getuid()));
707 std::string Utils::urlEncode(
const std::string &input) {
710 for (
char c : input) {
711 if ((c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z') || (c >=
'0' && c <=
'9') || c ==
'-' || c ==
'.' ||
712 c ==
'_' || c ==
'~' || c ==
'/') {
716 auto nib = static_cast<char>(((c >> 4) & 0x0F));
717 res.push_back(static_cast<char>((nib < 10) ? nib +
'0' : nib - 10 +
'A'));
718 nib = static_cast<char>(c & 0x0F);
719 res.push_back(static_cast<char>((nib < 10) ? nib +
'0' : nib - 10 +
'A'));
725 CURL *Utils::curlDupHandleWrapper(CURL *
const curl_in,
const bool using_pkcs11) {
726 CURL *curl = curl_easy_duphandle(curl_in);
733 curlEasySetoptWrapper(curl, CURLOPT_SSLENGINE,
"pkcs11");
744 static boost::filesystem::path &Get() {
752 boost::filesystem::path prefix = Utils::getStorageRootPath();
753 if (prefix.empty()) {
754 prefix = boost::filesystem::temp_directory_path();
756 boost::filesystem::path p = prefix / boost::filesystem::unique_path(
"aktualizr-%%%%-%%%%-%%%%-%%%%");
757 if (mkdir(p.c_str(), S_IRWXU) == -1) {
758 throw std::runtime_error(std::string(
"could not create temporary directory root: ").append(p.native()));
761 path = boost::filesystem::path(p);
765 boost::filesystem::remove_all(path);
771 boost::filesystem::path path;
774 std::string Utils::storage_root_path_;
776 void Utils::setStorageRootPath(
const std::string &storage_root_path) { storage_root_path_ = storage_root_path; }
778 boost::filesystem::path Utils::getStorageRootPath() {
return storage_root_path_; }
780 void Utils::setUserAgent(std::string user_agent) { user_agent_ = std::move(user_agent); }
782 const char *Utils::getUserAgent() {
783 if (user_agent_.empty()) {
784 user_agent_ = (std::string(
"Aktualizr/") + aktualizr_version());
786 return user_agent_.c_str();
789 std::string Utils::user_agent_;
791 void Utils::setCaPath(boost::filesystem::path path) { ca_path_ = std::move(path); }
793 const char *Utils::getCaPath() {
return ca_path_.c_str(); }
795 boost::filesystem::path Utils::ca_path_{
"/etc/ssl/certs"};
797 TemporaryFile::TemporaryFile(
const std::string &hint)
798 : tmp_name_(
SafeTempRoot::Get() / boost::filesystem::unique_path(std::string(
"%%%%-%%%%-").append(hint))) {}
800 TemporaryFile::~TemporaryFile() { boost::filesystem::remove(tmp_name_); }
802 void TemporaryFile::PutContents(
const std::string &contents)
const {
803 mode_t mode = S_IRUSR | S_IWUSR;
804 int fd = open(Path().c_str(), O_WRONLY | O_CREAT | O_TRUNC, mode);
806 throw std::runtime_error(std::string(
"Could not write content to file: ") + Path().
string() +
": " +
807 std::strerror(errno));
809 ssize_t written = write(fd, contents.c_str(), contents.size());
811 if (written < 0 || static_cast<size_t>(written) != contents.size()) {
812 throw std::runtime_error(std::string(
"Could not write content to file: ") + Path().
string());
816 boost::filesystem::path TemporaryFile::Path()
const {
return tmp_name_; }
818 std::string TemporaryFile::PathString()
const {
return Path().string(); }
820 TemporaryDirectory::TemporaryDirectory(
const std::string &hint)
821 : tmp_name_(
SafeTempRoot::Get() / boost::filesystem::unique_path(std::string(
"%%%%-%%%%-").append(hint))) {
822 Utils::createDirectories(tmp_name_, S_IRWXU);
825 TemporaryDirectory::~TemporaryDirectory() { boost::filesystem::remove_all(tmp_name_); }
827 boost::filesystem::path TemporaryDirectory::Path()
const {
return tmp_name_; }
829 boost::filesystem::path TemporaryDirectory::operator/(
const boost::filesystem::path &subdir)
const {
830 return (tmp_name_ / subdir);
833 std::string TemporaryDirectory::PathString()
const {
return Path().string(); }
835 void Utils::setSocketPort(sockaddr_storage *addr, in_port_t port) {
836 if (addr->ss_family == AF_INET) {
837 reinterpret_cast<sockaddr_in *>(addr)->sin_port = port;
838 }
else if (addr->ss_family == AF_INET6) {
839 reinterpret_cast<sockaddr_in6 *>(addr)->sin6_port = port;
843 bool operator<(
const sockaddr_storage &left,
const sockaddr_storage &right) {
844 if (left.ss_family == AF_INET) {
845 throw std::runtime_error(
"IPv4 addresses are not supported");
847 const unsigned char *left_addr = reinterpret_cast<const sockaddr_in6 *>(&left)->sin6_addr.s6_addr;
848 const unsigned char *right_addr = reinterpret_cast<const sockaddr_in6 *>(&right)->sin6_addr.s6_addr;
849 int res = memcmp(left_addr, right_addr, 16);
855 socket_fd_ = socket(AF_INET, SOCK_STREAM, 0);
856 if (-1 == socket_fd_) {
857 throw std::system_error(errno, std::system_category(),
"socket");
861 Socket::~Socket() { ::close(socket_fd_); }
863 std::string Socket::toString()
const {
864 auto saddr = Utils::ipGetSockaddr(socket_fd_);
865 return Utils::ipDisplayName(saddr) +
":" + std::to_string(Utils::ipPort(saddr));
868 void Socket::bind(in_port_t port,
bool reuse)
const {
870 memset(&sa, 0,
sizeof(sa));
871 sa.sin_family = AF_INET;
872 sa.sin_port = htons(port);
873 sa.sin_addr.s_addr = htonl(INADDR_ANY);
875 int reuseaddr = reuse ? 1 : 0;
876 if (-1 == setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
sizeof(reuseaddr))) {
877 throw std::system_error(errno, std::system_category(),
"socket");
880 if (-1 == ::bind(socket_fd_, reinterpret_cast<const sockaddr *>(&sa),
sizeof(sa))) {
881 throw std::system_error(errno, std::system_category(),
"socket");
885 ListenSocket::ListenSocket(in_port_t port) : _port(port) {
889 auto ephemeral_port = Utils::ipPort(Utils::ipGetSockaddr(socket_fd_));
890 if (-1 != ephemeral_port) {
891 _port = static_cast<in_port_t>(ephemeral_port);
896 ConnectionSocket::ConnectionSocket(
const std::string &ip, in_port_t port, in_port_t bind_port)
897 : remote_sock_address_{} {
898 memset(&remote_sock_address_, 0,
sizeof(remote_sock_address_));
899 remote_sock_address_.sin_family = AF_INET;
900 if (-1 == inet_pton(AF_INET, ip.c_str(), &(remote_sock_address_.sin_addr))) {
901 throw std::system_error(errno, std::system_category(),
"socket");
903 remote_sock_address_.sin_port = htons(port);
910 ConnectionSocket::~ConnectionSocket() { ::shutdown(socket_fd_, SHUT_RDWR); }
912 int ConnectionSocket::connect() {
913 return ::connect(socket_fd_, reinterpret_cast<const struct sockaddr *>(&remote_sock_address_),
914 sizeof(remote_sock_address_));
917 CurlEasyWrapper::CurlEasyWrapper() {
918 handle = curl_easy_init();
919 if (handle ==
nullptr) {
920 throw std::runtime_error(
"Could not initialize curl handle");
922 curlEasySetoptWrapper(handle, CURLOPT_USERAGENT, Utils::getUserAgent());
925 CurlEasyWrapper::~CurlEasyWrapper() {
926 if (handle !=
nullptr) {
927 curl_easy_cleanup(handle);