add socks_decoder, stratum_decoder and session_flags
This commit is contained in:
527
decoders/socks/socks_decoder.cpp
Normal file
527
decoders/socks/socks_decoder.cpp
Normal file
@@ -0,0 +1,527 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
extern "C" {
|
||||
#include "stellar/stellar.h"
|
||||
#include "stellar/session.h"
|
||||
#include "stellar/stellar_mq.h"
|
||||
#include "stellar/stellar_exdata.h"
|
||||
}
|
||||
|
||||
#include "socks_decoder_internal.h"
|
||||
#include "stellar/log.h"
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
|
||||
static void socks_decoder_session_exdata_free(int idx, void *ex_ptr, void *arg)
|
||||
{
|
||||
UNUSED(idx);
|
||||
UNUSED(arg);
|
||||
|
||||
if (ex_ptr == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
free(ex_ptr);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//packet format refer to https://zh.wikipedia.org/wiki/SOCKS
|
||||
static int socks_rough_rec(const char *payload, size_t payload_len)
|
||||
{
|
||||
//for socks4
|
||||
if (payload_len < SOCKS5_REQUEST_METHOD_MIN_LEN)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (payload[0] == SOCKS_FIELD_VER_4 && payload[payload_len-1] == 0)
|
||||
{
|
||||
if (payload_len >= 9)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (payload[0] == SOCKS_FIELD_VER_5)
|
||||
{
|
||||
if (2 + (unsigned int)payload[1] == payload_len)
|
||||
{
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int socks5_method_request(const char *payload, size_t payload_len)
|
||||
{
|
||||
if (payload_len < SOCKS5_REQUEST_METHOD_MIN_LEN)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (payload[0] != SOCKS_FIELD_VER_5)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((size_t)(2 + payload[1]) != payload_len)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socks4_request(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
|
||||
{
|
||||
if (payload_len < 9)
|
||||
return -1;
|
||||
if (payload[0] != SOCKS_FIELD_VER_4 || payload[payload_len-1] != 0)
|
||||
return -1;
|
||||
|
||||
struct socks_addr *addr = &(stream->info.dst_addr);
|
||||
|
||||
memcpy(&(addr->port), payload + 2, 2);
|
||||
memcpy(&(addr->ipv4), payload + 4, 4);
|
||||
|
||||
if (addr->ipv4 & 0x00FFFFFF)
|
||||
{
|
||||
addr->type = SOCKS_ADDR_IPV4;
|
||||
return 0;
|
||||
}
|
||||
else if (addr->ipv4 & 0xFF000000)//socks4a hostname
|
||||
{
|
||||
size_t pos = 8;
|
||||
while (pos < payload_len - 2 && payload[pos] != 0)
|
||||
{
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (payload[pos] == 0)
|
||||
{
|
||||
if (payload_len - 1 - pos < 2)
|
||||
return -1;
|
||||
|
||||
addr->fqdn.iov_len = payload_len - 1 - pos - 1;
|
||||
addr->fqdn.iov_base = (unsigned char *)calloc(1, addr->fqdn.iov_len + 1);
|
||||
|
||||
memcpy(addr->fqdn.iov_base, payload+pos+1, addr->fqdn.iov_len);
|
||||
|
||||
addr->type = SOCKS_ADDR_FQDN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int socks5_request(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
|
||||
{
|
||||
unsigned int port_offset = 0;
|
||||
struct socks_addr *addr = &(stream->info.dst_addr);
|
||||
|
||||
if (payload_len < SOCKS5_REQUEST_MIN_LEN)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (payload[0] != SOCKS_FIELD_VER_5)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (payload[1] != SOCKS_CMD_CONNECT && payload[1] != SOCKS_CMD_BIND)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (payload[2] != SOCKS5_RSV_DEFAULT)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (payload[3])
|
||||
{
|
||||
case SOCKS_ATYPE_IP4:
|
||||
{
|
||||
if (payload_len != 10)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr->type = SOCKS_ADDR_IPV4;
|
||||
memcpy(&(addr->ipv4), payload+4, 4);
|
||||
|
||||
port_offset = 8;
|
||||
|
||||
break;
|
||||
}
|
||||
//should be processed
|
||||
case SOCKS_ATYPE_IP6:
|
||||
{
|
||||
if(payload_len != 22)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
addr->type = SOCKS_ADDR_IPV6;
|
||||
memcpy(addr->ipv6, payload+4, 16);
|
||||
port_offset = 20;
|
||||
|
||||
break;
|
||||
}
|
||||
case SOCKS_ATYPE_FQDN:
|
||||
{
|
||||
if (payload[4] < 0 || payload_len != (size_t)(7 + payload[4]))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
addr->type = SOCKS_ADDR_FQDN;
|
||||
addr->fqdn.iov_len = payload[4];
|
||||
addr->fqdn.iov_base = (char *)calloc(1, addr->fqdn.iov_len + 1);
|
||||
memcpy(addr->fqdn.iov_base, payload+5, payload[4]);
|
||||
|
||||
port_offset = 5 + payload[4];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&(addr->port), payload+port_offset, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socks5_pass_request(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
|
||||
{
|
||||
unsigned char *ubuf = (unsigned char*)payload;
|
||||
unsigned char *cur;
|
||||
struct socks_info *info = &stream->info;
|
||||
|
||||
if (ubuf[0]!=1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (payload_len < 2 || payload_len < (size_t)(3+ubuf[1]) || payload_len != (size_t)(3+ubuf[1]+ubuf[2+ubuf[1]]))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
info->user_name.iov_len = ubuf[1];
|
||||
info->user_name.iov_base = (char *)calloc(1, info->user_name.iov_len + 1);
|
||||
memcpy(info->user_name.iov_base, ubuf+2, info->user_name.iov_len);
|
||||
|
||||
info->password.iov_len = ubuf[2+ubuf[1]];
|
||||
info->password.iov_base = (char *)calloc(1, info->password.iov_len + 1);
|
||||
cur = ubuf + 2 + info->user_name.iov_len + 1;
|
||||
memcpy(info->password.iov_base, cur, info->password.iov_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void c2s_handler(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
|
||||
{
|
||||
switch (stream->client_state)
|
||||
{
|
||||
case SS_BEGIN:
|
||||
if (socks5_method_request(payload, payload_len) == 0)
|
||||
{
|
||||
stream->client_state = SS_SUB;
|
||||
stream->info.version = SOCKS_VERSION_5;
|
||||
}
|
||||
else if (socks4_request(stream, payload, payload_len) == 0)
|
||||
{
|
||||
stream->client_state = SS_END;
|
||||
stream->info.version = SOCKS_VERSION_4;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->client_state = SS_FAILED;
|
||||
}
|
||||
break;
|
||||
case SS_SUB:
|
||||
if (socks5_request(stream, payload, payload_len) == 0)
|
||||
{
|
||||
stream->client_state = SS_END;
|
||||
}
|
||||
else if (socks5_pass_request(stream, payload, payload_len) != 0)
|
||||
{
|
||||
stream->client_state = SS_FAILED;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int socks5_method_replay(const char *payload, size_t payload_len)
|
||||
{
|
||||
if (payload_len != 2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (payload[0] != SOCKS_FIELD_VER_5)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (payload[1] != 0 && payload[1]!=2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
////printf("socks5 method replay!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socks4_replay(const char *payload, size_t payload_len)
|
||||
{
|
||||
if (payload_len != 8)
|
||||
return -1;
|
||||
if (payload[0] != 0 || payload[1] < 90 || payload[1] > 93)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socks5_replay(const char *payload, size_t payload_len)
|
||||
{
|
||||
if (payload_len < 6)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (payload[0] != SOCKS_FIELD_VER_5)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (payload[1] != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (payload[2] != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch(payload[3])
|
||||
{
|
||||
case SOCKS_ATYPE_IP4:
|
||||
if (payload_len < 10)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case SOCKS_ATYPE_IP6:
|
||||
if (payload_len != 22)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case SOCKS_ATYPE_FQDN:
|
||||
if (payload[4] < 0 || payload_len < (size_t)(7 + payload[4]))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socks5_pass_reply(const char *payload, size_t payload_len)
|
||||
{
|
||||
if (payload_len != 2)
|
||||
return -1;
|
||||
if (payload[0] != 1 || payload[1] != 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s2c_handler(struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
|
||||
{
|
||||
switch (stream->server_state)
|
||||
{
|
||||
case SS_BEGIN:
|
||||
if (stream->info.version == SOCKS_VERSION_5 && socks5_method_replay(payload, payload_len) == 0)
|
||||
{
|
||||
stream->server_state = SS_SUB;
|
||||
}
|
||||
else if (stream->info.version == SOCKS_VERSION_4 && socks4_replay(payload, payload_len) == 0)
|
||||
{
|
||||
stream->server_state = SS_END;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->server_state = SS_FAILED;
|
||||
//printf("socks error, s2c beign\n");
|
||||
}
|
||||
break;
|
||||
case SS_SUB:
|
||||
if (socks5_replay(payload, payload_len) == 0)
|
||||
{
|
||||
stream->server_state = SS_END;
|
||||
}
|
||||
else if (socks5_pass_reply(payload, payload_len) != 0)
|
||||
{
|
||||
stream->server_state = SS_FAILED;
|
||||
|
||||
//printf("socks error, s2c sub\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//error!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int socks_process(struct socks_decoder_info *socks_decoder_info, struct session *sess, struct socks_tunnel_stream *stream, const char *payload, size_t payload_len)
|
||||
{
|
||||
if (payload_len == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(stream->state)
|
||||
{
|
||||
case STATE_INIT:
|
||||
if (socks_rough_rec(payload, payload_len) < 0)
|
||||
{
|
||||
stream->state = STATE_FAILED;
|
||||
return -1;
|
||||
}
|
||||
stream->state = STATE_OPENING;
|
||||
[[fallthrough]];//continue execute STATE_OPENING
|
||||
case STATE_OPENING:
|
||||
switch (session_get_flow_type(sess))
|
||||
{
|
||||
case FLOW_TYPE_C2S:
|
||||
c2s_handler(stream, payload, payload_len);
|
||||
break;
|
||||
case FLOW_TYPE_S2C:
|
||||
s2c_handler(stream, payload, payload_len);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream->client_state == SS_END && stream->server_state == SS_END)
|
||||
{
|
||||
if (session_mq_publish_message(sess, socks_decoder_info->socks_decoder_topic_id, &stream->info) < 0)
|
||||
{
|
||||
STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "session_mq_publish_message OPENING failed");
|
||||
}
|
||||
stellar_session_plugin_dettach_current_session(sess);
|
||||
}
|
||||
else if (stream->client_state == SS_FAILED || stream->server_state == SS_FAILED)
|
||||
{
|
||||
//not a socks proxy
|
||||
stream->state = STATE_FAILED;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void socks_decoder_on_message(struct session *sess, int topic_id, const void *msg, void *per_session_ctx, void *plugin_env)
|
||||
{
|
||||
UNUSED(per_session_ctx);
|
||||
UNUSED(topic_id);
|
||||
UNUSED(msg);
|
||||
|
||||
struct socks_decoder_info *socks_decoder_info = (struct socks_decoder_info *)plugin_env;
|
||||
struct socks_tunnel_stream *socks_tunel_stream = (struct socks_tunnel_stream *)session_exdata_get(sess, socks_decoder_info->sess_exdata_idx);
|
||||
const char *payload = NULL;
|
||||
size_t payload_len = 0;
|
||||
const struct packet *pkt = NULL;
|
||||
|
||||
if (socks_tunel_stream == NULL)
|
||||
{
|
||||
socks_tunel_stream = (struct socks_tunnel_stream *)calloc(1, sizeof(struct socks_tunnel_stream));
|
||||
session_exdata_set(sess, socks_decoder_info->sess_exdata_idx, socks_tunel_stream);
|
||||
}
|
||||
|
||||
pkt = session_get0_current_packet(sess);
|
||||
payload = packet_get_payload(pkt);
|
||||
payload_len = packet_get_payload_len(pkt);
|
||||
|
||||
if (socks_process(socks_decoder_info, sess, socks_tunel_stream, payload, payload_len) < 0)
|
||||
{
|
||||
stellar_session_plugin_dettach_current_session(sess);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void *socks_decoder_init(struct stellar *st)
|
||||
{
|
||||
struct socks_decoder_info *socks_decoder_info = (struct socks_decoder_info *)calloc(1, sizeof(struct socks_decoder_info));
|
||||
|
||||
socks_decoder_info->sess_exdata_idx= stellar_exdata_new_index(st, "SOCKS_DECODER_SESS_CTX", socks_decoder_session_exdata_free, NULL);
|
||||
|
||||
socks_decoder_info->st = st;
|
||||
socks_decoder_info->log_handle = stellar_get_logger(st);
|
||||
|
||||
socks_decoder_info->plugin_id = stellar_session_plugin_register(st, NULL, NULL, socks_decoder_info);
|
||||
if (socks_decoder_info->plugin_id < 0)
|
||||
{
|
||||
STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "stellar_session_plugin_register failed");
|
||||
goto ERROR;
|
||||
|
||||
}
|
||||
|
||||
socks_decoder_info->socks_decoder_topic_id = stellar_mq_create_topic(st, SOCKS_MESSAGE_TOPIC, NULL, NULL);
|
||||
if (socks_decoder_info->socks_decoder_topic_id < 0)
|
||||
{
|
||||
STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "stellar_session_mq_create_topic failed");
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
socks_decoder_info->tcp_topic_id = stellar_mq_get_topic_id(st, TOPIC_TCP_STREAM);
|
||||
if (socks_decoder_info->tcp_topic_id < 0)
|
||||
{
|
||||
STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "stellar_session_mq_get_topic_id failed");
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
if (stellar_session_mq_subscribe(st, socks_decoder_info->tcp_topic_id, socks_decoder_on_message, socks_decoder_info->plugin_id) < 0)
|
||||
{
|
||||
STELLAR_LOG_FATAL(socks_decoder_info->log_handle, SOCKS_LOG_MOUDLE, "stellar_session_mq_subscribe tcp_topic_id failed");
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
return socks_decoder_info;
|
||||
|
||||
ERROR:
|
||||
if (socks_decoder_info != NULL)
|
||||
{
|
||||
free(socks_decoder_info);
|
||||
}
|
||||
perror("socks_decoder init failed");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
extern "C" void socks_decoder_exit(void *plugin_env)
|
||||
{
|
||||
struct socks_decoder_info *socks_decoder_info = (struct socks_decoder_info *)plugin_env;
|
||||
|
||||
if (socks_decoder_info != NULL)
|
||||
{
|
||||
free(socks_decoder_info);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
Reference in New Issue
Block a user