More changes for dll refactor
This commit is contained in:
@@ -1,539 +0,0 @@
|
|||||||
|
|
||||||
#define CHBUF_SIZE (256 * 1024)
|
|
||||||
#define POLLVEC_SIZE (DrivenEngine::MAX_CHAN + 1)
|
|
||||||
|
|
||||||
static MonoClock monoclock;
|
|
||||||
|
|
||||||
namespace util {
|
|
||||||
|
|
||||||
double profiling_clock() { return monoclock.get(); }
|
|
||||||
|
|
||||||
} // namespace util
|
|
||||||
|
|
||||||
static void if_error_print_and_exit(const std::string &str) {
|
|
||||||
if (!str.empty()) {
|
|
||||||
std::cerr << std::endl << "error: " << str << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string_view read_file(const char *fn, char *buf, int bufsize,
|
|
||||||
std::string &err) {
|
|
||||||
FILE *f = fopen(fn, "r");
|
|
||||||
if (f == 0) {
|
|
||||||
err = std::string("cannot read file") + fn;
|
|
||||||
buf[0] = 0;
|
|
||||||
return std::string_view(buf, 0);
|
|
||||||
}
|
|
||||||
int nread = fread(buf, 1, bufsize, f);
|
|
||||||
if (nread < 0) {
|
|
||||||
err = std::string("cannot read file: ") + fn;
|
|
||||||
buf[0] = 0;
|
|
||||||
return std::string_view(buf, 0);
|
|
||||||
}
|
|
||||||
if (nread == bufsize) {
|
|
||||||
err = std::string("file too large: ") + fn;
|
|
||||||
buf[0] = 0;
|
|
||||||
return std::string_view(buf, 0);
|
|
||||||
}
|
|
||||||
err = "";
|
|
||||||
return std::string_view(buf, nread);
|
|
||||||
}
|
|
||||||
class Driver {
|
|
||||||
public:
|
|
||||||
enum ChanState {
|
|
||||||
CHAN_INACTIVE,
|
|
||||||
CHAN_PLAINTEXT,
|
|
||||||
CHAN_SSL_CONNECTING,
|
|
||||||
CHAN_SSL_ACCEPTING,
|
|
||||||
CHAN_SSL_READWRITE,
|
|
||||||
};
|
|
||||||
struct ChanInfo {
|
|
||||||
int chid;
|
|
||||||
SOCKET socket;
|
|
||||||
SSL *ssl;
|
|
||||||
|
|
||||||
ChanState state;
|
|
||||||
int nbytes;
|
|
||||||
const char *bytes;
|
|
||||||
bool ready_now;
|
|
||||||
bool ready_on_pollin;
|
|
||||||
bool ready_on_pollout;
|
|
||||||
bool ready_on_outgoing;
|
|
||||||
int last_write_nbytes;
|
|
||||||
|
|
||||||
bool marked_for_deletion() const { return state == CHAN_INACTIVE; }
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<ChanInfo> chans_;
|
|
||||||
std::map<int, SOCKET> listen_sockets_;
|
|
||||||
bool read_console_recently_;
|
|
||||||
std::unique_ptr<char[]> chbuf_;
|
|
||||||
std::unique_ptr<struct pollfd[]> pollvec_;
|
|
||||||
drv::ReplayRecorder recorder_;
|
|
||||||
|
|
||||||
drvssl::UniqueCTX ssl_server_ctx_;
|
|
||||||
drvssl::UniqueCTX ssl_client_secure_ctx_;
|
|
||||||
drvssl::UniqueCTX ssl_client_insecure_ctx_;
|
|
||||||
|
|
||||||
void handle_listen_ports() {
|
|
||||||
const auto &listenports = recorder_.drv_get_listen_ports();
|
|
||||||
for (int port : listenports) {
|
|
||||||
if (listen_sockets_.find(port) == listen_sockets_.end()) {
|
|
||||||
std::string err;
|
|
||||||
SOCKET sock = listen_on_port(port, err);
|
|
||||||
if_error_print_and_exit(err);
|
|
||||||
assert(sock != INVALID_SOCKET);
|
|
||||||
listen_sockets_[port] = sock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_lua_source() {
|
|
||||||
if (recorder_.drv_get_rescan_lua_source()) {
|
|
||||||
std::string err;
|
|
||||||
std::string_view ctrl =
|
|
||||||
read_file("lua/control.lst", chbuf_.get(), CHBUF_SIZE, err);
|
|
||||||
if_error_print_and_exit(err);
|
|
||||||
std::vector<std::string> names = drv::parse_control_lst(ctrl);
|
|
||||||
recorder_.drv_clear_lua_source();
|
|
||||||
for (const std::string &str : names) {
|
|
||||||
std::string lfn = std::string("lua/") + str;
|
|
||||||
std::string_view data =
|
|
||||||
read_file(lfn.c_str(), chbuf_.get(), CHBUF_SIZE, err);
|
|
||||||
if_error_print_and_exit(err);
|
|
||||||
recorder_.drv_add_lua_source(str, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_channel(ChanInfo &chan, std::string_view err) {
|
|
||||||
// std::cerr << "Closing channel " << chan.chid << std::endl;
|
|
||||||
assert(chan.state != CHAN_INACTIVE);
|
|
||||||
// Close and release the SSL channel.
|
|
||||||
if (chan.ssl != nullptr) {
|
|
||||||
SSL_free(chan.ssl);
|
|
||||||
chan.ssl = nullptr;
|
|
||||||
}
|
|
||||||
// Close and release the socket.
|
|
||||||
assert(chan.socket != INVALID_SOCKET);
|
|
||||||
assert(socket_close(chan.socket) == 0);
|
|
||||||
chan.socket = INVALID_SOCKET;
|
|
||||||
// Close everything else.
|
|
||||||
recorder_.drv_notify_close(chan.chid, err);
|
|
||||||
chan.state = CHAN_INACTIVE;
|
|
||||||
chan.chid = -1;
|
|
||||||
chan.nbytes = 0;
|
|
||||||
chan.bytes = 0;
|
|
||||||
chan.ready_now = false;
|
|
||||||
chan.ready_on_pollin = false;
|
|
||||||
chan.ready_on_pollout = false;
|
|
||||||
chan.ready_on_outgoing = false;
|
|
||||||
chan.last_write_nbytes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_console_output() {
|
|
||||||
while (true) {
|
|
||||||
std::string_view s = recorder_.drv_peek_outgoing(0);
|
|
||||||
if (s.size() == 0) break;
|
|
||||||
int nwrote = console_write(s.data(), s.size());
|
|
||||||
if (nwrote <= 0) break;
|
|
||||||
recorder_.drv_sent_outgoing(0, nwrote);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_console_input() {
|
|
||||||
char buffer[256];
|
|
||||||
read_console_recently_ = false;
|
|
||||||
while (true) {
|
|
||||||
int nread = console_read(buffer, 256);
|
|
||||||
if (nread <= 0) break;
|
|
||||||
read_console_recently_ = true;
|
|
||||||
recorder_.drv_recv_incoming(0, std::string_view(buffer, nread));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void make_channel(SOCKET sock, int chid, SSL_CTX *ctx, ChanState state) {
|
|
||||||
ChanInfo newchan;
|
|
||||||
newchan.chid = chid;
|
|
||||||
newchan.socket = sock;
|
|
||||||
newchan.ssl = SSL_new(ctx);
|
|
||||||
newchan.state = state;
|
|
||||||
newchan.nbytes = 0;
|
|
||||||
newchan.bytes = 0;
|
|
||||||
newchan.ready_now = false;
|
|
||||||
newchan.ready_on_pollin = false;
|
|
||||||
newchan.ready_on_pollout = true;
|
|
||||||
newchan.ready_on_outgoing = false;
|
|
||||||
newchan.last_write_nbytes = 0;
|
|
||||||
SSL_set_fd(newchan.ssl, newchan.socket);
|
|
||||||
// SSL_set_msg_callback(newchan.ssl, SSL_trace);
|
|
||||||
// SSL_set_msg_callback_arg(newchan.ssl, BIO_new_fp(stderr,0));
|
|
||||||
chans_.push_back(newchan);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_new_outgoing_sockets() {
|
|
||||||
const auto &chans = recorder_.drv_get_new_outgoing();
|
|
||||||
for (int chid : chans) {
|
|
||||||
std::string err, cert, host, port;
|
|
||||||
std::string target(recorder_.drv_get_target(chid));
|
|
||||||
drv::split_target(target, cert, host, port);
|
|
||||||
if (cert.empty() || host.empty() || port.empty()) {
|
|
||||||
recorder_.drv_notify_close(
|
|
||||||
chid, std::string("invalid target: ") + target);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
SSL_CTX *ctx = nullptr;
|
|
||||||
if (cert == "cert") {
|
|
||||||
ctx = ssl_client_secure_ctx_.get();
|
|
||||||
} else if (cert == "nocert") {
|
|
||||||
ctx = ssl_client_insecure_ctx_.get();
|
|
||||||
} else {
|
|
||||||
recorder_.drv_notify_close(
|
|
||||||
chid, std::string("invalid cert rule: ") + target);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
SOCKET sock = open_connection(host.c_str(), port.c_str(), err);
|
|
||||||
if (sock == INVALID_SOCKET) {
|
|
||||||
recorder_.drv_notify_close(chid, err);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// std::cerr << "Opening channel " << chid << std::endl;
|
|
||||||
make_channel(sock, chid, ctx, CHAN_SSL_CONNECTING);
|
|
||||||
}
|
|
||||||
if (!chans.empty()) {
|
|
||||||
recorder_.drv_clear_new_outgoing();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void accept_connection(int port, SOCKET sock) {
|
|
||||||
std::string err;
|
|
||||||
SOCKET socket = accept_on_socket(sock, err);
|
|
||||||
if_error_print_and_exit(err);
|
|
||||||
if (socket != INVALID_SOCKET) {
|
|
||||||
int chid = recorder_.drv_notify_accept(port);
|
|
||||||
// std::cerr << "Accepted channel " << chid << std::endl;
|
|
||||||
make_channel(socket, chid, ssl_server_ctx_.get(),
|
|
||||||
CHAN_SSL_ACCEPTING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void advance_plaintext(ChanInfo &chan) {
|
|
||||||
std::string err;
|
|
||||||
|
|
||||||
// Try to write plaintext to the channel.
|
|
||||||
std::string_view s = recorder_.drv_peek_outgoing(chan.chid);
|
|
||||||
if (s.size() > 0) {
|
|
||||||
int sbytes = s.size();
|
|
||||||
if (sbytes > 65536) sbytes = 65536;
|
|
||||||
int wbytes = socket_send(chan.socket, s.data(), sbytes, err);
|
|
||||||
if (wbytes < 0) {
|
|
||||||
close_channel(chan, err);
|
|
||||||
} else {
|
|
||||||
recorder_.drv_sent_outgoing(chan.chid, wbytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to read plaintext from the channel.
|
|
||||||
// Someday, find a way to avoid this copy.
|
|
||||||
int nrecv = socket_recv(chan.socket, chbuf_.get(), 65536, err);
|
|
||||||
if (nrecv < 0) {
|
|
||||||
close_channel(chan, err);
|
|
||||||
} else {
|
|
||||||
recorder_.drv_recv_incoming(chan.chid,
|
|
||||||
std::string_view(chbuf_.get(), nrecv));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the ready-flags for next time.
|
|
||||||
chan.ready_on_outgoing = true;
|
|
||||||
chan.ready_on_pollin = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_ssl_error(ChanInfo &chan, int retval) {
|
|
||||||
int error = SSL_get_error(chan.ssl, retval);
|
|
||||||
// std::cerr << "SSL error code = " << error << " ";
|
|
||||||
if (error == SSL_ERROR_WANT_READ) {
|
|
||||||
chan.ready_on_pollin = true;
|
|
||||||
} else if (error == SSL_ERROR_WANT_WRITE) {
|
|
||||||
chan.ready_on_pollout = true;
|
|
||||||
} else {
|
|
||||||
std::string error = drvssl::error_string();
|
|
||||||
if (error == "") error = "unknown error";
|
|
||||||
close_channel(chan, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void advance_ssl_connecting(ChanInfo &chan) {
|
|
||||||
// std::cerr << "In advance_ssl_connecting" << std::endl;
|
|
||||||
int retval = SSL_connect(chan.ssl);
|
|
||||||
if (retval == 1) {
|
|
||||||
// Connection successful.
|
|
||||||
chan.state = CHAN_SSL_READWRITE;
|
|
||||||
chan.ready_now = true;
|
|
||||||
} else {
|
|
||||||
// std::cerr << "ssl_connect_error";
|
|
||||||
process_ssl_error(chan, retval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void advance_ssl_accepting(ChanInfo &chan) {
|
|
||||||
// std::cerr << "In advance_ssl_accepting" << std::endl;
|
|
||||||
int retval = SSL_accept(chan.ssl);
|
|
||||||
if (retval == 1) {
|
|
||||||
// Connection successful.
|
|
||||||
chan.state = CHAN_SSL_READWRITE;
|
|
||||||
chan.ready_now = true;
|
|
||||||
} else {
|
|
||||||
process_ssl_error(chan, retval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void advance_ssl_readwrite(ChanInfo &chan) {
|
|
||||||
// std::cerr << "In advance_ssl_readwrite" << std::endl;
|
|
||||||
// Try to read data.
|
|
||||||
int read_result = SSL_read(chan.ssl, chbuf_.get(), 65536);
|
|
||||||
if (read_result > 0) {
|
|
||||||
recorder_.drv_recv_incoming(
|
|
||||||
chan.chid, std::string_view(chbuf_.get(), read_result));
|
|
||||||
chan.ready_now = true;
|
|
||||||
} else {
|
|
||||||
process_ssl_error(chan, read_result);
|
|
||||||
if (chan.state == CHAN_INACTIVE) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to write data.
|
|
||||||
int wbytes;
|
|
||||||
if (chan.last_write_nbytes > 0) {
|
|
||||||
wbytes = chan.last_write_nbytes;
|
|
||||||
assert(wbytes < chan.nbytes);
|
|
||||||
} else {
|
|
||||||
wbytes = chan.nbytes;
|
|
||||||
if (wbytes > 65536) wbytes = 65536;
|
|
||||||
}
|
|
||||||
if (wbytes > 0) {
|
|
||||||
int write_result = SSL_write(chan.ssl, chan.bytes, wbytes);
|
|
||||||
if (write_result > 0) {
|
|
||||||
recorder_.drv_sent_outgoing(chan.chid, write_result);
|
|
||||||
chan.last_write_nbytes = 0;
|
|
||||||
chan.ready_on_outgoing = true;
|
|
||||||
} else {
|
|
||||||
chan.last_write_nbytes = wbytes;
|
|
||||||
process_ssl_error(chan, write_result);
|
|
||||||
if (chan.state == CHAN_INACTIVE) return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
chan.ready_on_outgoing = true;
|
|
||||||
}
|
|
||||||
// std::cerr << "rpi=" << chan.ready_on_pollin << ".rpo=" <<
|
|
||||||
// chan.ready_on_pollout << ".rn=" << chan.ready_now << ".rog=" <<
|
|
||||||
// chan.ready_on_outgoing << " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
void advance_channel(ChanInfo &chan) {
|
|
||||||
drvssl::clear_all_errors();
|
|
||||||
switch (chan.state) {
|
|
||||||
case CHAN_PLAINTEXT:
|
|
||||||
advance_plaintext(chan);
|
|
||||||
break;
|
|
||||||
case CHAN_SSL_CONNECTING:
|
|
||||||
advance_ssl_connecting(chan);
|
|
||||||
break;
|
|
||||||
case CHAN_SSL_ACCEPTING:
|
|
||||||
advance_ssl_accepting(chan);
|
|
||||||
break;
|
|
||||||
case CHAN_SSL_READWRITE:
|
|
||||||
advance_ssl_readwrite(chan);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_socket_input_output() {
|
|
||||||
std::string err;
|
|
||||||
int mstimeout = read_console_recently_ ? 100 : 1000;
|
|
||||||
|
|
||||||
// Peek output buffers and determine channel release flags.
|
|
||||||
bool any_released = false;
|
|
||||||
for (ChanInfo &chan : chans_) {
|
|
||||||
std::string_view s = recorder_.drv_peek_outgoing(chan.chid);
|
|
||||||
chan.nbytes = s.size();
|
|
||||||
chan.bytes = s.data();
|
|
||||||
if (chan.nbytes == 0) {
|
|
||||||
if (recorder_.drv_get_channel_released(chan.chid)) {
|
|
||||||
close_channel(chan, "");
|
|
||||||
any_released = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete any released channels
|
|
||||||
if (any_released) {
|
|
||||||
util::remove_marked_items(chans_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the struct pollfd vector.
|
|
||||||
int pollsize = 0;
|
|
||||||
for (const auto &p : listen_sockets_) {
|
|
||||||
struct pollfd &pfd = pollvec_[pollsize++];
|
|
||||||
pfd.fd = p.second;
|
|
||||||
pfd.events = POLLIN;
|
|
||||||
pfd.revents = 0;
|
|
||||||
}
|
|
||||||
for (const ChanInfo &chan : chans_) {
|
|
||||||
struct pollfd &pfd = pollvec_[pollsize++];
|
|
||||||
assert(chan.socket != INVALID_SOCKET);
|
|
||||||
pfd.fd = chan.socket;
|
|
||||||
pfd.events = 0;
|
|
||||||
pfd.revents = 0;
|
|
||||||
if (chan.ready_now) mstimeout = 0;
|
|
||||||
if (chan.ready_on_pollin) pfd.events |= POLLIN;
|
|
||||||
if (chan.ready_on_pollout) pfd.events |= POLLOUT;
|
|
||||||
if (chan.ready_on_outgoing && (chan.nbytes > 0))
|
|
||||||
pfd.events |= POLLOUT;
|
|
||||||
// std::cerr << "evt=" << pfd.events << ".nb=" << chan.nbytes <<
|
|
||||||
// std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the poll.
|
|
||||||
socket_poll(pollvec_.get(), pollsize, mstimeout, err);
|
|
||||||
if_error_print_and_exit(err);
|
|
||||||
|
|
||||||
// Check listening sockets.
|
|
||||||
int index = 0;
|
|
||||||
for (auto &p : listen_sockets_) {
|
|
||||||
struct pollfd &pfd = pollvec_[index++];
|
|
||||||
if (pfd.revents & (POLLIN | POLLERR)) {
|
|
||||||
accept_connection(p.first, p.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance channels where possible.
|
|
||||||
for (ChanInfo &chan : chans_) {
|
|
||||||
struct pollfd &pfd = pollvec_[index++];
|
|
||||||
bool pollin = ((pfd.revents & POLLIN) != 0);
|
|
||||||
bool pollout = ((pfd.revents & POLLOUT) != 0);
|
|
||||||
bool pollerr = ((pfd.revents & (POLLERR | POLLHUP)) != 0);
|
|
||||||
if (chan.ready_now || pollerr ||
|
|
||||||
(chan.ready_on_pollin && pollin) ||
|
|
||||||
(chan.ready_on_pollout && pollout) ||
|
|
||||||
(chan.ready_on_outgoing && (chan.nbytes > 0) && pollout)) {
|
|
||||||
chan.ready_now = false;
|
|
||||||
chan.ready_on_pollin = false;
|
|
||||||
chan.ready_on_pollout = false;
|
|
||||||
chan.ready_on_outgoing = false;
|
|
||||||
advance_channel(chan);
|
|
||||||
}
|
|
||||||
chan.nbytes = 0;
|
|
||||||
chan.bytes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete any newly-inactive channels
|
|
||||||
util::remove_marked_items(chans_);
|
|
||||||
}
|
|
||||||
|
|
||||||
int replay_logfile(const char *fn, bool verbose) {
|
|
||||||
drv::ReplayPlayer player;
|
|
||||||
player.open_logfile(fn);
|
|
||||||
if (verbose) {
|
|
||||||
player.enable_stdout();
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
drv::ReplayPlayer::Status st = player.step();
|
|
||||||
if (st != drv::ReplayPlayer::ST_REPLAYING) {
|
|
||||||
player.print_status(std::cerr);
|
|
||||||
return (st == drv::ReplayPlayer::ST_CLEAN_EXIT) ? 0 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int drive(int argc, char *argv[]) {
|
|
||||||
// Remove the program name from argv.
|
|
||||||
if (argc < 1) {
|
|
||||||
DrivenEngine::print_usage(std::cerr, "<unknown>");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
std::string program = argv[0];
|
|
||||||
argc -= 1;
|
|
||||||
argv += 1;
|
|
||||||
|
|
||||||
// If argv contains "replay <filename>", do a replay,
|
|
||||||
// and then skip everything else.
|
|
||||||
if (argc >= 1) {
|
|
||||||
std::string cmd(argv[0]);
|
|
||||||
if ((cmd == "replay") || (cmd == "vreplay")) {
|
|
||||||
if (argc != 2) {
|
|
||||||
std::cerr << "usage: " << program << " replay <filename>"
|
|
||||||
<< std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return replay_logfile(argv[1], cmd == "vreplay");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If argv contains "record <filename>", start recording,
|
|
||||||
// and remove the "record <filename>" from argv.
|
|
||||||
if (argc >= 1) {
|
|
||||||
std::string cmd = argv[0];
|
|
||||||
if (cmd == "record") {
|
|
||||||
if (argc < 2) {
|
|
||||||
DrivenEngine::print_usage(std::cerr, program);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
bool ok = recorder_.open_logfile(argv[1]);
|
|
||||||
if (!ok) {
|
|
||||||
std::cerr << "Could not open logfile: " << argv[1]
|
|
||||||
<< std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
argc -= 2;
|
|
||||||
argv += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the engine.
|
|
||||||
if (argc < 1) {
|
|
||||||
DrivenEngine::print_usage(std::cerr, program);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
bool engine_made = recorder_.create_engine(argv[0]);
|
|
||||||
if (!engine_made) {
|
|
||||||
DrivenEngine::print_usage(std::cerr, program);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_console_recently_ = false;
|
|
||||||
|
|
||||||
chbuf_.reset(new char[CHBUF_SIZE]);
|
|
||||||
pollvec_.reset(new struct pollfd[POLLVEC_SIZE]);
|
|
||||||
|
|
||||||
ssl_server_ctx_.reset(drvssl::new_context(SSL_VERIFY_NONE));
|
|
||||||
ssl_client_secure_ctx_.reset(drvssl::new_context(SSL_VERIFY_PEER));
|
|
||||||
ssl_client_insecure_ctx_.reset(drvssl::new_context(SSL_VERIFY_NONE));
|
|
||||||
ssl_load_certificate_authorities(ssl_client_secure_ctx_.get());
|
|
||||||
drvssl::ctx_load_dummy_cert(ssl_server_ctx_.get());
|
|
||||||
|
|
||||||
handle_lua_source();
|
|
||||||
recorder_.drv_invoke_event_init(argc, argv);
|
|
||||||
handle_listen_ports();
|
|
||||||
|
|
||||||
while (!recorder_.drv_get_stop_driver()) {
|
|
||||||
handle_lua_source();
|
|
||||||
handle_console_output();
|
|
||||||
handle_new_outgoing_sockets();
|
|
||||||
handle_socket_input_output();
|
|
||||||
handle_console_input();
|
|
||||||
handle_console_output();
|
|
||||||
recorder_.drv_invoke_event_update(monoclock.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ChanInfo &chan : chans_) {
|
|
||||||
close_channel(chan, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
DrivenEngine::set(nullptr);
|
|
||||||
recorder_.clean_exit();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
#include "wrap-map.hpp"
|
|
||||||
#include "wrap-vector.hpp"
|
|
||||||
#include "wrap-string.hpp"
|
|
||||||
|
|
||||||
#include "driver-util.hpp"
|
|
||||||
#include "driver-ssl.hpp"
|
|
||||||
#include "drivenengine.hpp"
|
|
||||||
#include "util.hpp"
|
|
||||||
#include "source.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include <poll.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <termios.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/personality.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <malloc.h>
|
|
||||||
|
|
||||||
using SOCKET=int;
|
|
||||||
const int INVALID_SOCKET = -1;
|
|
||||||
|
|
||||||
struct termios orig_termios;
|
|
||||||
|
|
||||||
std::string strerror_str(int err) {
|
|
||||||
char errbuf[256];
|
|
||||||
return strerror_r(err, errbuf, 256);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_nonblocking(int fd) {
|
|
||||||
int flags = fcntl(fd, F_GETFL, 0);
|
|
||||||
assert(flags != -1);
|
|
||||||
int status = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
||||||
assert(status != -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void disable_tty_raw() {
|
|
||||||
tcsetattr(0, TCSAFLUSH, &orig_termios);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void enable_tty_raw() {
|
|
||||||
int status = tcgetattr(0, &orig_termios);
|
|
||||||
assert(status >= 0);
|
|
||||||
atexit(disable_tty_raw);
|
|
||||||
struct termios raw = orig_termios;
|
|
||||||
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
|
||||||
raw.c_lflag &= ~(ECHO | ICANON);
|
|
||||||
raw.c_oflag |= OPOST;
|
|
||||||
raw.c_cc[VMIN] = 0;
|
|
||||||
raw.c_cc[VTIME] = 0;
|
|
||||||
status = tcsetattr(0, TCSAFLUSH, &raw);
|
|
||||||
assert(status >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SOCKET open_connection(const char *host, const char *port, std::string &err) {
|
|
||||||
struct addrinfo *addrs = nullptr;
|
|
||||||
struct addrinfo *goodaddr = nullptr;
|
|
||||||
struct addrinfo hints;
|
|
||||||
SOCKET sock = INVALID_SOCKET;
|
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
|
||||||
hints.ai_family = AF_INET;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
hints.ai_protocol = IPPROTO_TCP;
|
|
||||||
hints.ai_flags = AI_NUMERICSERV;
|
|
||||||
|
|
||||||
err.clear();
|
|
||||||
int status = getaddrinfo(host, port, &hints, &addrs);
|
|
||||||
if (status != 0) {
|
|
||||||
err = gai_strerror(status);
|
|
||||||
goto error_general;
|
|
||||||
}
|
|
||||||
if (addrs == nullptr) {
|
|
||||||
err = "no such host found";
|
|
||||||
goto error_general;
|
|
||||||
}
|
|
||||||
goodaddr = addrs;
|
|
||||||
assert(goodaddr->ai_family == AF_INET);
|
|
||||||
assert(goodaddr->ai_socktype == SOCK_STREAM);
|
|
||||||
assert(goodaddr->ai_protocol == IPPROTO_TCP);
|
|
||||||
sock = socket(goodaddr->ai_family, goodaddr->ai_socktype, goodaddr->ai_protocol);
|
|
||||||
if (sock <= 0) goto error_errno;
|
|
||||||
|
|
||||||
set_nonblocking(sock);
|
|
||||||
|
|
||||||
status = connect(sock, goodaddr->ai_addr, goodaddr->ai_addrlen);
|
|
||||||
if ((status != 0) && (errno != EINPROGRESS)) goto error_errno;
|
|
||||||
|
|
||||||
freeaddrinfo(addrs);
|
|
||||||
return sock;
|
|
||||||
|
|
||||||
error_errno:
|
|
||||||
err = strerror_str(errno);
|
|
||||||
error_general:
|
|
||||||
if (sock != INVALID_SOCKET) close(sock);
|
|
||||||
if (addrs != nullptr) freeaddrinfo(addrs);
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SOCKET listen_on_port(int port, std::string &err) {
|
|
||||||
int status, enable;
|
|
||||||
err.clear();
|
|
||||||
|
|
||||||
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (sock <= 0) goto error_errno;
|
|
||||||
|
|
||||||
enable = 1;
|
|
||||||
status = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
|
||||||
if (status != 0) goto error_errno;
|
|
||||||
|
|
||||||
struct sockaddr_in server;
|
|
||||||
server.sin_family = AF_INET;
|
|
||||||
server.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
server.sin_port = htons(port);
|
|
||||||
|
|
||||||
status = bind(sock, (struct sockaddr *)&server, sizeof(server));
|
|
||||||
if (status != 0) goto error_errno;
|
|
||||||
|
|
||||||
status = listen(sock, 10);
|
|
||||||
if (status != 0) goto error_errno;
|
|
||||||
|
|
||||||
set_nonblocking(sock);
|
|
||||||
return sock;
|
|
||||||
|
|
||||||
error_errno:
|
|
||||||
err = strerror_str(errno);
|
|
||||||
if (sock >= 0) close(sock);
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SOCKET accept_on_socket(SOCKET listen_socket, std::string &err) {
|
|
||||||
err.clear();
|
|
||||||
SOCKET chsock = accept(listen_socket, nullptr, nullptr);
|
|
||||||
if (chsock >= 0) {
|
|
||||||
set_nonblocking(chsock);
|
|
||||||
return chsock;
|
|
||||||
} else {
|
|
||||||
if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != ECONNABORTED)) {
|
|
||||||
err = strerror_str(errno);
|
|
||||||
}
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the return values for socket_send and socket_recv are:
|
|
||||||
//
|
|
||||||
// positive: sent or received bytes successfully
|
|
||||||
// zero: would block
|
|
||||||
// negative: channel closed, possibly cleanly or possibly with error
|
|
||||||
//
|
|
||||||
static int socket_send(SOCKET socket, const char *bytes, int nbytes, std::string &err) {
|
|
||||||
err.clear();
|
|
||||||
int wbytes = send(socket, bytes, nbytes, 0);
|
|
||||||
if (wbytes < 0) {
|
|
||||||
if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
err = strerror_str(errno);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return wbytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int socket_recv(SOCKET socket, char *bytes, int nbytes, std::string &err) {
|
|
||||||
err.clear();
|
|
||||||
int nrecv = recv(socket, bytes, nbytes, 0);
|
|
||||||
if (nrecv < 0) {
|
|
||||||
if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) {
|
|
||||||
err = strerror_str(errno);
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} else if (nrecv == 0) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return nrecv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int socket_close(SOCKET socket) {
|
|
||||||
return close(socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int socket_poll(struct pollfd *pollvec, int pollcount, int mstimeout, std::string &err) {
|
|
||||||
// socket_poll is implicitly expected to also poll stdin,
|
|
||||||
// if the OS allows that. Linux does, so we add stdin to the
|
|
||||||
// poll vector. The poll vector is required to have at
|
|
||||||
// least one free space in order to do this.
|
|
||||||
pollvec[pollcount].fd = 0;
|
|
||||||
pollvec[pollcount].events = POLLIN;
|
|
||||||
pollcount += 1;
|
|
||||||
|
|
||||||
// Do the poll.
|
|
||||||
int status = poll(pollvec, pollcount, mstimeout);
|
|
||||||
if (status < 0) {
|
|
||||||
err = strerror_str(errno);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int console_write(const char *bytes, int nbytes) {
|
|
||||||
return write(1, bytes, nbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int console_read(char *bytes, int nbytes) {
|
|
||||||
return read(0, bytes, nbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ssl_load_certificate_authorities(SSL_CTX *ctx) {
|
|
||||||
assert(SSL_CTX_set_default_verify_paths(ctx) == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void disable_randomization(int argc, char *argv[]) {
|
|
||||||
const int old_personality = personality(ADDR_NO_RANDOMIZE);
|
|
||||||
if (!(old_personality & ADDR_NO_RANDOMIZE)) {
|
|
||||||
const int new_personality = personality(ADDR_NO_RANDOMIZE);
|
|
||||||
if (new_personality & ADDR_NO_RANDOMIZE) {
|
|
||||||
execv(argv[0], argv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class MonoClock {
|
|
||||||
private:
|
|
||||||
struct timespec base_;
|
|
||||||
public:
|
|
||||||
MonoClock() {
|
|
||||||
int status = clock_gettime(CLOCK_MONOTONIC, &base_);
|
|
||||||
assert(status == 0);
|
|
||||||
}
|
|
||||||
double get() {
|
|
||||||
struct timespec t;
|
|
||||||
int status = clock_gettime(CLOCK_MONOTONIC, &t);
|
|
||||||
assert(status == 0);
|
|
||||||
double tv_sec = t.tv_sec - base_.tv_sec;
|
|
||||||
double tv_nsec = t.tv_nsec - base_.tv_nsec;
|
|
||||||
return tv_sec + (tv_nsec * 1.0E-9);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "driver-common.cpp"
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
disable_randomization(argc, argv);
|
|
||||||
enable_tty_raw();
|
|
||||||
assert(OPENSSL_init_ssl(0, NULL) == 1);
|
|
||||||
drvssl::clear_all_errors();
|
|
||||||
SourceDB::register_lua_builtins();
|
|
||||||
Driver driver;
|
|
||||||
return driver.drive(argc, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,295 +0,0 @@
|
|||||||
#define WINVER 0x0600
|
|
||||||
#define _WIN32_WINNT 0x0600
|
|
||||||
|
|
||||||
#include "wrap-map.hpp"
|
|
||||||
#include "wrap-string.hpp"
|
|
||||||
#include "wrap-vector.hpp"
|
|
||||||
|
|
||||||
#include "driver-util.hpp"
|
|
||||||
#include "driver-ssl.hpp"
|
|
||||||
#include "drivenengine.hpp"
|
|
||||||
#include "util.hpp"
|
|
||||||
#include "source.hpp"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#include <synchapi.h>
|
|
||||||
#include <sysinfoapi.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#include <openssl/rsa.h>
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <openssl/err.h>
|
|
||||||
#include <openssl/bio.h>
|
|
||||||
#include <openssl/pem.h>
|
|
||||||
|
|
||||||
static void set_nonblocking(SOCKET sock) {
|
|
||||||
u_long mode = 1; // 1 to enable non-blocking socket
|
|
||||||
int status = ioctlsocket(sock, FIONBIO, &mode);
|
|
||||||
assert(status == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string strerror_str(int errcode) {
|
|
||||||
std::ostringstream oss;
|
|
||||||
oss << "error " << errcode;
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
static PADDRINFOA find_good_addr(PADDRINFOA addrinfo) {
|
|
||||||
for (PADDRINFOA addr = addrinfo; addr != nullptr; addr = addr->ai_next) {
|
|
||||||
if (addr->ai_family == AF_INET) {
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SOCKET open_connection(const char *host, const char *port, std::string &err) {
|
|
||||||
PADDRINFOA addrs = nullptr;
|
|
||||||
PADDRINFOA goodaddr = nullptr;
|
|
||||||
SOCKET sock = INVALID_SOCKET;
|
|
||||||
|
|
||||||
err.clear();
|
|
||||||
int status = getaddrinfo(host, port, nullptr, &addrs);
|
|
||||||
while (status == WSATRY_AGAIN) {
|
|
||||||
status = getaddrinfo(host, port, nullptr, &addrs);
|
|
||||||
}
|
|
||||||
if (status == WSAHOST_NOT_FOUND) {
|
|
||||||
err = "host not found";
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (status != 0) {
|
|
||||||
err = "DNS resolution malfunction";
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
goodaddr = find_good_addr(addrs);
|
|
||||||
if (goodaddr == nullptr) {
|
|
||||||
err = "host not an internet host";
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
sock = socket(goodaddr->ai_family, SOCK_STREAM, IPPROTO_TCP);
|
|
||||||
if (sock == INVALID_SOCKET) {
|
|
||||||
err = "could not create a socket";
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
set_nonblocking(sock);
|
|
||||||
status = connect(sock, goodaddr->ai_addr, goodaddr->ai_addrlen);
|
|
||||||
if (status != 0) {
|
|
||||||
int errcode = WSAGetLastError();
|
|
||||||
if (errcode != WSAEWOULDBLOCK) {
|
|
||||||
err = "connect failure";
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
freeaddrinfo(addrs);
|
|
||||||
return sock;
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (sock != INVALID_SOCKET) closesocket(sock);
|
|
||||||
if (addrs != nullptr) freeaddrinfo(addrs);
|
|
||||||
return SOCKET_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
SOCKET listen_on_port(int port, std::string &err) {
|
|
||||||
int status;
|
|
||||||
err.clear();
|
|
||||||
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (sock == INVALID_SOCKET) {
|
|
||||||
err = "could not create a socket";
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in server;
|
|
||||||
server.sin_family = AF_INET;
|
|
||||||
server.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
server.sin_port = htons(port);
|
|
||||||
|
|
||||||
status = bind(sock, (struct sockaddr *)&server, sizeof(server));
|
|
||||||
if (status < 0) {
|
|
||||||
err = "could not bind port";
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
status = listen(sock, 10);
|
|
||||||
if (status < 0) {
|
|
||||||
err = "could not listen on socket";
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
set_nonblocking(sock);
|
|
||||||
std::cerr << "listening socket is " << sock << std::endl;
|
|
||||||
return sock;
|
|
||||||
|
|
||||||
error:
|
|
||||||
if (sock != INVALID_SOCKET) closesocket(sock);
|
|
||||||
return SOCKET_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SOCKET accept_on_socket(SOCKET listen_socket, std::string &err) {
|
|
||||||
SOCKET chsock = accept(listen_socket, nullptr, nullptr);
|
|
||||||
if (chsock != INVALID_SOCKET) {
|
|
||||||
set_nonblocking(chsock);
|
|
||||||
return chsock;
|
|
||||||
} else {
|
|
||||||
int errcode = WSAGetLastError();
|
|
||||||
if ((errcode == WSAEWOULDBLOCK) || (errcode == WSAECONNRESET)) {
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
} else {
|
|
||||||
err = "accept failed";
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int socket_send(SOCKET socket, const char *bytes, int nbytes, std::string &err) {
|
|
||||||
err.clear();
|
|
||||||
int wbytes = send(socket, bytes, nbytes, 0);
|
|
||||||
if (wbytes == SOCKET_ERROR) {
|
|
||||||
int errcode = WSAGetLastError();
|
|
||||||
if (errcode == WSAEWOULDBLOCK) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
err = "send failure";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert(wbytes > 0);
|
|
||||||
return wbytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int socket_recv(SOCKET socket, char *bytes, int nbytes, std::string &err) {
|
|
||||||
err.clear();
|
|
||||||
int nrecv = recv(socket, bytes, nbytes, 0);
|
|
||||||
if (nrecv < 0) {
|
|
||||||
int errcode = WSAGetLastError();
|
|
||||||
if (errcode == WSAEWOULDBLOCK) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
err = "recv failure";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else if (nrecv == 0) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return nrecv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int socket_close(SOCKET socket) {
|
|
||||||
return closesocket(socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int socket_poll(struct pollfd *pollvec, int pollcount, int mstimeout, std::string &err) {
|
|
||||||
if (pollcount == 0) {
|
|
||||||
if (mstimeout > 0) Sleep(mstimeout);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int status = WSAPoll(pollvec, pollcount, mstimeout);
|
|
||||||
if (status < 0) {
|
|
||||||
err = strerror_str(WSAGetLastError());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void init_winsock() {
|
|
||||||
WSADATA data;
|
|
||||||
int errcode = WSAStartup(2, &data);
|
|
||||||
if (errcode != 0) {
|
|
||||||
fprintf(stderr, "Winsock didn't initalize, error %d", errcode);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int console_write(const char *bytes, int nbytes) {
|
|
||||||
if (nbytes == 0) return 0;
|
|
||||||
HANDLE hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
assert(hstdout != INVALID_HANDLE_VALUE);
|
|
||||||
DWORD nwrote;
|
|
||||||
if (nbytes > 10000) nbytes = 10000;
|
|
||||||
assert(WriteConsoleA(hstdout, bytes, nbytes, &nwrote, nullptr));
|
|
||||||
assert(nwrote > 0);
|
|
||||||
return nwrote;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int console_read(char *bytes, int nbytes) {
|
|
||||||
HANDLE hstdin = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
assert(hstdin != INVALID_HANDLE_VALUE);
|
|
||||||
INPUT_RECORD inrecords[512];
|
|
||||||
DWORD nread, nevents;
|
|
||||||
int nascii = 0;
|
|
||||||
if (GetNumberOfConsoleInputEvents(hstdin, &nevents)) {
|
|
||||||
if (int(nevents) > nbytes) nevents = nbytes;
|
|
||||||
ReadConsoleInputA(hstdin, inrecords, nevents, &nread);
|
|
||||||
for (int i = 0; i < int(nread); i++) {
|
|
||||||
const INPUT_RECORD &inr = inrecords[i];
|
|
||||||
if (inr.EventType != KEY_EVENT) continue;
|
|
||||||
const KEY_EVENT_RECORD &key = inr.Event.KeyEvent;
|
|
||||||
if (!key.bKeyDown) continue;
|
|
||||||
char c = key.uChar.AsciiChar;
|
|
||||||
bytes[nascii++] = c;
|
|
||||||
}
|
|
||||||
return nascii;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ssl_load_certificate_authorities(SSL_CTX *ctx) {
|
|
||||||
HCERTSTORE hStore = CertOpenSystemStoreW(0, L"ROOT");
|
|
||||||
PCCERT_CONTEXT pContext = NULL;
|
|
||||||
X509 *x509;
|
|
||||||
X509_STORE *store = SSL_CTX_get_cert_store(ctx);
|
|
||||||
|
|
||||||
if (!hStore) {
|
|
||||||
fprintf(stderr, "Cannot open system certificate store.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((pContext = CertEnumCertificatesInStore(hStore, pContext))) {
|
|
||||||
const unsigned char *encoded_cert = pContext->pbCertEncoded;
|
|
||||||
x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
|
|
||||||
if (x509) {
|
|
||||||
X509_STORE_add_cert(store, x509);
|
|
||||||
X509_free(x509);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CertCloseStore(hStore, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
class MonoClock {
|
|
||||||
public:
|
|
||||||
double freq_;
|
|
||||||
|
|
||||||
MonoClock() {
|
|
||||||
LARGE_INTEGER x;
|
|
||||||
BOOL status = QueryPerformanceFrequency(&x);
|
|
||||||
assert(status != 0);
|
|
||||||
freq_ = 1.0 / double(x.QuadPart);
|
|
||||||
}
|
|
||||||
|
|
||||||
double get() {
|
|
||||||
LARGE_INTEGER x;
|
|
||||||
BOOL status = QueryPerformanceCounter(&x);
|
|
||||||
assert(status != 0);
|
|
||||||
return double(x.QuadPart) * freq_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "driver-common.cpp"
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
init_winsock();
|
|
||||||
OPENSSL_init_ssl(0, NULL);
|
|
||||||
SourceDB::register_lua_builtins();
|
|
||||||
Driver driver;
|
|
||||||
return driver.drive(argc, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
#include "driver-ssl.hpp"
|
|
||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
|
||||||
#include <vector>
|
|
||||||
#include <filesystem>
|
|
||||||
|
|
||||||
extern std::string strerror_str(int err);
|
|
||||||
|
|
||||||
namespace drvssl {
|
|
||||||
|
|
||||||
const char *dummy_cert =
|
|
||||||
"-----BEGIN CERTIFICATE-----\n"
|
|
||||||
"MIIDezCCAmOgAwIBAgIUajKmxrLMr9zBMlphrTJU5qKG8FgwDQYJKoZIhvcNAQEL\n"
|
|
||||||
"BQAwTDELMAkGA1UEBhMCVVMxFTATBgNVBAgMDFBlbm5zeWx2YW5pYTESMBAGA1UE\n"
|
|
||||||
"CgwJbG9jYWxob3N0MRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMjIwMzIyMTczMzA4\n"
|
|
||||||
"WhgPMjEyMjAyMjYxNzMzMDhaMEwxCzAJBgNVBAYTAlVTMRUwEwYDVQQIDAxQZW5u\n"
|
|
||||||
"c3lsdmFuaWExEjAQBgNVBAoMCWxvY2FsaG9zdDESMBAGA1UEAwwJbG9jYWxob3N0\n"
|
|
||||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5OWIaKqYae4nPxvu5EP3\n"
|
|
||||||
"VilcjApYcMT4+2ypfQoB6PEep5lwguA929rNsTKnhGsEiQAZ0eZPEZN7VhUwf/hz\n"
|
|
||||||
"26jIyTT43ELkt6k97wwSZSXuT65RpSiemwEs6g2mMwzpgP6nv+yam4HjE9AKiHGN\n"
|
|
||||||
"YeTV72Nw1EN70t6IjIf4jsJRXqDJkUx5sSSD6j0WBTOhzozIDgZHTDwiLhatE66m\n"
|
|
||||||
"SNoD8oWC0PscbUgOJkFpbaCAS8RJmpsdgkTFae2rzL9cOFLGw6OgV/BV1J1s0ks8\n"
|
|
||||||
"+veoMMtIO6fese+OZ+DyQbuGaoaltZUXzY6QjD5l34m2mGplelT7BrpcqJTBHwmh\n"
|
|
||||||
"CwIDAQABo1MwUTAdBgNVHQ4EFgQUXQM5TVfJ9gpUXg8fZ8yfuUVcBP8wHwYDVR0j\n"
|
|
||||||
"BBgwFoAUXQM5TVfJ9gpUXg8fZ8yfuUVcBP8wDwYDVR0TAQH/BAUwAwEB/zANBgkq\n"
|
|
||||||
"hkiG9w0BAQsFAAOCAQEAqYX/ZGv0Qh/xdXppjnqojm8mH0giDW4tvwMqHcW3YRa3\n"
|
|
||||||
"9J2yYot+rHjU5g4n6HEmWDBE0eqLz9n3Y3fkFzT8RWZwBaST965CgsfGofyuA2hC\n"
|
|
||||||
"Ddn4Am3B5tTPmi8WWRZg8amhpGVD/mwkoVFIK0M337b1aZUJYPE+Kc9WetSL2KqB\n"
|
|
||||||
"EhqSQpkAWhVadzP85dq2T9EDjAvhlFTFlDEBx1GDUcc8M0KQ9NEvLT7LgoUcbMiT\n"
|
|
||||||
"PerlSZQTB0crchXTRSERgiwu80r7D6STn/RcPL9Fg5PkA94/d87jGbmV4sxSRsvM\n"
|
|
||||||
"z+DnJGjHrV1J/jHPrnVvVLpigBlGno3C5O/sRw3gcQ==\n"
|
|
||||||
"-----END CERTIFICATE-----\n";
|
|
||||||
|
|
||||||
const char *dummy_key =
|
|
||||||
"-----BEGIN PRIVATE KEY-----\n"
|
|
||||||
"MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDk5Yhoqphp7ic/\n"
|
|
||||||
"G+7kQ/dWKVyMClhwxPj7bKl9CgHo8R6nmXCC4D3b2s2xMqeEawSJABnR5k8Rk3tW\n"
|
|
||||||
"FTB/+HPbqMjJNPjcQuS3qT3vDBJlJe5PrlGlKJ6bASzqDaYzDOmA/qe/7JqbgeMT\n"
|
|
||||||
"0AqIcY1h5NXvY3DUQ3vS3oiMh/iOwlFeoMmRTHmxJIPqPRYFM6HOjMgOBkdMPCIu\n"
|
|
||||||
"Fq0TrqZI2gPyhYLQ+xxtSA4mQWltoIBLxEmamx2CRMVp7avMv1w4UsbDo6BX8FXU\n"
|
|
||||||
"nWzSSzz696gwy0g7p96x745n4PJBu4ZqhqW1lRfNjpCMPmXfibaYamV6VPsGulyo\n"
|
|
||||||
"lMEfCaELAgMBAAECggEBAJa1AiFX4U4tva1xqNKmZV1XklWqIhzts7lnDBkF08gZ\n"
|
|
||||||
"qcNT5Z5mIpR09eVropwvEidZ56Yp63l5D0XYYbyAS1gfQ0QnGot7h7fdOKgB3MK4\n"
|
|
||||||
"PLY94gfKPNN17KqWHg2SvNNv1+cn04v78xUCb0zy5tHDp5Acexdm70ohtupARElJ\n"
|
|
||||||
"LSHdS7ebsqZUFXbbM3BpPEsQLi3PrzNs1DrKkZ3rR6eMGrsDqExXx8/foi9aZKsd\n"
|
|
||||||
"BGM2/kcTJ5aY6NhSv5iqO1oK46sbMrjVW/bYNsOyl0eFjwTRahn+Zhp/JMewZYeu\n"
|
|
||||||
"715g6kzbZNwEzBLgrhNPF6E2ycEr/C6z5bE78g5QCkECgYEA8s07UUY25bjYiWWy\n"
|
|
||||||
"W38pT7d/OXBSyKnq16N6MjVahl29r7nezFiDeLhLC0QiwXu/+qyxVZkB95MMGZXS\n"
|
|
||||||
"AsaKFNis3AJ6eR4SYyhpSScYKNvlKIiW37TtR4FDcy7y5LL6tFpiDDIGH3LuyWNo\n"
|
|
||||||
"d76142MBpv5aStnLGYU3pcZj43sCgYEA8VbNM4nqgSCQcbnHYjvsgphEMNSaoVie\n"
|
|
||||||
"xob2uigXdV6Te0ayoUFBnVNKVsRhk+sswuTV4k1pK/On+USVl2tQ16tcaVMjTfSD\n"
|
|
||||||
"HLYTJLmt6s4DcywWj5dfkbDoe5PulGXNZE960qXmOC62Lf0VMRwJ5x4FBRvGTjKC\n"
|
|
||||||
"zvekI2/kO7ECgYEAhBGeclb/BXXGUvY+TgadMf9d9KBkZ0IFu8Xwcd8TnoLe6vbv\n"
|
|
||||||
"ebery75zE228egIWKwREcYsIxuH1cvVLhrb35N73J7UxaTAyUD1rB598RL1XqPSj\n"
|
|
||||||
"HIwNhReK2NxwwnWYaQHA02FiczjRKjooWPojdcwk2fEArDZLg1YzLrj7HIECgYEA\n"
|
|
||||||
"htdx1Y8ESFtyeShMv5UtoxYCW6oeL3H9XH0CE6bc3IYYLvOkULbOO2HTEkGtJ2Fp\n"
|
|
||||||
"5AbJfiS0U4tS2dI5Jp4eUDH9cxexjRfFvd/5ODbKdnver5X9kQMJsbQ/YPSZg66R\n"
|
|
||||||
"oK9Lt7Bbvh5TScSy93psCgba1SzckspkDdGNkwMsaTECgYEAnFWaxormLUpXQRLs\n"
|
|
||||||
"tKzMMHgVnHlsHiqXH432zmT2fpGZHYoWbsGuQjjrHGnSiu3QbDhnzM6y/T2GRs6z\n"
|
|
||||||
"zHteIo/tzIyxg4MvJGJ9qANA7HoiKBdQ7G/I/NLJIyWAjj+e7/hgzKFcf+dpjpDq\n"
|
|
||||||
"HcKc9a4WXhC7yu79e5BnKWltHXY=\n"
|
|
||||||
"-----END PRIVATE KEY-----\n";
|
|
||||||
|
|
||||||
std::string error_string() {
|
|
||||||
// Get the last code.
|
|
||||||
int code = 0;
|
|
||||||
while (true) {
|
|
||||||
int icode = ERR_get_error();
|
|
||||||
if (icode == 0) break;
|
|
||||||
code = icode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch and clear errno.
|
|
||||||
int terrno = errno;
|
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
if (code != 0) {
|
|
||||||
const char *rc = ERR_reason_error_string(code);
|
|
||||||
if (rc != nullptr) {
|
|
||||||
return rc;
|
|
||||||
} else {
|
|
||||||
return strerror_str(ERR_GET_REASON(code));
|
|
||||||
}
|
|
||||||
} else if (terrno != 0) {
|
|
||||||
return strerror_str(terrno);
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_all_errors() {
|
|
||||||
ERR_clear_error();
|
|
||||||
errno = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SSL_CTX *new_context(int verify) {
|
|
||||||
SSL_CTX *ctx = SSL_CTX_new(TLS_method());
|
|
||||||
SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
|
||||||
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
|
||||||
SSL_CTX_set_verify(ctx, verify, nullptr);
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ctx_use_certificate_str(SSL_CTX *ctx, const char *str) {
|
|
||||||
UniqueBIO bio(BIO_new(BIO_s_mem()));
|
|
||||||
BIO_puts(bio.get(), str);
|
|
||||||
UniqueX509 certificate(PEM_read_bio_X509(bio.get(), NULL, NULL, NULL));
|
|
||||||
return SSL_CTX_use_certificate(ctx, certificate.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ctx_use_privatekey_str(SSL_CTX *ctx, const char *str) {
|
|
||||||
UniqueBIO bio(BIO_new(BIO_s_mem()));
|
|
||||||
BIO_puts(bio.get(), str);
|
|
||||||
UniquePKEY pkey(PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL));
|
|
||||||
return SSL_CTX_use_PrivateKey(ctx, pkey.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
void ctx_load_dummy_cert(SSL_CTX *ctx) {
|
|
||||||
ERR_clear_error();
|
|
||||||
if (ctx_use_certificate_str(ctx, dummy_cert) <= 0) {
|
|
||||||
ERR_print_errors_fp(stderr);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (ctx_use_privatekey_str(ctx, dummy_key) <= 0) {
|
|
||||||
ERR_print_errors_fp(stderr);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int count_certificates(const char *fn) {
|
|
||||||
static char null_passwd;
|
|
||||||
ErrClearErrorOnExit ece;
|
|
||||||
UniqueBIO bio(BIO_new(BIO_s_file()));
|
|
||||||
assert(bio != nullptr);
|
|
||||||
if (BIO_read_filename(bio.get(), fn) <= 0) {
|
|
||||||
std::cerr << "Cannot open file: " << fn << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
int total = 0;
|
|
||||||
while (true) {
|
|
||||||
UniqueX509 x(PEM_read_bio_X509_AUX(bio.get(), nullptr, nullptr, &null_passwd));
|
|
||||||
if (x == nullptr) break;
|
|
||||||
total += 1;
|
|
||||||
}
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool contains_privatekey(const char *fn) {
|
|
||||||
static char null_passwd;
|
|
||||||
ErrClearErrorOnExit ece;
|
|
||||||
UniqueBIO bio(BIO_new(BIO_s_file()));
|
|
||||||
assert(bio != nullptr);
|
|
||||||
if (BIO_read_filename(bio.get(), fn) <= 0) {
|
|
||||||
std::cerr << "Cannot open file: " << fn << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
UniquePKEY k(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, &null_passwd));
|
|
||||||
return k != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ctx_load_cert_from_directory(SSL_CTX *ctx, const std::string &dir) {
|
|
||||||
std::vector<std::string> key_paths;
|
|
||||||
std::vector<std::string> cert_paths;
|
|
||||||
|
|
||||||
for (const auto & entry : std::filesystem::directory_iterator(dir)) {
|
|
||||||
std::string fn = entry.path();
|
|
||||||
if (count_certificates(fn.c_str()) >= 1) {
|
|
||||||
cert_paths.push_back(fn);
|
|
||||||
}
|
|
||||||
if (contains_privatekey(fn.c_str())) {
|
|
||||||
key_paths.push_back(fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cert_paths.size() > 1) {
|
|
||||||
std::cerr << "Directory contains multiple certs: " << dir << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (key_paths.size() > 1) {
|
|
||||||
std::cerr << "Directory contains multiple keys: " << dir << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (cert_paths.empty()) {
|
|
||||||
std::cerr << "Directory doesn't contain a cert: " << dir << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (key_paths.empty()) {
|
|
||||||
std::cerr << "Directory doesn't contain a key: " << dir << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int status;
|
|
||||||
status = SSL_CTX_use_PrivateKey_file(ctx, key_paths[0].c_str(), SSL_FILETYPE_PEM);
|
|
||||||
assert(status == 1);
|
|
||||||
status = SSL_CTX_use_certificate_chain_file(ctx, cert_paths[0].c_str());
|
|
||||||
assert(status == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace drvssl
|
|
||||||
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
#ifndef DRIVER_SSL_HPP
|
|
||||||
#define DRIVER_SSL_HPP
|
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#include <openssl/rsa.h>
|
|
||||||
#include <openssl/x509.h>
|
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <openssl/err.h>
|
|
||||||
#include <openssl/bio.h>
|
|
||||||
#include <openssl/pem.h>
|
|
||||||
#include <openssl/conf.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace drvssl {
|
|
||||||
|
|
||||||
struct SSL_Deleter {
|
|
||||||
void operator()(SSL *ssl) { SSL_free(ssl); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CTX_Deleter {
|
|
||||||
void operator()(SSL_CTX *ctx) { SSL_CTX_free(ctx); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BIO_Deleter {
|
|
||||||
void operator()(BIO *bio) { BIO_free(bio); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct X509_Deleter {
|
|
||||||
void operator()(X509 *x) { X509_free(x); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PKEY_Deleter {
|
|
||||||
void operator()(EVP_PKEY *p) { EVP_PKEY_free(p); }
|
|
||||||
};
|
|
||||||
|
|
||||||
using UniqueSSL = std::unique_ptr<SSL, SSL_Deleter>;
|
|
||||||
using UniqueCTX = std::unique_ptr<SSL_CTX, CTX_Deleter>;
|
|
||||||
using UniqueBIO = std::unique_ptr<BIO, BIO_Deleter>;
|
|
||||||
using UniqueX509 = std::unique_ptr<X509, X509_Deleter>;
|
|
||||||
using UniquePKEY = std::unique_ptr<EVP_PKEY, PKEY_Deleter>;
|
|
||||||
|
|
||||||
struct ErrClearErrorOnExit {
|
|
||||||
~ErrClearErrorOnExit() {
|
|
||||||
ERR_clear_error();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return the OpenSSL error as a string.
|
|
||||||
std::string error_string();
|
|
||||||
void clear_all_errors();
|
|
||||||
SSL_CTX *new_context(int verify);
|
|
||||||
void ctx_load_dummy_cert(SSL_CTX *ctx);
|
|
||||||
void ctx_load_cert_from_directory(SSL_CTX *ctx, const std::string &dir);
|
|
||||||
|
|
||||||
} // namespace drvssl
|
|
||||||
|
|
||||||
#endif // DRIVER_SSL_HPP
|
|
||||||
|
|
||||||
@@ -1,528 +0,0 @@
|
|||||||
|
|
||||||
#include "wrap-string.hpp"
|
|
||||||
#include "wrap-vector.hpp"
|
|
||||||
#include "util.hpp"
|
|
||||||
#include "spookyv2.hpp"
|
|
||||||
#include "driver-util.hpp"
|
|
||||||
#include "luastack.hpp"
|
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
#include <fstream>
|
|
||||||
#include <ios>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#define RLOG_BUFSIZE (1024 * 1024)
|
|
||||||
#define MAX_ARGC 1024
|
|
||||||
|
|
||||||
namespace drv {
|
|
||||||
|
|
||||||
std::vector<std::string_view> split_view(std::string_view v, char sep) {
|
|
||||||
std::vector<std::string_view> result;
|
|
||||||
while (true) {
|
|
||||||
size_t pos = v.find(sep);
|
|
||||||
if (pos == std::string_view::npos) break;
|
|
||||||
result.push_back(v.substr(0, pos));
|
|
||||||
v = v.substr(pos + 1);
|
|
||||||
}
|
|
||||||
result.push_back(v);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void split_target(std::string_view target, std::string &cert, std::string &host, std::string &port) {
|
|
||||||
std::vector<std::string_view> split = split_view(target, ':');
|
|
||||||
if (split.size() != 3) {
|
|
||||||
cert.clear(); host.clear(); port.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (split[0].empty() || split[1].empty() || split[2].empty()) {
|
|
||||||
cert.clear(); host.clear(); port.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cert = std::string(split[0]);
|
|
||||||
host = std::string(split[1]);
|
|
||||||
port = std::string(split[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> parse_control_lst(std::string_view ctrl) {
|
|
||||||
std::vector<std::string> result;
|
|
||||||
while (!ctrl.empty()) {
|
|
||||||
std::string_view line = sv::read_to_line(ctrl);
|
|
||||||
std::string_view trimmed = sv::trim(line);
|
|
||||||
if ((trimmed.size() > 0) && (trimmed[0] != '#')) {
|
|
||||||
result.emplace_back(trimmed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum DrvAction {
|
|
||||||
CREATE_ENGINE,
|
|
||||||
CLEAN_EXIT,
|
|
||||||
DRV_CLEAR_NEW_OUTGOING,
|
|
||||||
DRV_SENT_OUTGOING,
|
|
||||||
DRV_RECV_INCOMING,
|
|
||||||
DRV_NOTIFY_CLOSE,
|
|
||||||
DRV_NOTIFY_ACCEPT,
|
|
||||||
DRV_CLEAR_LUA_SOURCE,
|
|
||||||
DRV_ADD_LUA_SOURCE,
|
|
||||||
DRV_INVOKE_EVENT_INIT,
|
|
||||||
DRV_INVOKE_EVENT_UPDATE
|
|
||||||
};
|
|
||||||
|
|
||||||
inline static const char *action_string(DrvAction act) {
|
|
||||||
switch(act) {
|
|
||||||
case CREATE_ENGINE: return "CREATE_ENGINE";
|
|
||||||
case CLEAN_EXIT: return "CLEAN_EXIT";
|
|
||||||
case DRV_CLEAR_NEW_OUTGOING: return "DRV_CLEAR_NEW_OUTGOING";
|
|
||||||
case DRV_SENT_OUTGOING: return "DRV_SENT_OUTGOING";
|
|
||||||
case DRV_RECV_INCOMING: return "DRV_RECV_INCOMING";
|
|
||||||
case DRV_NOTIFY_CLOSE: return "DRV_NOTIFY_CLOSE";
|
|
||||||
case DRV_NOTIFY_ACCEPT: return "DRV_NOTIFY_ACCEPT";
|
|
||||||
case DRV_CLEAR_LUA_SOURCE: return "DRV_CLEAR_LUA_SOURCE";
|
|
||||||
case DRV_ADD_LUA_SOURCE: return "DRV_ADD_LUA_SOURCE";
|
|
||||||
case DRV_INVOKE_EVENT_INIT: return "DRV_INVOKE_EVENT_INIT";
|
|
||||||
case DRV_INVOKE_EVENT_UPDATE: return "DRV_INVOKE_EVENT_UPDATE";
|
|
||||||
default: return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wlog_uint8(std::ofstream &s, uint8_t v) {
|
|
||||||
s.put((char)v);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wlog_uint16(std::ofstream &s, uint16_t v) {
|
|
||||||
s.write((const char *)&v, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wlog_uint32(std::ofstream &s, uint32_t v) {
|
|
||||||
s.write((const char *)&v, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wlog_uint64(std::ofstream &s, uint64_t v) {
|
|
||||||
s.write((const char *)&v, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wlog_double(std::ofstream &s, double v) {
|
|
||||||
s.write((const char *)&v, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wlog_string(std::ofstream &s, std::string_view v) {
|
|
||||||
assert(v.size() < RLOG_BUFSIZE);
|
|
||||||
if (v.size() >= 255) {
|
|
||||||
wlog_uint8(s, 0xFF);
|
|
||||||
wlog_uint32(s, v.size());
|
|
||||||
} else {
|
|
||||||
wlog_uint8(s, v.size());
|
|
||||||
}
|
|
||||||
s.write(v.data(), v.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
static void wlog_cmd_hash(std::ofstream &s, DrvAction act, uint32_t hash) {
|
|
||||||
// std::cerr << "Logging " << action_string(act) << " " << hash << std::endl;
|
|
||||||
wlog_uint8(s, act);
|
|
||||||
wlog_uint32(s, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
// After doing an rlog operation, you should check the stream
|
|
||||||
// for "s.good()" to find out if there was any error.
|
|
||||||
static uint8_t rlog_uint8(std::ifstream &s) {
|
|
||||||
uint8_t result;
|
|
||||||
s.read((char *)&result, 1);
|
|
||||||
if (!s.good()) return 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint16_t rlog_uint16(std::ifstream &s) {
|
|
||||||
uint16_t result;
|
|
||||||
s.read((char *)&result, 2);
|
|
||||||
if (!s.good()) return 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t rlog_uint32(std::ifstream &s) {
|
|
||||||
uint32_t result;
|
|
||||||
s.read((char *)&result, 4);
|
|
||||||
if (!s.good()) return 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t rlog_uint64(std::ifstream &s) {
|
|
||||||
uint64_t result;
|
|
||||||
s.read((char *)&result, 8);
|
|
||||||
if (!s.good()) return 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double rlog_double(std::ifstream &s) {
|
|
||||||
double result;
|
|
||||||
s.read((char *)&result, 8);
|
|
||||||
if (!s.good()) return 0.0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view rlog_string(std::ifstream &s, char *rlog_buf) {
|
|
||||||
uint32_t len = rlog_uint8(s);
|
|
||||||
if (len == 255) {
|
|
||||||
len = rlog_uint32(s);
|
|
||||||
}
|
|
||||||
assert(len <= RLOG_BUFSIZE);
|
|
||||||
if (len > 0) s.read(rlog_buf, len);
|
|
||||||
if (!s.good()) return std::string_view();
|
|
||||||
return std::string_view(rlog_buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReplayPlayer::ReplayPlayer() {
|
|
||||||
status_ = ST_REPLAYING;
|
|
||||||
enable_stdout_ = false;
|
|
||||||
buf_.reset(new char[RLOG_BUFSIZE]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::flush() {
|
|
||||||
f_.flush();
|
|
||||||
if (!f_.good()) {
|
|
||||||
std::cerr << "Logfile write failed, replay logging abandoned." << std::endl;
|
|
||||||
f_.close();
|
|
||||||
f_.clear();
|
|
||||||
logging_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReplayRecorder::open_logfile(const char *fn) {
|
|
||||||
f_.open(fn, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
|
|
||||||
if (f_.good()) {
|
|
||||||
logging_ = true;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
f_.close();
|
|
||||||
f_.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ReplayPlayer::Status ReplayPlayer::step() {
|
|
||||||
if (status_ != ST_REPLAYING) return status_;
|
|
||||||
uint8_t code = rlog_uint8(f_);
|
|
||||||
if (f_.eof()) {
|
|
||||||
set_status(ST_LOGFILE_ENDS_ABRUPTLY);
|
|
||||||
return status_;
|
|
||||||
}
|
|
||||||
int hash = rlog_uint32(f_);
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return status_;
|
|
||||||
}
|
|
||||||
// std::cerr << "Executing: " << action_string(DrvAction(code)) << " " << eng::memhash() << std::endl;
|
|
||||||
if (hash != eng::memhash()) {
|
|
||||||
set_status(ST_NONDERMINISTIC);
|
|
||||||
return status_;
|
|
||||||
}
|
|
||||||
switch (code) {
|
|
||||||
case CREATE_ENGINE: create_engine(); break;
|
|
||||||
case CLEAN_EXIT: clean_exit(); break;
|
|
||||||
case DRV_CLEAR_NEW_OUTGOING: drv_clear_new_outgoing(); break;
|
|
||||||
case DRV_SENT_OUTGOING: drv_sent_outgoing(); break;
|
|
||||||
case DRV_RECV_INCOMING: drv_recv_incoming(); break;
|
|
||||||
case DRV_NOTIFY_CLOSE: drv_notify_close(); break;
|
|
||||||
case DRV_NOTIFY_ACCEPT: drv_notify_accept(); break;
|
|
||||||
case DRV_CLEAR_LUA_SOURCE: drv_clear_lua_source(); break;
|
|
||||||
case DRV_ADD_LUA_SOURCE: drv_add_lua_source(); break;
|
|
||||||
case DRV_INVOKE_EVENT_INIT: drv_invoke_event_init(); break;
|
|
||||||
case DRV_INVOKE_EVENT_UPDATE: drv_invoke_event_update(); break;
|
|
||||||
default:
|
|
||||||
assert(false && "Replay Log contains invalid command.");
|
|
||||||
}
|
|
||||||
return status_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::set_status(Status e) {
|
|
||||||
status_ = e;
|
|
||||||
f_.close();
|
|
||||||
f_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::print_status(std::ostream &s) {
|
|
||||||
switch (status_) {
|
|
||||||
case ST_REPLAYING:
|
|
||||||
s << "No errors detected: " << logfn_ << std::endl;
|
|
||||||
return;
|
|
||||||
case ST_CLEAN_EXIT:
|
|
||||||
s << "Engine exited cleanly without errors: " << logfn_ << std::endl;
|
|
||||||
return;
|
|
||||||
case ST_ERR_OPENING_LOGFILE:
|
|
||||||
s << "Could not open logfile: " << logfn_ << std::endl;
|
|
||||||
return;
|
|
||||||
case ST_LOGFILE_ENDS_ABRUPTLY:
|
|
||||||
s << "Logfile reached end-of-file: " << logfn_ << std::endl;
|
|
||||||
return;
|
|
||||||
case ST_LOGFILE_CORRUPT:
|
|
||||||
s << "Logfile corrupt: " << logfn_ << std::endl;
|
|
||||||
return;
|
|
||||||
case ST_NONDERMINISTIC:
|
|
||||||
s << "Nondeterminism detected: " << logfn_ << std::endl;
|
|
||||||
return;
|
|
||||||
case ST_COULDNT_CREATE_ENGINE:
|
|
||||||
s << "Could not create engine: " << logfn_ << " " << engine_ << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReplayPlayer::open_logfile(const char *fn) {
|
|
||||||
logfn_ = fn;
|
|
||||||
f_.clear();
|
|
||||||
f_.open(fn, std::ios_base::in | std::ios_base::binary);
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_ERR_OPENING_LOGFILE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReplayRecorder::create_engine(const char *kind) {
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, CREATE_ENGINE, eng::memhash());
|
|
||||||
wlog_string(f_, kind);
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
e_ = DrivenEngine::make(kind);
|
|
||||||
DrivenEngine::set(e_.get());
|
|
||||||
return e_ != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::create_engine() {
|
|
||||||
std::string_view kind = rlog_string(f_, buf_.get());
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
engine_ = std::string(kind);
|
|
||||||
e_ = DrivenEngine::make(kind);
|
|
||||||
DrivenEngine::set(e_.get());
|
|
||||||
if (e_ == nullptr) {
|
|
||||||
set_status(ST_COULDNT_CREATE_ENGINE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::clean_exit() {
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, CLEAN_EXIT, eng::memhash());
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::clean_exit() {
|
|
||||||
set_status(ST_CLEAN_EXIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::drv_clear_new_outgoing() {
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, DRV_CLEAR_NEW_OUTGOING, eng::memhash());
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
e_->drv_clear_new_outgoing();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::drv_clear_new_outgoing() {
|
|
||||||
e_->drv_clear_new_outgoing();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::drv_sent_outgoing(int chid, int nbytes) {
|
|
||||||
assert ((nbytes >= 0) && (nbytes <= 65535));
|
|
||||||
if (logging_) {
|
|
||||||
std::string_view data = e_->drv_peek_outgoing(chid);
|
|
||||||
assert(nbytes <= int(data.size()));
|
|
||||||
wlog_cmd_hash(f_, DRV_SENT_OUTGOING, eng::memhash());
|
|
||||||
wlog_uint16(f_, chid);
|
|
||||||
wlog_uint16(f_, nbytes);
|
|
||||||
wlog_uint64(f_, SpookyHash::QkHash64(data.data(), nbytes));
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
e_->drv_sent_outgoing(chid, nbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::drv_sent_outgoing() {
|
|
||||||
int chid = rlog_uint16(f_);
|
|
||||||
int nbytes = rlog_uint16(f_);
|
|
||||||
uint64_t hash = rlog_uint64(f_);
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::string_view data = e_->drv_peek_outgoing(chid);
|
|
||||||
if (nbytes > int(data.size())) {
|
|
||||||
set_status(ST_NONDERMINISTIC);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (hash != SpookyHash::QkHash64(data.data(), nbytes)) {
|
|
||||||
set_status(ST_NONDERMINISTIC);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((chid == 0) && (enable_stdout_)) {
|
|
||||||
std::string_view sub = data.substr(0, nbytes);
|
|
||||||
std::cout << sub;
|
|
||||||
}
|
|
||||||
e_->drv_sent_outgoing(chid, nbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::drv_recv_incoming(int chid, std::string_view data) {
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, DRV_RECV_INCOMING, eng::memhash());
|
|
||||||
wlog_uint16(f_, chid);
|
|
||||||
wlog_string(f_, data);
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
e_->drv_recv_incoming(chid, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::drv_recv_incoming() {
|
|
||||||
int chid = rlog_uint16(f_);
|
|
||||||
std::string_view data = rlog_string(f_, buf_.get());
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e_->drv_recv_incoming(chid, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::drv_notify_close(int chid, std::string_view err) {
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, DRV_NOTIFY_CLOSE, eng::memhash());
|
|
||||||
wlog_uint16(f_, chid);
|
|
||||||
wlog_string(f_, err);
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
e_->drv_notify_close(chid, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::drv_notify_close() {
|
|
||||||
int chid = rlog_uint16(f_);
|
|
||||||
std::string_view err = rlog_string(f_, buf_.get());
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e_->drv_notify_close(chid, err);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ReplayRecorder::drv_notify_accept(int port) {
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, DRV_NOTIFY_ACCEPT, eng::memhash());
|
|
||||||
wlog_uint16(f_, port);
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
return e_->drv_notify_accept(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::drv_notify_accept() {
|
|
||||||
int port = rlog_uint16(f_);
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e_->drv_notify_accept(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::drv_clear_lua_source() {
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, DRV_CLEAR_LUA_SOURCE, eng::memhash());
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
e_->drv_clear_lua_source();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::drv_clear_lua_source() {
|
|
||||||
e_->drv_clear_lua_source();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::drv_add_lua_source(std::string_view fn, std::string_view data) {
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, DRV_ADD_LUA_SOURCE, eng::memhash());
|
|
||||||
wlog_string(f_, fn);
|
|
||||||
wlog_string(f_, data);
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
e_->drv_add_lua_source(fn, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::drv_add_lua_source() {
|
|
||||||
std::string fn(rlog_string(f_, buf_.get()));
|
|
||||||
std::string_view data = rlog_string(f_, buf_.get());
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e_->drv_add_lua_source(fn, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::drv_invoke_event_init(int argc, char *argv[]) {
|
|
||||||
assert(argc <= MAX_ARGC);
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, DRV_INVOKE_EVENT_INIT, eng::memhash());
|
|
||||||
wlog_uint16(f_, argc);
|
|
||||||
for (int i = 0; i < argc; i++) {
|
|
||||||
wlog_string(f_, argv[i]);
|
|
||||||
}
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
e_->drv_invoke_event_init(argc, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::drv_invoke_event_init() {
|
|
||||||
std::vector<std::string> argv;
|
|
||||||
int argc = rlog_uint16(f_);
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (argc > MAX_ARGC) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < argc; i++) {
|
|
||||||
std::string_view arg = rlog_string(f_, buf_.get());
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
argv.emplace_back(arg);
|
|
||||||
}
|
|
||||||
std::vector<char *> cargv;
|
|
||||||
for (int i = 0; i < argc; i++) {
|
|
||||||
cargv.emplace_back(const_cast<char *>(argv[i].c_str()));
|
|
||||||
}
|
|
||||||
e_->drv_invoke_event_init(argc, &(cargv[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayRecorder::drv_invoke_event_update(double clock) {
|
|
||||||
if (logging_) {
|
|
||||||
wlog_cmd_hash(f_, DRV_INVOKE_EVENT_UPDATE, eng::memhash());
|
|
||||||
wlog_double(f_, clock);
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
e_->drv_invoke_event_update(clock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplayPlayer::drv_invoke_event_update() {
|
|
||||||
double clock = rlog_double(f_);
|
|
||||||
if (!f_.good()) {
|
|
||||||
set_status(ST_LOGFILE_CORRUPT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
e_->drv_invoke_event_update(clock);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace drv
|
|
||||||
|
|
||||||
LuaDefine(unittests_driverutil, "", "some unit tests") {
|
|
||||||
// Test split_target
|
|
||||||
std::string cert, host, port;
|
|
||||||
drv::split_target("cert:stanford.edu:80", cert, host, port);
|
|
||||||
LuaAssertStrEq(L, cert, "cert");
|
|
||||||
LuaAssertStrEq(L, host, "stanford.edu");
|
|
||||||
LuaAssertStrEq(L, port, "80");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
|
|
||||||
#ifndef DRIVER_UTIL_HPP
|
|
||||||
#define DRIVER_UTIL_HPP
|
|
||||||
|
|
||||||
#include "wrap-string.hpp"
|
|
||||||
#include "wrap-vector.hpp"
|
|
||||||
#include <string_view>
|
|
||||||
#include <fstream>
|
|
||||||
#include <ostream>
|
|
||||||
#include "drivenengine.hpp"
|
|
||||||
|
|
||||||
namespace drv {
|
|
||||||
|
|
||||||
void split_target(std::string_view target, std::string &cert, std::string &host, std::string &port);
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::string> parse_control_lst(std::string_view ctrl);
|
|
||||||
|
|
||||||
|
|
||||||
class ReplayRecorder {
|
|
||||||
private:
|
|
||||||
std::ofstream f_;
|
|
||||||
UniqueDrivenEngine e_;
|
|
||||||
bool logging_;
|
|
||||||
|
|
||||||
void flush();
|
|
||||||
public:
|
|
||||||
// Initialization consists of three steps:
|
|
||||||
//
|
|
||||||
// 1. The constructor, which creates an empty ReplayRecorder.
|
|
||||||
// 2. Open the logfile for writing, if desired, using open_logfile.
|
|
||||||
// 3. Make the engine, using create_engine.
|
|
||||||
//
|
|
||||||
// After that, you can use drv_xxx methods to send messages to the
|
|
||||||
// engine. These messages will get logged if the replay log is open.
|
|
||||||
//
|
|
||||||
ReplayRecorder() : logging_(false) {}
|
|
||||||
|
|
||||||
// Open the logfile.
|
|
||||||
//
|
|
||||||
// If you're going to open a logfile, you must do so before
|
|
||||||
// creating the engine. Returns false if the logfile couldn't be
|
|
||||||
// opened.
|
|
||||||
//
|
|
||||||
bool open_logfile(const char *fn);
|
|
||||||
|
|
||||||
// Create the DrivenEngine.
|
|
||||||
//
|
|
||||||
// Returns false if the DrivenEngine couldn't be created.
|
|
||||||
//
|
|
||||||
bool create_engine(const char *kind);
|
|
||||||
|
|
||||||
// Report to the logger that the engine is about to exit cleanly,
|
|
||||||
// without any error.
|
|
||||||
//
|
|
||||||
void clean_exit();
|
|
||||||
|
|
||||||
// These don't need to be logged.
|
|
||||||
//
|
|
||||||
const eng::vector<int> &drv_get_listen_ports() const { return e_->drv_get_listen_ports(); }
|
|
||||||
const eng::vector<int> &drv_get_new_outgoing() const { return e_->drv_get_new_outgoing(); }
|
|
||||||
std::string_view drv_get_target(int chid) const { return e_->drv_get_target(chid); }
|
|
||||||
bool drv_outgoing_empty(int chid) const { return e_->drv_outgoing_empty(chid); }
|
|
||||||
bool drv_get_channel_released(int chid) const { return e_->drv_get_channel_released(chid); }
|
|
||||||
std::string_view drv_peek_outgoing(int chid) const { return e_->drv_peek_outgoing(chid); }
|
|
||||||
bool drv_get_rescan_lua_source() const { return e_->drv_get_rescan_lua_source(); }
|
|
||||||
bool drv_get_stop_driver() const { return e_->drv_get_stop_driver(); }
|
|
||||||
|
|
||||||
// These operations do need to be logged.
|
|
||||||
//
|
|
||||||
void drv_clear_new_outgoing();
|
|
||||||
void drv_sent_outgoing(int chid, int nbytes);
|
|
||||||
void drv_recv_incoming(int chid, std::string_view data);
|
|
||||||
void drv_notify_close(int chid, std::string_view err);
|
|
||||||
int drv_notify_accept(int port);
|
|
||||||
void drv_clear_lua_source();
|
|
||||||
void drv_add_lua_source(std::string_view fn, std::string_view data);
|
|
||||||
void drv_invoke_event_init(int argc, char *argv[]);
|
|
||||||
void drv_invoke_event_update(double clock);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReplayPlayer {
|
|
||||||
public:
|
|
||||||
enum Status {
|
|
||||||
ST_REPLAYING,
|
|
||||||
ST_CLEAN_EXIT,
|
|
||||||
ST_ERR_OPENING_LOGFILE,
|
|
||||||
ST_LOGFILE_ENDS_ABRUPTLY,
|
|
||||||
ST_LOGFILE_CORRUPT,
|
|
||||||
ST_NONDERMINISTIC,
|
|
||||||
ST_COULDNT_CREATE_ENGINE,
|
|
||||||
};
|
|
||||||
private:
|
|
||||||
std::ifstream f_;
|
|
||||||
UniqueDrivenEngine e_;
|
|
||||||
std::unique_ptr<char[]> buf_;
|
|
||||||
Status status_;
|
|
||||||
std::string logfn_;
|
|
||||||
std::string engine_;
|
|
||||||
bool enable_stdout_;
|
|
||||||
|
|
||||||
void set_status(Status e);
|
|
||||||
|
|
||||||
void create_engine();
|
|
||||||
void clean_exit();
|
|
||||||
void drv_clear_new_outgoing();
|
|
||||||
void drv_sent_outgoing();
|
|
||||||
void drv_recv_incoming();
|
|
||||||
void drv_notify_close();
|
|
||||||
void drv_notify_accept();
|
|
||||||
void drv_clear_lua_source();
|
|
||||||
void drv_add_lua_source();
|
|
||||||
void drv_invoke_event_init();
|
|
||||||
void drv_invoke_event_update();
|
|
||||||
public:
|
|
||||||
ReplayPlayer();
|
|
||||||
|
|
||||||
// Open the logfile for reading.
|
|
||||||
//
|
|
||||||
// Returns false if the logfile can't be opened.
|
|
||||||
//
|
|
||||||
bool open_logfile(const char *fn);
|
|
||||||
|
|
||||||
// Enable stdout.
|
|
||||||
//
|
|
||||||
// Normally, stdout is suppressed during replay.
|
|
||||||
// If you enable stdout, then the engine will print
|
|
||||||
// all the same messages it did when running in the
|
|
||||||
// first place.
|
|
||||||
//
|
|
||||||
void enable_stdout() { enable_stdout_ = true; }
|
|
||||||
|
|
||||||
// Execute a single step from the replay log.
|
|
||||||
//
|
|
||||||
// Returns a status code, which is usually ST_REPLAYING.
|
|
||||||
// If it's anything else, display the status and stop.
|
|
||||||
//
|
|
||||||
Status step();
|
|
||||||
|
|
||||||
// Print a status message.
|
|
||||||
//
|
|
||||||
// Print a message associated with the most recent status report.
|
|
||||||
//
|
|
||||||
void print_status(std::ostream &s);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace drv
|
|
||||||
|
|
||||||
#endif // DRIVER_UTIL_HPP
|
|
||||||
Reference in New Issue
Block a user