blob: 8da3baf442e46ef7a72b2d8ee82c72eefbb835bd [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sockscripter.h"
#include <arpa/inet.h>
#include <getopt.h>
#include <net/if.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <iomanip>
#include <optional>
#include "addr.h"
#include "log.h"
#include "packet.h"
#include "src/lib/fxl/strings/string_number_conversions.h"
#include "util.h"
#if PACKET_SOCKETS
#include <netpacket/packet.h>
#endif
#define PRINT_SOCK_OPT_VAL(level, name) \
LOG(INFO) << #level "=" << (level) << " " << #name << "=" << name
#define SET_SOCK_OPT_VAL(level, name, val) \
{ \
if (api_->setsockopt(sockfd_, (level), (name), &(val), sizeof(val)) < 0) { \
LOG(ERROR) << "Failed to set socket option " #level ":" #name "-" \
<< "[" << errno << "]" << strerror(errno); \
return false; \
} \
LOG(INFO) << "Set " #level ":" #name " = " << (int)(val); \
return true; \
}
#define LOG_SOCK_OPT_VAL(level, name, type_val) \
{ \
type_val opt = 0; \
socklen_t opt_len = sizeof(opt); \
if (api_->getsockopt(sockfd_, (level), (name), &(opt), &opt_len) < 0) { \
LOG(ERROR) << "Error-Getting " #level ":" #name "-" \
<< "[" << errno << "]" << strerror(errno); \
return false; \
} \
LOG(INFO) << #level ":" #name " is set to " << (int)opt; \
return true; \
}
bool TestRepeatCfg::Parse(const std::string& cmd) {
command = "";
repeat_count = 1;
delay_ms = 0;
if (cmd[0] != '{') {
return false;
}
auto cmd_e = cmd.find_first_of('}');
if (cmd_e == std::string::npos) {
return false;
}
command = cmd.substr(1, cmd_e - 1);
auto n_t_str = cmd.substr(cmd_e + 1);
if (n_t_str.empty()) {
return true;
}
auto n_num_str_i = n_t_str.find("N=");
if (n_num_str_i != std::string::npos) {
auto n_num_str = n_t_str.substr(n_num_str_i + 2);
if (!str2int(n_num_str, &repeat_count)) {
LOG(ERROR) << "Error parsing '" << cmd << "': in N=<n>, <n> is not a number!";
return false;
}
}
auto t_num_str_i = n_t_str.find("T=");
if (t_num_str_i != std::string::npos) {
auto t_num_str = n_t_str.substr(t_num_str_i + 2);
if (!str2int(t_num_str, &delay_ms)) {
LOG(ERROR) << "Error parsing '" << cmd << "': in T=<t>, <t> is not a number!";
}
}
return true;
}
struct SocketType {
const char* name;
int domain;
int type;
std::optional<int> proto;
} socket_types[] = {{"udp", AF_INET, SOCK_DGRAM, IPPROTO_UDP},
{"udp6", AF_INET6, SOCK_DGRAM, IPPROTO_UDP},
{"icmp", AF_INET, SOCK_DGRAM, IPPROTO_ICMP},
{"icmp6", AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6},
{"tcp", AF_INET, SOCK_STREAM, IPPROTO_TCP},
{"tcp6", AF_INET6, SOCK_STREAM, IPPROTO_TCP},
{"raw", AF_INET, SOCK_RAW, std::nullopt},
{"raw6", AF_INET6, SOCK_RAW, std::nullopt},
#if PACKET_SOCKETS
{"packet", AF_PACKET, SOCK_DGRAM, 0},
{"packet-raw", AF_PACKET, SOCK_RAW, 0}
#endif
};
using SockScripterHandler = bool (SockScripter::*)(char*);
const struct Command {
// Command name as used on the commandline.
const char* name;
// Argument description, nullptr if the command doesn't have an argument.
const char* opt_arg_descr;
// String displayed in the help text.
const char* help_str;
SockScripterHandler handler;
} kCommands[] = {
{"close", nullptr, "close socket", &SockScripter::Close},
{"bind", "<bind-ip>:<bind-port>", "bind to the given local address", &SockScripter::Bind},
{"shutdown", "<how>", "shutdown socket for rd and/or wr", &SockScripter::Shutdown},
{"bound", nullptr, "log bound-to-address", &SockScripter::LogBoundToAddress},
{"log-peername", nullptr, "log peer name", &SockScripter::LogPeerAddress},
{"connect", "<connect-ip>:<connect-port>", "connect to the given remote address",
&SockScripter::Connect},
{"disconnect", nullptr, "disconnect a connected socket", &SockScripter::Disconnect},
{"set-broadcast", "{0|1}", "set SO_BROADCAST flag", &SockScripter::SetBroadcast},
{"log-broadcast", nullptr, "log SO_BROADCAST option flag", &SockScripter::LogBroadcast},
{"set-bindtodevice", "<if-name-string>", "set SO_BINDTODEVICE", &SockScripter::SetBindToDevice},
{"log-bindtodevice", nullptr, "log SO_BINDTODEVICE option value",
&SockScripter::LogBindToDevice},
{"set-reuseaddr", "{0|1}", "set SO_REUSEADDR flag", &SockScripter::SetReuseaddr},
{"log-reuseaddr", nullptr, "log SO_REUSEADDR option value", &SockScripter::LogReuseaddr},
{"set-reuseport", "{0|1}", "set SO_REUSEPORT flag", &SockScripter::SetReuseport},
{"log-reuseport", nullptr, "log SO_REUSEPORT option value", &SockScripter::LogReuseport},
{"set-unicast-ttl", "<ttl-for-IP_TTL>", "set TTL for V4 unicast packets",
&SockScripter::SetIpUnicastTTL},
{"log-unicast-ttl", nullptr, "log IP_TTL option value", &SockScripter::LogIpUnicastTTL},
{"set-unicast-hops", "<ttl-for-IPV6_UNICAST_HOPS>", "set hops for V6 unicast packets",
&SockScripter::SetIpUnicastHops},
{"log-unicast-hops", nullptr, "log IPV6_UNICAST_HOPS option value",
&SockScripter::LogIpUnicastHops},
{"set-mcast-ttl", "<ttl-for-IP_MULTICAST-TTL>", "set TTL for V4 mcast packets",
&SockScripter::SetIpMcastTTL},
{"log-mcast-ttl", nullptr, "log IP_MULTICAST_TTL option value", &SockScripter::LogIpMcastTTL},
{"set-mcast-loop4", "{1|0}", "set IP_MULTICAST_LOOP flag", &SockScripter::SetIpMcastLoop4},
{"log-mcast-loop4", nullptr, "log IP_MULTICAST_LOOP option value",
&SockScripter::LogIpMcastLoop4},
{"set-mcast-hops", "<ttl-for-IPV6_MULTICAST_HOPS>", "set hops for V6 mcast packets",
&SockScripter::SetIpMcastHops},
{"log-mcast-hops", nullptr, "log IPV6_MULTICAST_HOPS option value",
&SockScripter::LogIpMcastHops},
{"set-mcast-loop6", "{1|0}", "set IPV6_MULTICAST_LOOP flag", &SockScripter::SetIpMcastLoop6},
{"log-mcast-loop6", nullptr, "log IPV6_MULTICAST_LOOP option value",
&SockScripter::LogIpMcastLoop6},
{"set-mcast-if4", "{<local-intf-Addr>}", "set IP_MULTICAST_IF for IPv4 mcast",
&SockScripter::SetIpMcastIf4},
{"log-mcast-if4", nullptr, "log IP_MULTICAST_IF option value", &SockScripter::LogIpMcastIf4},
{"set-mcast-if6", "<local-intf-id>", "set IP_MULTICAST_IF6 for IPv6 mcast",
&SockScripter::SetIpMcastIf6},
{"log-mcast-if6", nullptr, "log IP_MULTICAST_IF6 option value", &SockScripter::LogIpMcastIf6},
{"set-ipv6-only", "{0|1}", "set IPV6_V6ONLY flag", &SockScripter::SetIpV6Only},
{"log-ipv6-only", nullptr, "log IPV6_V6ONLY option value", &SockScripter::LogIpV6Only},
{"set-ip-recvorigdstaddr", "{0|1}", "set IP_RECVORIGDSTADDR flag",
&SockScripter::SetIpRecvOrigDstAddr},
{"log-ip-recvorigdstaddr", nullptr, "log IP_RECVORIGDSTADDR option value",
&SockScripter::LogIpRecvOrigDstAddr},
{"set-ipv6-recvpktinfo", "{0|1}", "set IPV6_RECVPKTINFO flag",
&SockScripter::SetIpv6RecvPktInfo},
{"log-ipv6-recvpktinfo", nullptr, "log IPV6_RECVPKTINFO option value",
&SockScripter::LogIpv6RecvPktInfo},
{"set-transparent", "{0|1}", "set IP_TRANSPARENT flag", &SockScripter::SetIpTransparent},
{"log-transparent", nullptr, "log IP_TRANSPARENT option value",
&SockScripter::LogIpTransparent},
{"set-ip-hdrincl", "{0|1}", "set IP_HDRINCL flag", &SockScripter::SetIpHeaderInclude},
{"log-ip-hdrincl", nullptr, "log IP_HDRINCL option value", &SockScripter::LogIpHeaderInclude},
{"join4", "<mcast-ip>-<local-intf-Addr>",
"join IPv4 mcast group (IP_ADD_MEMBERSHIP) on local interface", &SockScripter::Join4},
{"drop4", "<mcast-ip>-<local-intf-Addr>",
"drop IPv4 mcast group (IP_DROP_MEMBERSHIP) on local interface", &SockScripter::Drop4},
{"block4", "<mcast-ip>-<source-ip>-<local-intf-ip>",
"block IPv4 packets from source sent to mcast group on local interface (IP_BLOCK_SOURCE)",
&SockScripter::Block4},
{"join6", "<mcast-ip>-<local-intf-id>",
"join IPv6 mcast group (IPV6_ADD_MEMBERSHIP/IPV6_JOIN_GROUP) on local interface",
&SockScripter::Join6},
{"drop6", "<mcast-ip>-<local-intf-id>",
"drop IPv6 mcast group (IPV6_DROP_MEMBERSHIP/IPV6_LEAVE_GROUP) on local interface",
&SockScripter::Drop6},
{"listen", "<backlog>", "listen on TCP socket with accept backlog length",
&SockScripter::Listen},
{"accept", nullptr, "accept on TCP socket", &SockScripter::Accept},
{"close-listener", nullptr, "close listening TCP socket", &SockScripter::CloseListener},
{"sendto", "<send-to-ip>:<send-to-port>", "sendto(send_buf, ip, port)", &SockScripter::SendTo},
{"send", nullptr, "send send_buf on a connected socket", &SockScripter::Send},
{"recvfrom", nullptr, "recvfrom()", &SockScripter::RecvFrom},
{"recvfrom-ping", nullptr, "recvfrom() and ping the packet back to the sender",
&SockScripter::RecvFromPing},
{"recv", nullptr, "recv() on a connected TCP socket", &SockScripter::Recv},
{"recv-ping", nullptr,
"recv() and ping back the packet on a connected TCP "
"socket",
&SockScripter::RecvPing},
{"set-send-buf-hex", "\"xx xx ..\" ", "set send-buffer with hex values",
&SockScripter::SetSendBufHex},
{"set-send-buf-text", "\"<string>\" ", "set send-buffer with text chars",
&SockScripter::SetSendBufText},
{"sleep", "<sleep-secs>", "sleeps", &SockScripter::Sleep},
#if PACKET_SOCKETS
{"packet-bind", "<protocol>:<if-name-string>",
"bind a packet socket to the given protocol and interface", &SockScripter::PacketBind},
{"packet-send-to", "<protocol>:<if-name-string>",
"send send_buf on a packet socket with the given protocol out the given interface.",
&SockScripter::PacketSendTo},
#endif
};
void print_socket_types() {
for (const auto& socket_type : socket_types) {
std::cout << socket_type.name << " ";
}
std::cout << std::endl;
}
bool check_socket_type_has_proto(const char* stype) {
for (const auto& socket_type : socket_types) {
if (strcmp(stype, socket_type.name) == 0) {
return socket_type.proto.has_value();
}
}
return false;
}
int print_commands_list() {
for (const auto& cmd : kCommands) {
std::cout << cmd.name << " ";
}
std::cout << std::endl;
return 0;
}
int check_command_has_args(const char* command) {
for (const auto& cmd : kCommands) {
if (strcmp(command, cmd.name) == 0) {
if (cmd.opt_arg_descr) {
return 0;
}
break;
}
}
return 1;
}
int usage(const char* name) {
std::stringstream socket_types_str;
for (const auto& socket_type : socket_types) {
socket_types_str << " " << socket_type.name << " ";
if (!socket_type.proto.has_value()) {
socket_types_str << "<protocol>";
}
socket_types_str << " : open new " << GetDomainName(socket_type.domain) << " "
<< GetTypeName(socket_type.type);
if (socket_type.proto.has_value()) {
socket_types_str << " " << GetProtoName(socket_type.proto.value());
}
socket_types_str << " socket" << std::endl;
}
std::stringstream cmds_str;
for (const auto& cmd : kCommands) {
cmds_str << " " << cmd.name << " " << (cmd.opt_arg_descr ? cmd.opt_arg_descr : "") << " : "
<< cmd.help_str << "\n";
}
fprintf(stderr,
"\nUsage: %s [-h] [-s] [-p <proto>] [-c] [-a <cmd>] "
" {socket-type} {socket-cmds}\n"
" -h : this help message\n"
" -s : prints available socket-types (for bash completion)\n"
" -p <proto> : returns 0 if the given socket-type has a parameter\n"
" -c : prints available commands (for bash completion)\n"
" -a <cmd> : returns 0 if the given command has a parameter\n"
" -C : continue after socket operation errors\n"
"\n"
" socket-type is one of the following:\n%s\n"
" socket-cmd is one of the following:\n%s\n"
" <local-intf-id> is an integer prefixed by '%%', e.g. '%%1'\n"
" <local-intf-Addr> is of the format [<IP>][<ID>], e.g. '192.168.1.166',"
"'192.168.1.166%%2', '%%2'\n"
" <backlog> is a signed integer, e.g. '100', '0', '-5'\n\n"
" A command can be repeated by wrapping it in the following structure:\n"
" {<cmd>}[N=<n>][T=<t>]\n"
" <n> is the number of repeats (default: n=1)\n"
" <t> is the delay in ms between commands (default: t=1000)\n\n"
" Examples:\n ------\n"
" Joining the multicast group 224.0.0.120 on local-IP "
"192.168.1.99, bind to 224.0.0.120:2000,\n"
" and receive a two packets.\n"
" %s udp join4 224.0.0.120-192.168.1.99 bind 224.0.0.120:2000 "
"{recvfrom}N=2\n"
"\n"
" Send two packets, 50ms apart, to the multicast group 224.0.0.120:2000 "
"from the local interface 192.168.1.166\n"
" %s udp set-mcast-if4 192.168.1.166 {sendto}N=2T=50 224.0.0.120:2000\n"
"\n\n",
name, socket_types_str.str().c_str(), cmds_str.str().c_str(), name, name);
return 99;
}
struct Escaped {
explicit Escaped(const std::string_view contents) : contents(contents) {}
std::string_view contents;
};
std::ostream& operator<<(std::ostream& out, const Escaped& logged) {
for (unsigned char c : logged.contents) {
if (std::isprint(c)) {
out << c;
} else {
out << "\\x" << std::hex << std::setw(2) << std::setfill('0') << static_cast<uint16_t>(c);
}
}
return out;
}
int SockScripter::Execute(int argc, char* const argv[]) {
optind = 1;
int opt;
while ((opt = getopt(argc, argv, "+Chsp:ca:")) != -1) {
switch (opt) {
case 's':
print_socket_types();
return 0;
case 'p':
return check_socket_type_has_proto(optarg) ? 0 : 1;
case 'c':
return print_commands_list();
case 'C':
continue_after_error_ = true;
break;
case 'a':
return check_command_has_args(optarg);
case 'h':
default:
return usage(argv[0]);
}
}
if (optind >= argc) {
return usage(argv[0]);
}
{
bool found = false;
for (const auto& socket_type : socket_types) {
if (strcmp(argv[optind], socket_type.name) == 0) {
found = true;
int proto;
if (socket_type.proto.has_value()) {
proto = socket_type.proto.value();
} else {
optind++;
if (optind >= argc) {
fprintf(stderr, "Error-Need protocol# for RAW socket!\n\n");
return -1;
}
if (!str2int(argv[optind], &proto)) {
fprintf(stderr, "Error-Invalid protocol# (%s) for RAW socket!\n\n", argv[optind]);
return -1;
}
}
if (!Open(socket_type.domain, socket_type.type, proto)) {
return -1;
}
break;
}
}
if (!found) {
fprintf(stderr, "Error-first parameter (%s) needs to be socket type:", argv[optind]);
print_socket_types();
return -1;
}
}
optind++;
while (optind < argc) {
TestRepeatCfg cfg;
const char* cmd_arg;
if (argv[optind][0] == '{') {
if (!cfg.Parse(argv[optind])) {
return -1;
}
cmd_arg = cfg.command.c_str();
} else {
cmd_arg = argv[optind];
}
{
bool found = false;
for (const auto& cmd : kCommands) {
if (strcmp(cmd_arg, cmd.name) == 0) {
found = true;
optind++;
char* arg = nullptr;
if (cmd.opt_arg_descr) {
if (optind < argc) {
arg = argv[optind];
optind++;
} else {
fprintf(stderr, "Missing argument %s for %s!\n\n", cmd.opt_arg_descr, cmd.name);
return -1;
}
}
for (int i = 0; i < cfg.repeat_count; i++) {
auto handler = cmd.handler;
if (!(this->*(handler))(arg) && !continue_after_error_) {
return -1;
}
usleep(1000 * cfg.delay_ms);
}
break;
}
}
if (!found) {
fprintf(stderr, "Error: Cannot find command '%s'!\n\n", argv[optind]);
return -1;
}
}
}
return 0;
}
bool SockScripter::Open(int domain, int type, int proto) {
sockfd_ = api_->socket(domain, type, proto);
if (sockfd_ < 0) {
LOG(ERROR) << "Error-Opening " << GetDomainName(domain) << "-" << GetTypeName(type)
<< " socket "
<< "(proto:" << GetProtoName(proto) << ") "
<< "failed-[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "Opened " << GetDomainName(domain) << "-" << GetTypeName(type) << " socket "
<< "(proto:" << GetProtoName(proto) << ") fd:" << sockfd_;
return true;
}
bool SockScripter::Close(char* arg) {
if (sockfd_ < 0) {
LOG(ERROR) << "No socket to close";
return false;
}
if (auto err = api_->close(sockfd_); err != 0) {
LOG(ERROR) << "Failed to close fd=" << sockfd_ << ": " << std::strerror(err);
return false;
}
LOG(INFO) << "Closed socket-fd:" << sockfd_;
sockfd_ = -1;
if (tcp_listen_socket_fd_ >= 0) {
sockfd_ = tcp_listen_socket_fd_;
tcp_listen_socket_fd_ = -1;
}
return true;
}
bool SockScripter::CloseListener(char* arg) {
if (tcp_listen_socket_fd_ < 0) {
LOG(ERROR) << "No listening TCP socket to close";
return false;
}
if (auto err = api_->close(tcp_listen_socket_fd_); err != 0) {
LOG(ERROR) << "Failed to close fd=" << tcp_listen_socket_fd_ << ": " << std::strerror(err);
return false;
}
LOG(INFO) << "Closed socket-fd:" << tcp_listen_socket_fd_;
tcp_listen_socket_fd_ = -1;
return true;
}
bool SockScripter::SetBroadcast(char* arg) {
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid broadcast flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(SOL_SOCKET, SO_BROADCAST, flag)
}
bool SockScripter::LogBroadcast(char* arg) { LOG_SOCK_OPT_VAL(SOL_SOCKET, SO_BROADCAST, int) }
bool SockScripter::SetReuseaddr(char* arg) {
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid reuseaddr flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(SOL_SOCKET, SO_REUSEADDR, flag)
}
bool SockScripter::SetReuseport(char* arg) {
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid reuseport flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(SOL_SOCKET, SO_REUSEPORT, flag)
}
bool SockScripter::LogReuseaddr(char* arg) { LOG_SOCK_OPT_VAL(SOL_SOCKET, SO_REUSEADDR, int) }
bool SockScripter::LogReuseport(char* arg) { LOG_SOCK_OPT_VAL(SOL_SOCKET, SO_REUSEPORT, int) }
bool SockScripter::SetIpUnicastTTL(char* arg) {
int ttl;
if (!str2int(arg, &ttl) || ttl < 0) {
LOG(ERROR) << "Error: Invalid unicast TTL='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IP, IP_TTL, ttl)
}
bool SockScripter::SetIpUnicastHops(char* arg) {
int hops;
if (!str2int(arg, &hops) || hops < 0) {
LOG(ERROR) << "Error: Invalid unicast hops='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_UNICAST_HOPS, hops)
}
bool SockScripter::LogIpUnicastHops(char* arg) {
LOG_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_UNICAST_HOPS, int)
}
bool SockScripter::SetIpMcastTTL(char* arg) {
int ttl;
if (!str2int(arg, &ttl) || ttl < 0) {
LOG(ERROR) << "Error: Invalid mcast TTL='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IP, IP_MULTICAST_TTL, ttl)
}
bool SockScripter::LogIpMcastTTL(char* arg) { LOG_SOCK_OPT_VAL(IPPROTO_IP, IP_MULTICAST_TTL, int) }
bool SockScripter::LogIpUnicastTTL(char* arg) { LOG_SOCK_OPT_VAL(IPPROTO_IP, IP_TTL, int) }
bool SockScripter::SetIpMcastLoop4(char* arg) {
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid loop4 flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IP, IP_MULTICAST_LOOP, flag)
}
bool SockScripter::LogIpMcastLoop4(char* arg) {
LOG_SOCK_OPT_VAL(IPPROTO_IP, IP_MULTICAST_LOOP, int)
}
bool SockScripter::SetIpMcastHops(char* arg) {
int hops;
if (!str2int(arg, &hops) || hops < 0) {
LOG(ERROR) << "Error: Invalid mcast hops='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_MULTICAST_HOPS, hops)
}
bool SockScripter::LogIpMcastHops(char* arg) {
LOG_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_MULTICAST_HOPS, int)
}
bool SockScripter::SetIpV6Only(char* arg) {
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid ipv6-only flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_V6ONLY, flag)
}
bool SockScripter::LogIpV6Only(char* arg) { LOG_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_V6ONLY, int) }
bool SockScripter::SetIpMcastLoop6(char* arg) {
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid loop6 flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_MULTICAST_LOOP, flag)
}
bool SockScripter::LogIpMcastLoop6(char* arg) {
LOG_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_MULTICAST_LOOP, int)
}
bool SockScripter::SetBindToDevice(char* arg) {
#ifdef SO_BINDTODEVICE
if (api_->setsockopt(sockfd_, SOL_SOCKET, SO_BINDTODEVICE, arg,
static_cast<socklen_t>(strlen(arg))) < 0) {
LOG(ERROR) << "Error-Setting SO_BINDTODEVICE failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "Set SO_BINDTODEVICE to " << arg;
return true;
#else
LOG(ERROR) << "SO_BINDTODEVICE not defined in this platform";
return false;
#endif
}
bool SockScripter::LogBindToDevice(char* arg) {
#ifdef SO_BINDTODEVICE
char name[IFNAMSIZ] = {};
socklen_t name_len = sizeof(name);
if (api_->getsockopt(sockfd_, SOL_SOCKET, SO_BINDTODEVICE, name, &name_len) < 0) {
LOG(ERROR) << "Error-Getting SO_BINDTODEVICE failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "SO_BINDTODEVICE is set to " << name;
return true;
#else
LOG(ERROR) << "SO_BINDTODEVICE not defined in this platform";
return false;
#endif
}
bool SockScripter::SetIpMcastIf4(char* arg) {
auto [if_addr, if_id] = ParseIpv4WithScope(arg);
if (!if_addr.has_value() && !if_id.has_value()) {
LOG(ERROR) << "Error-No IPv4 address or local interface id given for "
<< "IP_MULTICAST_IF";
return false;
}
struct ip_mreqn mreq = {};
// mreq.imr_multiaddr is not set
if (if_addr.has_value()) {
mreq.imr_address = if_addr.value();
}
if (if_id.has_value()) {
mreq.imr_ifindex = if_id.value();
}
if (api_->setsockopt(sockfd_, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) {
LOG(ERROR) << "Error-Setting IP_MULTICAST_IF failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
std::stringstream o;
if (if_addr.has_value()) {
char buf[INET_ADDRSTRLEN] = {};
o << inet_ntop(AF_INET, &if_addr.value(), buf, sizeof(buf));
}
if (if_id.has_value()) {
o << '%' << if_id.value();
}
LOG(INFO) << "Set IP_MULTICAST_IF to " << o.str();
return true;
}
bool SockScripter::LogIpMcastIf4(char* arg) {
struct in_addr addr;
memset(&addr, 0, sizeof(addr));
socklen_t addr_len = sizeof(addr);
if (api_->getsockopt(sockfd_, IPPROTO_IP, IP_MULTICAST_IF, &addr, &addr_len) < 0) {
LOG(ERROR) << "Error-Getting IP_MULTICAST_IF failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
char buf[INET_ADDRSTRLEN] = {};
LOG(INFO) << "IP_MULTICAST_IF is set to " << inet_ntop(AF_INET, &addr, buf, sizeof(buf));
return true;
}
bool SockScripter::SetIpMcastIf6(char* arg) {
int id;
if (!str2int(arg, &id) || id < 0) {
LOG(ERROR) << "Error-Invalid local interface ID given for IPV6_MULTICAST_IF: " << arg;
return false;
}
if (api_->setsockopt(sockfd_, IPPROTO_IPV6, IPV6_MULTICAST_IF, &id, sizeof(id)) < 0) {
LOG(ERROR) << "Error-Setting IPV6_MULTICAST_IF to " << id << " failed-[" << errno << "]"
<< strerror(errno);
return false;
}
LOG(INFO) << "Set IPV6_MULTICAST_IF to " << id;
return true;
}
bool SockScripter::LogIpMcastIf6(char* arg) {
int id = -1;
socklen_t id_len = sizeof(id);
if (api_->getsockopt(sockfd_, IPPROTO_IPV6, IPV6_MULTICAST_IF, &id, &id_len) < 0) {
LOG(ERROR) << "Error-Getting IPV6_MULTICAST_IF failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "IPV6_MULTICAST_IF is set to " << id;
return true;
}
bool SockScripter::LogBoundToAddress(char* arg) {
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
if (api_->getsockname(sockfd_, reinterpret_cast<sockaddr*>(&addr), &addr_len) < 0) {
LOG(ERROR) << "Error-Calling getsockname failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "Bound to " << Format(addr);
return true;
}
bool SockScripter::LogPeerAddress(char* arg) {
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
if (api_->getpeername(sockfd_, reinterpret_cast<sockaddr*>(&addr), &addr_len) < 0) {
LOG(ERROR) << "Error-Calling getpeername failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "Connected to " << Format(addr);
return true;
}
bool SockScripter::SetIpRecvOrigDstAddr(char* arg) {
#ifdef IP_RECVORIGDSTADDR
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid IP_RECVORIGDSTADDR flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IP, IP_RECVORIGDSTADDR, flag)
#else
LOG(ERROR) << "IP_RECVORIGDSTADDR not defined on this platform";
return false;
#endif
}
bool SockScripter::LogIpRecvOrigDstAddr(char* arg) {
#ifdef IP_RECVORIGDSTADDR
LOG_SOCK_OPT_VAL(IPPROTO_IP, IP_RECVORIGDSTADDR, int)
#else
LOG(ERROR) << "IP_RECVORIGDSTADDR not defined on this platform";
return false;
#endif
}
bool SockScripter::SetIpv6RecvPktInfo(char* arg) {
#ifdef IPV6_RECVPKTINFO
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid IPV6_RECVPKTINFO flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_RECVPKTINFO, flag)
#else
LOG(ERROR) << "IPV6_RECVPKTINFO not defined on this platform";
return false;
#endif
}
bool SockScripter::LogIpv6RecvPktInfo(char* arg) {
#ifdef IPV6_RECVPKTINFO
LOG_SOCK_OPT_VAL(IPPROTO_IPV6, IPV6_RECVPKTINFO, int)
#else
LOG(ERROR) << "IPV6_RECVPKTINFO not defined on this platform";
return false;
#endif
}
bool SockScripter::SetIpTransparent(char* arg) {
#ifdef IP_TRANSPARENT
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid IP_TRANSPARENT flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IP, IP_TRANSPARENT, flag)
#else
LOG(ERROR) << "IP_TRANSPARENT not defined on this platform";
return false;
#endif
}
bool SockScripter::LogIpTransparent(char* arg) {
#ifdef IP_TRANSPARENT
LOG_SOCK_OPT_VAL(IPPROTO_IP, IP_TRANSPARENT, int)
#else
LOG(ERROR) << "IP_TRANSPARENT not defined on this platform";
return false;
#endif
}
bool SockScripter::SetIpHeaderInclude(char* arg) {
int flag;
if (!getFlagInt(arg, &flag)) {
LOG(ERROR) << "Error: Invalid IP_HDRINCL flag='" << arg << "'!";
return false;
}
SET_SOCK_OPT_VAL(IPPROTO_IP, IP_HDRINCL, flag)
return true;
}
bool SockScripter::LogIpHeaderInclude(char* arg) {
LOG_SOCK_OPT_VAL(IPPROTO_IP, IP_HDRINCL, int);
return true;
}
bool SockScripter::Bind(char* arg) {
std::optional addr = Parse(arg);
if (!addr.has_value()) {
return false;
}
LOG(INFO) << "Bind(fd:" << sockfd_ << ") to " << Format(addr.value());
socklen_t addr_len = AddrLen(addr.value());
if (api_->bind(sockfd_, reinterpret_cast<sockaddr*>(&addr.value()), addr_len) < 0) {
LOG(ERROR) << "Error-Bind(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
return LogBoundToAddress(nullptr);
}
#if PACKET_SOCKETS
bool SockScripter::PacketSendTo(char* arg) {
std::string argstr = arg;
std::optional<sockaddr_ll> sll = ParseSockAddrLlFromArg(argstr, api_);
if (!sll.has_value()) {
return false;
}
auto snd_buf = snd_buf_gen_.GetSndStr();
LOG(INFO) << "PacketSendTo(data:\"" << Escaped(snd_buf) << "\", len:" << snd_buf.length()
<< ", fd" << sockfd_ << ", protocol:" << ntohs(sll->sll_protocol)
<< ", if_index:" << sll->sll_ifindex << ")";
ssize_t sent = api_->sendto(sockfd_, snd_buf.c_str(), snd_buf.length(), snd_flags_,
reinterpret_cast<const struct sockaddr*>(&sll), sizeof(sll));
if (sent < 0) {
LOG(ERROR) << "Error-PacketSendTo(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "Sent [" << sent << "] on fd:" << sockfd_;
return true;
}
bool SockScripter::PacketBind(char* arg) {
std::string argstr = arg;
std::optional<sockaddr_ll> sll = ParseSockAddrLlFromArg(argstr, api_);
if (!sll.has_value()) {
return false;
}
LOG(INFO) << "PacketBind(fd:" << sockfd_ << ", protocol:" << ntohs(sll->sll_protocol)
<< ", if_index:" << sll->sll_ifindex << ")";
if (api_->bind(sockfd_, reinterpret_cast<const struct sockaddr*>(&sll), sizeof(sll)) < 0) {
LOG(ERROR) << "Error-PacketBind(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
return true;
}
#endif // PACKET_SOCKETS
bool SockScripter::Shutdown(char* arg) {
std::string howStr(arg);
bool read = false;
if (howStr.find("rd") != std::string::npos) {
read = true;
}
bool write = false;
if (howStr.find("wr") != std::string::npos) {
write = true;
}
int how;
if (read && write) {
how = SHUT_RDWR;
howStr = "SHUT_RDWR";
} else if (read) {
how = SHUT_RD;
howStr = "SHUT_RD";
} else if (write) {
how = SHUT_WR;
howStr = "SHUT_WR";
} else {
LOG(ERROR) << "Error-Cannot parse how='" << arg << "'";
return false;
}
LOG(INFO) << "Shutdown(fd:" << sockfd_ << ", " << howStr << ")";
if (api_->shutdown(sockfd_, how) < 0) {
LOG(ERROR) << "Error-Shutdown(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
return LogBoundToAddress(nullptr);
}
bool SockScripter::Connect(char* arg) {
std::optional addr = Parse(arg);
if (!addr.has_value()) {
return false;
}
LOG(INFO) << "Connect(fd:" << sockfd_ << ") to " << Format(addr.value());
socklen_t addr_len = AddrLen(addr.value());
if (api_->connect(sockfd_, reinterpret_cast<sockaddr*>(&addr.value()), addr_len) < 0) {
LOG(ERROR) << "Error-Connect(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
return LogBoundToAddress(nullptr);
}
bool SockScripter::Disconnect(char* arg) {
LOG(INFO) << "Disconnect(fd:" << sockfd_ << ")";
struct sockaddr_storage addr = {};
addr.ss_family = AF_UNSPEC;
if (api_->connect(sockfd_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr.ss_family)) < 0) {
LOG(ERROR) << "Error-Disconnect(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
return LogBoundToAddress(nullptr);
}
bool SockScripter::JoinOrDrop4(const char* func, char* arg, int optname, const char* optname_str) {
if (arg == nullptr) {
LOG(ERROR) << "Called " << func << " with arg == nullptr!";
return false;
}
std::string saved_arg(arg);
char* mcast_ip_str = strtok(arg, "-");
char* ip_id_str = strtok(nullptr, "-");
if (mcast_ip_str == nullptr || ip_id_str == nullptr) {
LOG(ERROR) << "Error-" << func << " got arg='" << saved_arg << "', "
<< "needs to be <mcast-ip>-{<local-intf-ip>|<local-intf-id>}";
return false;
}
std::optional mcast_addr = Parse(mcast_ip_str, std::nullopt);
if (!mcast_addr.has_value() || mcast_addr.value().ss_family != AF_INET) {
LOG(ERROR) << "Error-" << func << " got invalid mcast address='" << mcast_ip_str << "'!";
return false;
}
auto [if_addr, if_id] = ParseIpv4WithScope(ip_id_str);
if (!if_addr.has_value() && !if_id.has_value()) {
LOG(ERROR) << "Error-" << func << " got invalid interface='" << ip_id_str << "'!";
return false;
}
struct ip_mreqn mreq = {
.imr_multiaddr = reinterpret_cast<sockaddr_in*>(&mcast_addr.value())->sin_addr,
};
if (if_addr.has_value()) {
mreq.imr_address = if_addr.value();
}
if (if_id.has_value()) {
mreq.imr_ifindex = if_id.value();
}
if (api_->setsockopt(sockfd_, IPPROTO_IP, optname, &mreq, sizeof(mreq)) < 0) {
LOG(ERROR) << "Error-Setting " << optname_str << " failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
std::stringstream o;
if (if_addr.has_value()) {
char buf[INET_ADDRSTRLEN] = {};
o << inet_ntop(AF_INET, &if_addr.value(), buf, sizeof(buf));
}
if (if_id.has_value()) {
o << '%' << if_id.value();
}
LOG(INFO) << "Set " << optname_str << " for " << Format(mcast_addr.value()) << " on " << o.str()
<< ".";
return true;
}
bool SockScripter::Join4(char* arg) {
return JoinOrDrop4(__FUNCTION__, arg, IP_ADD_MEMBERSHIP, "IP_ADD_MEMBERSHIP");
}
bool SockScripter::Drop4(char* arg) {
return JoinOrDrop4(__FUNCTION__, arg, IP_DROP_MEMBERSHIP, "IP_DROP_MEMBERSHIP");
}
bool SockScripter::Block4(char* arg) {
std::string saved_arg(arg);
char* mcast_ip_str = strtok(arg, "-");
char* source_ip_str = strtok(nullptr, "-");
char* if_ip_str = strtok(nullptr, "-");
if (mcast_ip_str == nullptr || source_ip_str == nullptr || if_ip_str == nullptr) {
LOG(ERROR) << "Error-Block4 got arg='" << saved_arg << "', "
<< "needs to be <mcast-ip>-<source-ip>-<local-intf-id>";
return false;
}
std::optional mcast_addr = Parse(mcast_ip_str, std::nullopt);
if (!mcast_addr.has_value() || mcast_addr.value().ss_family != AF_INET) {
LOG(ERROR) << "Error-Block4 got invalid mcast address='" << mcast_ip_str << "'!";
return false;
}
std::optional source_addr = Parse(source_ip_str, std::nullopt);
if (!source_addr.has_value() || source_addr.value().ss_family != AF_INET) {
LOG(ERROR) << "Error-Block4 got invalid source address='" << source_ip_str << "'!";
return false;
}
std::optional if_addr = Parse(if_ip_str, std::nullopt);
if (!if_addr.has_value() || if_addr.value().ss_family != AF_INET) {
LOG(ERROR) << "Error-Block4 got invalid interface address='" << if_ip_str << "'!";
return false;
}
struct ip_mreq_source mreq;
mreq.imr_multiaddr = reinterpret_cast<sockaddr_in*>(&mcast_addr.value())->sin_addr;
mreq.imr_interface = reinterpret_cast<sockaddr_in*>(&if_addr.value())->sin_addr;
mreq.imr_sourceaddr = reinterpret_cast<sockaddr_in*>(&source_addr.value())->sin_addr;
if (api_->setsockopt(sockfd_, IPPROTO_IP, IP_BLOCK_SOURCE, &mreq, sizeof(mreq)) < 0) {
LOG(ERROR) << "Error-Setting IP_BLOCK_SOURCE failed-[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "Set IP_BLOCK_SOURCE for " << Format(mcast_addr.value()) << " source "
<< Format(source_addr.value()) << " iface " << Format(if_addr.value()) << ".";
return true;
}
bool SockScripter::JoinOrDrop6(const char* func, char* arg, int optname, const char* optname_str) {
if (arg == nullptr) {
LOG(ERROR) << "Called " << func << " with arg == nullptr!";
return false;
}
std::string saved_arg(arg);
char* mcast_ip_str = strtok(arg, "-");
char* id_str = strtok(nullptr, "-");
if (mcast_ip_str == nullptr || id_str == nullptr) {
LOG(ERROR) << "Error-" << func << " got arg='" << saved_arg << "'', "
<< "needs to be '<mcast-ip>-<local-intf-id>'";
return false;
}
std::optional mcast_addr = Parse(mcast_ip_str, std::nullopt);
if (!mcast_addr.has_value() || mcast_addr.value().ss_family != AF_INET6) {
LOG(ERROR) << "Error-" << func << " got invalid mcast address='" << mcast_ip_str << "'!";
return false;
}
int if_id;
if (!str2int(id_str, &if_id) || if_id < 0) {
LOG(ERROR) << "Error-" << func << " got invalid interface='" << id_str << "'!";
return false;
}
struct ipv6_mreq mreq = {
.ipv6mr_multiaddr = reinterpret_cast<sockaddr_in6*>(&mcast_addr.value())->sin6_addr,
.ipv6mr_interface = static_cast<unsigned int>(if_id),
};
if (api_->setsockopt(sockfd_, IPPROTO_IPV6, optname, &mreq, sizeof(mreq)) < 0) {
LOG(ERROR) << "Error-Setting " << optname_str << " failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "Set " << optname_str << " for " << Format(mcast_addr.value()) << " on " << if_id
<< ".";
return true;
}
bool SockScripter::Join6(char* arg) {
// Note that IPV6_JOIN_GROUP and IPV6_ADD_MEMBERSHIP map to the same optname integer code, so we
// can pick either one. We go with IPV6_JOIN_GROUP due to wider support.
return JoinOrDrop6(__FUNCTION__, arg, IPV6_JOIN_GROUP, "IPV6_JOIN_GROUP");
}
bool SockScripter::Drop6(char* arg) {
return JoinOrDrop6(__FUNCTION__, arg, IPV6_LEAVE_GROUP, "IPV6_LEAVE_GROUP");
}
bool SockScripter::Listen(char* arg) {
int backlog = 0;
if (!str2int(arg, &backlog)) {
LOG(ERROR) << "Error-listen got invalid backlog size=" << arg;
return false;
}
if (api_->listen(sockfd_, backlog) < 0) {
LOG(ERROR) << "Error-listen(fd:" << sockfd_ << ", " << backlog << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
return true;
}
bool SockScripter::Accept(char* arg) {
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
LOG(INFO) << "Accepting(fd:" << sockfd_ << ")...";
int fd = api_->accept(sockfd_, reinterpret_cast<sockaddr*>(&addr), &addr_len);
if (fd < 0) {
LOG(ERROR) << "Error-Accept(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << " accepted(fd:" << sockfd_ << ") new connection "
<< "(fd:" << fd << ") from " << Format(addr);
// Switch to the newly created connection, remember the previous one.
tcp_listen_socket_fd_ = sockfd_;
sockfd_ = fd;
return true;
}
bool SockScripter::SendTo(char* arg) {
std::optional addr = Parse(arg);
if (!addr.has_value()) {
return false;
}
auto snd_buf = snd_buf_gen_.GetSndStr();
LOG(INFO) << "Sending [" << snd_buf.length() << "]='" << Escaped(snd_buf) << "' on fd:" << sockfd_
<< " to " << Format(addr.value());
socklen_t addr_len = AddrLen(addr.value());
ssize_t sent = api_->sendto(sockfd_, snd_buf.c_str(), snd_buf.length(), snd_flags_,
reinterpret_cast<sockaddr*>(&addr.value()), addr_len);
if (sent < 0) {
LOG(ERROR) << "Error-sendto(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "Sent [" << sent << "] on fd:" << sockfd_;
return true;
}
bool SockScripter::Send(char* arg) {
auto snd_buf = snd_buf_gen_.GetSndStr();
LOG(INFO) << "Sending [" << snd_buf.length() << "]='" << Escaped(snd_buf)
<< "' on fd:" << sockfd_;
ssize_t sent = api_->send(sockfd_, snd_buf.c_str(), snd_buf.length(), snd_flags_);
if (sent < 0) {
LOG(ERROR) << "Error-send(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
LOG(INFO) << "Sent [" << sent << "] on fd:" << sockfd_;
return true;
}
bool SockScripter::RecvFromInternal(bool ping) {
sockaddr_storage addr;
socklen_t addr_len = sizeof(addr);
LOG(INFO) << "RecvFrom(fd:" << sockfd_ << ")...";
memset(recv_buf_, 0, sizeof(recv_buf_));
ssize_t recvd = api_->recvfrom(sockfd_, recv_buf_, sizeof(recv_buf_) - 1, recv_flags_,
reinterpret_cast<sockaddr*>(&addr), &addr_len);
if (recvd < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
LOG(INFO) << " returned EAGAIN or EWOULDBLOCK!";
} else {
LOG(ERROR) << " Error-recvfrom(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
}
return false;
}
std::string_view received(&recv_buf_[0], recvd);
LOG(INFO) << " received(fd:" << sockfd_ << ") [" << recvd << "]'" << Escaped(received) << "' "
<< "from " << Format(addr);
if (ping) {
if (api_->sendto(sockfd_, recv_buf_, recvd, snd_flags_, reinterpret_cast<sockaddr*>(&addr),
addr_len) < 0) {
LOG(ERROR) << "Error-sendto(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
}
return true;
}
bool SockScripter::RecvFrom(char* arg) { return RecvFromInternal(false /* ping */); }
bool SockScripter::RecvFromPing(char* arg) { return RecvFromInternal(true /* ping */); }
int SockScripter::RecvInternal(bool ping) {
LOG(INFO) << "Recv(fd:" << sockfd_ << ") ...";
LogBoundToAddress(nullptr);
LogPeerAddress(nullptr);
memset(recv_buf_, 0, sizeof(recv_buf_));
ssize_t recvd = api_->recv(sockfd_, recv_buf_, sizeof(recv_buf_) - 1, recv_flags_);
if (recvd < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
LOG(INFO) << " returned EAGAIN or EWOULDBLOCK!";
} else {
LOG(ERROR) << " Error-recv(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
}
return false;
}
LOG(INFO) << " received(fd:" << sockfd_ << ") [" << recvd << "]'" << recv_buf_ << "' ";
if (ping) {
if (api_->send(sockfd_, recv_buf_, recvd, recv_flags_) < 0) {
LOG(ERROR) << "Error-send(fd:" << sockfd_ << ") failed-"
<< "[" << errno << "]" << strerror(errno);
return false;
}
}
return true;
}
bool SockScripter::Recv(char* arg) { return RecvInternal(false /* ping */); }
bool SockScripter::RecvPing(char* arg) { return RecvInternal(true /* ping */); }
bool SockScripter::SetSendBufHex(char* arg) { return snd_buf_gen_.SetSendBufHex(arg); }
bool SockScripter::SetSendBufText(char* arg) { return snd_buf_gen_.SetSendBufText(arg); }
bool SockScripter::Sleep(char* arg) {
int sleeptime;
if (!str2int(arg, &sleeptime) || sleeptime < 0) {
LOG(ERROR) << "Error: Invalid sleeptime='" << arg << "'!";
return false;
}
LOG(INFO) << "Sleep for " << sleeptime << " secs...";
if (sleeptime > 0) {
sleep(sleeptime);
}
LOG(INFO) << "Wake up!";
return true;
}
std::string SendBufferGenerator::GetSndStr() {
switch (mode_) {
case COUNTER_TEXT: {
std::string ret_str = snd_str_;
auto cnt_specifier = ret_str.find("%c");
if (cnt_specifier != std::string::npos) {
ret_str.replace(cnt_specifier, 2, std::to_string(counter_));
}
counter_++;
return ret_str;
}
case STATIC_TEXT:
default:
return snd_str_;
}
}
bool SendBufferGenerator::SetSendBufHex(const char* arg) {
std::string_view str(arg, strlen(arg));
std::stringstream ss;
while (str.length()) {
auto f = str.front();
uint8_t v;
// always allow any number of spaces or commas to happen
if (f == ' ' || f == ',') {
str = str.substr(1);
continue;
} else if (str.length() < 2 ||
!fxl::StringToNumberWithError(str.substr(0, 2), &v, fxl::Base::k16)) {
// trailing character at the end that we don't recognize or failed to parse number
return false;
}
str = str.substr(2);
ss.put(v);
}
snd_str_ = ss.str();
return true;
}
bool SendBufferGenerator::SetSendBufText(const char* arg) {
snd_str_ = arg;
return true;
}