#include "SocksFilter.h"
namespace panda { namespace unievent { namespace socks {
%%{
machine socks5_client_parser;
action negotiate {
panda_log_verbose_debug("negotiate");
if(noauth) {
do_connect();
} else {
do_auth();
}
}
action auth {
panda_log_verbose_debug("auth");
do_connect();
}
action connect {
panda_log_verbose_debug("connect");
if(rep) {
do_error(errc::protocol_error);
fbreak;
}
do_connected();
}
action auth_status {
panda_log_verbose_debug("auth status");
auth_status = (uint8_t)*fpc;
}
action noauth_auth_method {
panda_log_verbose_debug("noauth method");
noauth = true;
}
action userpass_auth_method {
panda_log_verbose_debug("userpass method");
noauth = false;
}
action noacceptable_auth_method {
panda_log_verbose_debug("noacceptable method");
do_error(errc::no_acceptable_auth_method);
fbreak;
}
action ip4 {
panda_log_verbose_debug("ip4");
}
action ip6 {
panda_log_verbose_debug("ip6");
}
action atyp {
atyp = (uint8_t)*fpc;
panda_log_verbose_debug("atyp: " << atyp);
}
action rep {
rep = (uint8_t)*fpc;
panda_log_verbose_debug("rep: " << rep);
}
action error {
do_error(errc::protocol_error);
}
ver=0x05;
byte=any;
auth_method = 0x00 @noauth_auth_method | 0x02 @userpass_auth_method | 0xFF @noacceptable_auth_method;
negotiate_reply := (ver auth_method) @negotiate $!error;
auth_ver = 0x01;
auth_status = any @auth_status;
auth_reply := (auth_ver auth_status) @auth @!error;
rep = 0x00;
rsv = 0x00;
atyp = byte @atyp;
dst_addr_ip4 = 4*byte $ip4;
dst_addr_ip6 = 16*byte $ip6;
dst_addr = dst_addr_ip4 when {atyp==0x01} |
dst_addr_ip6 when {atyp==0x04};
dst_port = 2*byte;
connect_reply := (ver rep @rep rsv atyp dst_addr dst_port) @connect @!error;
}%%
%%write data;
void SocksFilter::handle_read (string& buf, const ErrorCode& err) {
panda_log_debug("handle_read, err: " << err << " state:" << state << ", " << buf.length() << " bytes");
if (state == State::terminal) return NextFilter::handle_read(buf, err);
if (err) return do_error(err);
panda_log_verbose_debug(log::escaped{buf});
// pointer to current buffer
const char* buffer_ptr = buf.data();
// start parsing from the beginning pointer
const char* p = buffer_ptr;
// to the end pointer
const char* pe = buffer_ptr + buf.size();
const char* eof = pe;
// select reply parser by our state
switch (state) {
case State::handshake_reply:
cs = socks5_client_parser_en_negotiate_reply;
break;
case State::auth_reply:
cs = socks5_client_parser_en_auth_reply;
break;
case State::connect_reply:
cs = socks5_client_parser_en_connect_reply;
break;
case State::parsing:
// need more input
break;
case State::error:
panda_log_notice("error state, wont parse");
return;
default:
panda_log_notice("bad state, len: " << int(p - buffer_ptr));
do_error(errc::protocol_error);
return;
}
state = State::parsing;
%%write exec;
if (state == State::error) {
panda_log_notice("parser exiting in error state on pos: " << int(p - buffer_ptr));
} else if (state != State::parsing) {
panda_log_debug("parser finished");
}
}
}}}