627 lines
20 KiB
C
627 lines
20 KiB
C
|
|
#include "http_simple.h"
|
|
|
|
static char* g_useragent[] = {
|
|
"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0",
|
|
"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0",
|
|
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
|
|
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36",
|
|
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0",
|
|
"Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)",
|
|
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27",
|
|
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)",
|
|
"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
|
|
"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36",
|
|
"Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
|
|
"Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
|
|
};
|
|
|
|
static int g_useragent_index = -1;
|
|
|
|
typedef struct http_simple_local_data {
|
|
int has_sent_header;
|
|
int has_recv_header;
|
|
char *encode_buffer;
|
|
int host_matched;
|
|
char *recv_buffer;
|
|
int recv_buffer_size;
|
|
}http_simple_local_data;
|
|
|
|
void http_simple_local_data_init(http_simple_local_data* local) {
|
|
local->has_sent_header = 0;
|
|
local->has_recv_header = 0;
|
|
local->encode_buffer = NULL;
|
|
|
|
local->recv_buffer = malloc(0);
|
|
local->recv_buffer_size = 0;
|
|
|
|
local->host_matched = 0;
|
|
|
|
if (g_useragent_index == -1) {
|
|
g_useragent_index = xorshift128plus() % (sizeof(g_useragent) / sizeof(*g_useragent));
|
|
}
|
|
}
|
|
|
|
obfs * http_simple_new_obfs() {
|
|
obfs * self = new_obfs();
|
|
self->l_data = malloc(sizeof(http_simple_local_data));
|
|
http_simple_local_data_init((http_simple_local_data*)self->l_data);
|
|
return self;
|
|
}
|
|
|
|
void http_simple_dispose(obfs *self) {
|
|
http_simple_local_data *local = (http_simple_local_data*)self->l_data;
|
|
if (local->encode_buffer != NULL) {
|
|
free(local->encode_buffer);
|
|
local->encode_buffer = NULL;
|
|
}
|
|
free(local);
|
|
dispose_obfs(self);
|
|
}
|
|
|
|
char http_simple_hex(char c) {
|
|
if (c < 10) return c + '0';
|
|
return c - 10 + 'a';
|
|
}
|
|
|
|
int get_data_from_http_header(char *data, char **outdata) {
|
|
char *delim = "\r\n";
|
|
char *delim_hex = "%";
|
|
int outlength = 0;
|
|
|
|
char *buf = *outdata;
|
|
char *p_line;
|
|
p_line = strtok(data, delim);
|
|
|
|
//while(p_line)
|
|
{
|
|
char *p_hex;
|
|
|
|
p_hex = strtok(p_line, delim_hex);
|
|
|
|
while((p_hex = strtok(NULL, delim_hex)))
|
|
{
|
|
char hex = 0;
|
|
|
|
if(strlen(p_hex) <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if(strlen(p_hex) > 2)
|
|
{
|
|
char *c_hex = (char*)malloc(2);
|
|
memcpy(c_hex, p_hex, 2);
|
|
hex = (char)strtol(c_hex, NULL, 16);
|
|
free(c_hex);
|
|
}
|
|
else
|
|
{
|
|
hex = (char)strtol(p_hex, NULL, 16);
|
|
}
|
|
|
|
outlength += 1;
|
|
buf = (char*)realloc(buf, outlength);
|
|
buf[outlength - 1] = hex;
|
|
}
|
|
|
|
//p_line = strtok(p_line, delim);
|
|
}
|
|
*outdata = buf;
|
|
return outlength;
|
|
}
|
|
|
|
void get_host_from_http_header(char *data, char **host) {
|
|
char* data_begin = strstr(data, "Host: ");
|
|
|
|
if(data_begin == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
data_begin += 6;
|
|
char* data_end = strstr(data_begin, "\r\n");
|
|
char* data_end_port = strstr(data_begin, ":");
|
|
|
|
int host_length = 0;
|
|
|
|
if(data_end_port != NULL)
|
|
{
|
|
host_length = data_end_port - data_begin;
|
|
}
|
|
else
|
|
{
|
|
host_length = data_end - data_begin;
|
|
}
|
|
|
|
if(host_length <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
memset(*host, 0x00, 1024);
|
|
memcpy(*host, data_begin, host_length);
|
|
}
|
|
|
|
void http_simple_encode_head(http_simple_local_data *local, char *data, int datalength) {
|
|
if (local->encode_buffer == NULL) {
|
|
local->encode_buffer = (char*)malloc(datalength * 3 + 1);
|
|
}
|
|
int pos = 0;
|
|
for (; pos < datalength; ++pos) {
|
|
local->encode_buffer[pos * 3] = '%';
|
|
local->encode_buffer[pos * 3 + 1] = http_simple_hex(((unsigned char)data[pos] >> 4));
|
|
local->encode_buffer[pos * 3 + 2] = http_simple_hex(data[pos] & 0xF);
|
|
}
|
|
local->encode_buffer[pos * 3] = 0;
|
|
}
|
|
|
|
int http_simple_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) {
|
|
char *encryptdata = *pencryptdata;
|
|
http_simple_local_data *local = (http_simple_local_data*)self->l_data;
|
|
if (local->has_sent_header) {
|
|
return datalength;
|
|
}
|
|
char hosts[1024];
|
|
char * phost[128];
|
|
int host_num = 0;
|
|
int pos;
|
|
char hostport[128];
|
|
int head_size = self->server.head_len + (xorshift128plus() & 0x3F);
|
|
int outlength;
|
|
char * out_buffer = (char*)malloc(datalength + 2048);
|
|
char * body_buffer = NULL;
|
|
if (head_size > datalength)
|
|
head_size = datalength;
|
|
http_simple_encode_head(local, encryptdata, head_size);
|
|
if (self->server.param && strlen(self->server.param) == 0)
|
|
self->server.param = NULL;
|
|
strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts);
|
|
phost[host_num++] = hosts;
|
|
for (pos = 0; hosts[pos]; ++pos) {
|
|
if (hosts[pos] == ',') {
|
|
phost[host_num++] = &hosts[pos + 1];
|
|
hosts[pos] = 0;
|
|
} else if (hosts[pos] == '#') {
|
|
char * body_pointer = &hosts[pos + 1];
|
|
char * p;
|
|
int trans_char = 0;
|
|
p = body_buffer = (char*)malloc(2048);
|
|
for ( ; *body_pointer; ++body_pointer) {
|
|
if (*body_pointer == '\\') {
|
|
trans_char = 1;
|
|
continue;
|
|
} else if (*body_pointer == '\n') {
|
|
*p = '\r';
|
|
*++p = '\n';
|
|
continue;
|
|
}
|
|
if (trans_char) {
|
|
if (*body_pointer == '\\' ) {
|
|
*p = '\\';
|
|
} else if (*body_pointer == 'n' ) {
|
|
*p = '\r';
|
|
*++p = '\n';
|
|
} else {
|
|
*p = '\\';
|
|
*p = *body_pointer;
|
|
}
|
|
trans_char = 0;
|
|
} else {
|
|
*p = *body_pointer;
|
|
}
|
|
++p;
|
|
}
|
|
*p = 0;
|
|
hosts[pos] = 0;
|
|
break;
|
|
}
|
|
}
|
|
host_num = xorshift128plus() % host_num;
|
|
if (self->server.port == 80)
|
|
sprintf(hostport, "%s", phost[host_num]);
|
|
else
|
|
sprintf(hostport, "%s:%d", phost[host_num], self->server.port);
|
|
if (body_buffer) {
|
|
sprintf(out_buffer,
|
|
"GET /%s HTTP/1.1\r\n"
|
|
"Host: %s\r\n"
|
|
"%s\r\n\r\n",
|
|
local->encode_buffer,
|
|
hostport,
|
|
body_buffer);
|
|
} else {
|
|
sprintf(out_buffer,
|
|
"GET /%s HTTP/1.1\r\n"
|
|
"Host: %s\r\n"
|
|
"User-Agent: %s\r\n"
|
|
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
|
|
"Accept-Language: en-US,en;q=0.8\r\n"
|
|
"Accept-Encoding: gzip, deflate\r\n"
|
|
"DNT: 1\r\n"
|
|
"Connection: keep-alive\r\n"
|
|
"\r\n",
|
|
local->encode_buffer,
|
|
hostport,
|
|
g_useragent[g_useragent_index]
|
|
);
|
|
}
|
|
//LOGI("http header: %s", out_buffer);
|
|
outlength = strlen(out_buffer);
|
|
memmove(out_buffer + outlength, encryptdata + head_size, datalength - head_size);
|
|
outlength += datalength - head_size;
|
|
local->has_sent_header = 1;
|
|
if (*capacity < outlength) {
|
|
*pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2);
|
|
encryptdata = *pencryptdata;
|
|
}
|
|
memmove(encryptdata, out_buffer, outlength);
|
|
free(out_buffer);
|
|
if (body_buffer != NULL)
|
|
free(body_buffer);
|
|
if (local->encode_buffer != NULL) {
|
|
free(local->encode_buffer);
|
|
local->encode_buffer = NULL;
|
|
}
|
|
return outlength;
|
|
}
|
|
|
|
int http_simple_server_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) {
|
|
char *encryptdata = *pencryptdata;
|
|
http_simple_local_data *local = (http_simple_local_data*)self->l_data;
|
|
if (local->has_sent_header) {
|
|
return datalength;
|
|
}
|
|
int outlength;
|
|
char * out_buffer = (char*)malloc(datalength + 2048);
|
|
|
|
time_t now;
|
|
struct tm *tm_now;
|
|
char datetime[200];
|
|
|
|
time(&now);
|
|
tm_now = localtime(&now);
|
|
strftime(datetime, 200, "%a, %d %b %Y %H:%M:%S GMT", tm_now);
|
|
|
|
sprintf(out_buffer,
|
|
"HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Encoding: gzip\r\nContent-Type: text/html\r\nDate: "
|
|
"%s"
|
|
"\r\nServer: nginx\r\nVary: Accept-Encoding\r\n\r\n",
|
|
datetime);
|
|
|
|
outlength = strlen(out_buffer);
|
|
memmove(out_buffer + outlength, encryptdata, datalength);
|
|
outlength += datalength;
|
|
|
|
local->has_sent_header = 1;
|
|
if (*capacity < outlength) {
|
|
*pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2);
|
|
encryptdata = *pencryptdata;
|
|
}
|
|
memmove(encryptdata, out_buffer, outlength);
|
|
free(out_buffer);
|
|
return outlength;
|
|
}
|
|
|
|
int http_simple_client_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) {
|
|
char *encryptdata = *pencryptdata;
|
|
http_simple_local_data *local = (http_simple_local_data*)self->l_data;
|
|
*needsendback = 0;
|
|
if (local->has_recv_header) {
|
|
return datalength;
|
|
}
|
|
char* data_begin = strstr(encryptdata, "\r\n\r\n");
|
|
if (data_begin) {
|
|
int outlength;
|
|
data_begin += 4;
|
|
local->has_recv_header = 1;
|
|
outlength = datalength - (data_begin - encryptdata);
|
|
memmove(encryptdata, data_begin, outlength);
|
|
return outlength;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int http_simple_server_decode(obfs *self, char **pencryptdata, int datalength, size_t* capacity, int *needsendback) {
|
|
char *encryptdata = *pencryptdata;
|
|
http_simple_local_data *local = (http_simple_local_data*)self->l_data;
|
|
*needsendback = 0;
|
|
if (local->has_recv_header) {
|
|
return datalength;
|
|
}
|
|
|
|
if(datalength != 0)
|
|
{
|
|
local->recv_buffer = (char*)realloc(local->recv_buffer, local->recv_buffer_size + datalength);
|
|
memmove(local->recv_buffer + local->recv_buffer_size, encryptdata, datalength);
|
|
local->recv_buffer_size += datalength;
|
|
|
|
int outlength = local->recv_buffer_size;
|
|
if (*capacity < outlength) {
|
|
*pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2);
|
|
encryptdata = *pencryptdata;
|
|
}
|
|
memcpy(encryptdata, local->recv_buffer, local->recv_buffer_size);
|
|
}
|
|
|
|
if(local->recv_buffer_size > 10)
|
|
{
|
|
if(strstr(local->recv_buffer, "GET /") == local->recv_buffer || strstr(local->recv_buffer, "POST /") == local->recv_buffer)
|
|
{
|
|
if(local->recv_buffer_size > 65536)
|
|
{
|
|
free(local->recv_buffer);
|
|
local->recv_buffer = malloc(0);
|
|
local->recv_buffer_size = 0;
|
|
local->has_sent_header = 1;
|
|
local->has_recv_header = 1;
|
|
LOGE("http_simple: over size");
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
free(local->recv_buffer);
|
|
local->recv_buffer = malloc(0);
|
|
local->recv_buffer_size = 0;
|
|
local->has_sent_header = 1;
|
|
local->has_recv_header = 1;
|
|
LOGE("http_simple: not match begin");
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOGE("http_simple: too short");
|
|
local->has_sent_header = 1;
|
|
local->has_recv_header = 1;
|
|
return -1;
|
|
}
|
|
|
|
char* data_begin = strstr(encryptdata, "\r\n\r\n");
|
|
if (data_begin) {
|
|
int outlength;
|
|
char *ret_buf = (char*)malloc(*capacity);
|
|
memset(ret_buf, 0x00, *capacity);
|
|
int ret_buf_len = 0;
|
|
ret_buf_len = get_data_from_http_header(encryptdata, &ret_buf);
|
|
|
|
if (self->server.param && strlen(self->server.param) == 0)
|
|
{
|
|
self->server.param = NULL;
|
|
}
|
|
else
|
|
{
|
|
if(local->host_matched == 0)
|
|
{
|
|
char *host = (char*)malloc(1024);
|
|
get_host_from_http_header(local->recv_buffer, &host);
|
|
char hosts[1024];
|
|
char * phost[128];
|
|
int host_num = 0;
|
|
int pos = 0;
|
|
int is_match = 0;
|
|
char * body_buffer = NULL;
|
|
strncpy(hosts, self->server.param, sizeof hosts);
|
|
phost[host_num++] = hosts;
|
|
|
|
for (pos = 0; hosts[pos]; ++pos) {
|
|
if (hosts[pos] == ',') {
|
|
phost[host_num++] = &hosts[pos + 1];
|
|
hosts[pos] = 0;
|
|
} else if (hosts[pos] == '#') {
|
|
char * body_pointer = &hosts[pos + 1];
|
|
char * p;
|
|
int trans_char = 0;
|
|
p = body_buffer = (char*)malloc(2048);
|
|
for ( ; *body_pointer; ++body_pointer) {
|
|
if (*body_pointer == '\\') {
|
|
trans_char = 1;
|
|
continue;
|
|
} else if (*body_pointer == '\n') {
|
|
*p = '\r';
|
|
*++p = '\n';
|
|
continue;
|
|
}
|
|
if (trans_char) {
|
|
if (*body_pointer == '\\' ) {
|
|
*p = '\\';
|
|
} else if (*body_pointer == 'n' ) {
|
|
*p = '\r';
|
|
*++p = '\n';
|
|
} else {
|
|
*p = '\\';
|
|
*p = *body_pointer;
|
|
}
|
|
trans_char = 0;
|
|
} else {
|
|
*p = *body_pointer;
|
|
}
|
|
++p;
|
|
}
|
|
*p = 0;
|
|
hosts[pos] = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
for(pos = 0; pos < host_num; pos++)
|
|
{
|
|
if(strcmp(phost[pos], host) == 0)
|
|
{
|
|
is_match = 1;
|
|
local->host_matched = 1;
|
|
}
|
|
}
|
|
|
|
if(is_match == 0)
|
|
{
|
|
free(local->recv_buffer);
|
|
local->recv_buffer = malloc(0);
|
|
local->recv_buffer_size = 0;
|
|
local->has_sent_header = 1;
|
|
local->has_recv_header = 1;
|
|
LOGE("http_simple: not match host, host: %s", host);
|
|
return -1;
|
|
}
|
|
|
|
free(host);
|
|
}
|
|
}
|
|
|
|
if(ret_buf_len <= 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
data_begin += 4;
|
|
local->has_recv_header = 1;
|
|
|
|
ret_buf = (char*)realloc(ret_buf, ret_buf_len + datalength - (data_begin - encryptdata));
|
|
outlength = ret_buf_len + datalength - (data_begin - encryptdata);
|
|
|
|
memcpy(ret_buf + ret_buf_len, data_begin, datalength - (data_begin - encryptdata));
|
|
|
|
if (*capacity < outlength) {
|
|
*pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2);
|
|
encryptdata = *pencryptdata;
|
|
}
|
|
|
|
memcpy(encryptdata, ret_buf, outlength);
|
|
free(ret_buf);
|
|
return outlength;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void boundary(char result[])
|
|
{
|
|
char *str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
int i,lstr;
|
|
char ss[3] = {0};
|
|
lstr = strlen(str);
|
|
srand((unsigned int)time((time_t *)NULL));
|
|
for(i = 0; i < 32; ++i)
|
|
{
|
|
sprintf(ss, "%c", str[(rand()%lstr)]);
|
|
strcat(result, ss);
|
|
}
|
|
}
|
|
|
|
int http_post_client_encode(obfs *self, char **pencryptdata, int datalength, size_t* capacity) {
|
|
char *encryptdata = *pencryptdata;
|
|
http_simple_local_data *local = (http_simple_local_data*)self->l_data;
|
|
if (local->has_sent_header) {
|
|
return datalength;
|
|
}
|
|
char hosts[1024];
|
|
char * phost[128];
|
|
int host_num = 0;
|
|
int pos;
|
|
char hostport[128];
|
|
int head_size = self->server.head_len + (xorshift128plus() & 0x3F);
|
|
int outlength;
|
|
char * out_buffer = (char*)malloc(datalength + 2048);
|
|
char * body_buffer = NULL;
|
|
if (head_size > datalength)
|
|
head_size = datalength;
|
|
http_simple_encode_head(local, encryptdata, head_size);
|
|
if (self->server.param && strlen(self->server.param) == 0)
|
|
self->server.param = NULL;
|
|
strncpy(hosts, self->server.param ? self->server.param : self->server.host, sizeof hosts);
|
|
phost[host_num++] = hosts;
|
|
for (pos = 0; hosts[pos]; ++pos) {
|
|
if (hosts[pos] == ',') {
|
|
phost[host_num++] = &hosts[pos + 1];
|
|
hosts[pos] = 0;
|
|
} else if (hosts[pos] == '#') {
|
|
char * body_pointer = &hosts[pos + 1];
|
|
char * p;
|
|
int trans_char = 0;
|
|
p = body_buffer = (char*)malloc(2048);
|
|
for ( ; *body_pointer; ++body_pointer) {
|
|
if (*body_pointer == '\\') {
|
|
trans_char = 1;
|
|
continue;
|
|
} else if (*body_pointer == '\n') {
|
|
*p = '\r';
|
|
*++p = '\n';
|
|
continue;
|
|
}
|
|
if (trans_char) {
|
|
if (*body_pointer == '\\' ) {
|
|
*p = '\\';
|
|
} else if (*body_pointer == 'n' ) {
|
|
*p = '\r';
|
|
*++p = '\n';
|
|
} else {
|
|
*p = '\\';
|
|
*p = *body_pointer;
|
|
}
|
|
trans_char = 0;
|
|
} else {
|
|
*p = *body_pointer;
|
|
}
|
|
++p;
|
|
}
|
|
*p = 0;
|
|
hosts[pos] = 0;
|
|
break;
|
|
}
|
|
}
|
|
host_num = xorshift128plus() % host_num;
|
|
if (self->server.port == 80)
|
|
sprintf(hostport, "%s", phost[host_num]);
|
|
else
|
|
sprintf(hostport, "%s:%d", phost[host_num], self->server.port);
|
|
if (body_buffer) {
|
|
sprintf(out_buffer,
|
|
"POST /%s HTTP/1.1\r\n"
|
|
"Host: %s\r\n"
|
|
"%s\r\n\r\n",
|
|
local->encode_buffer,
|
|
hostport,
|
|
body_buffer);
|
|
} else {
|
|
char result[33] = {0};
|
|
boundary(result);
|
|
sprintf(out_buffer,
|
|
"POST /%s HTTP/1.1\r\n"
|
|
"Host: %s\r\n"
|
|
"User-Agent: %s\r\n"
|
|
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
|
|
"Accept-Language: en-US,en;q=0.8\r\n"
|
|
"Accept-Encoding: gzip, deflate\r\n"
|
|
"Content-Type: multipart/form-data; boundary=%s\r\n"
|
|
"DNT: 1\r\n"
|
|
"Connection: keep-alive\r\n"
|
|
"\r\n",
|
|
local->encode_buffer,
|
|
hostport,
|
|
g_useragent[g_useragent_index],
|
|
result
|
|
);
|
|
}
|
|
//LOGI("http header: %s", out_buffer);
|
|
outlength = strlen(out_buffer);
|
|
memmove(out_buffer + outlength, encryptdata + head_size, datalength - head_size);
|
|
outlength += datalength - head_size;
|
|
local->has_sent_header = 1;
|
|
if (*capacity < outlength) {
|
|
*pencryptdata = (char*)realloc(*pencryptdata, *capacity = outlength * 2);
|
|
encryptdata = *pencryptdata;
|
|
}
|
|
memmove(encryptdata, out_buffer, outlength);
|
|
free(out_buffer);
|
|
if (body_buffer != NULL)
|
|
free(body_buffer);
|
|
if (local->encode_buffer != NULL) {
|
|
free(local->encode_buffer);
|
|
local->encode_buffer = NULL;
|
|
}
|
|
return outlength;
|
|
}
|