#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; }