static inline ssize_t http_method (int fd, Http_destination *dest, Http_method method, ssize_t length) { char str[1024]; /* FIXME: possible buffer overflow */ Http_request *request; ssize_t n; if (fd == -1) { log_error ("http_method: fd == -1"); return -1; } n = 0; if (dest->proxy_name != NULL) n = sprintf (str, "http://%s:%d", dest->host_name, dest->host_port); sprintf (str + n, "/index.html?crap=%ld", time (NULL)); request = http_create_request (method, str, 1, 1); if (request == NULL) return -1; sprintf (str, "%s:%d", dest->host_name, dest->host_port); http_add_header (&request->header, "Host", str); if (length >= 0) { sprintf (str, "%d", length); http_add_header (&request->header, "Content-Length", str); } http_add_header (&request->header, "Connection", "close"); if (dest->proxy_authorization) { http_add_header (&request->header, "Proxy-Authorization", dest->proxy_authorization); } if (dest->user_agent) { http_add_header (&request->header, "User-Agent", dest->user_agent); } n = http_write_request (fd, request); http_destroy_request (request); return n; }
/* 函数说明:隧道服务端接收一个隧道连接: 一个隧道连接包含两个TCP连接in_fd(接收数据),out_fd(发送数据); 两个TCP连接如何建立,其流程如此: 在接收TCP连接时,如果还没成功接收到一个TCP连接(in_fd&&out_fd==-1)则测试时间为无穷; 否则只等待10s时间来接收下一个TCP连接; 1、首先接收一个TCP连接: 2、然后解析一个HTTP request,如果method是 -1,转到1 3、如果是post ,put,则将in_fd赋值,转到1; 4、如果是get,则将out_fd赋值,转到1; 5、其他,关闭连接,转到1; */ int tunnel_accept(Tunnel *tunnel) { if(tunnel->in_fd!=INVALID_SOCKET&&tunnel->out_fd!=INVALID_SOCKET) { return 0; } //假如该服务端正在连接中,不接受; /* 循环说明: 终止条件:tunnel->in_fd!=INVALID_SOCKET&&tunnel->out_fd!=INVALID_SOCKET 即已建立起两条TCP连接,一条用于发送、一条用于接收数据! 步骤: 1、accept接收一个tcp连接; 2、从该连接中解析一个http request数据包:http_parse_request; 3、根据request数据包中method做出不同的处理:switch(method): case -1,非法数据包,关闭连接; case POST \PUT : */ while(tunnel->in_fd==INVALID_SOCKET||tunnel->out_fd==INVALID_SOCKET) { struct sockaddr_in addr; Http_request *request; //fd_set fs; int m; //timeval t; int len,n; SOCKET s; /* t.tv_sec=2; t.tv_usec=0; FD_ZERO(&fs); FD_SET(tunnel->server_socket,&fs); n=select(0,&fs,NULL,NULL,((tunnel->in_fd!=INVALID_SOCKET||tunnel->out_fd!=INVALID_SOCKET)?&t:NULL)); if(n==SOCKET_ERROR) { return -1; } else if(n==0)break; */ /* 测试服务器套接字是否有connect请求到来; 如果已经连接上一个TCP连接了,就只等ACCEPT_TIME; 否则则一直等待下去; */ len=sizeof(addr); //printf("进入accept函数\n"); s=accept(tunnel->server_socket,(struct sockaddr *)&addr,&len); //printf("accept 函数!错误码:%d\n",WSAGetLastError()); if(s==INVALID_SOCKET)return -1; m=http_parse_request(s,&request); //从连接套接字s读取http request; //output(request); if(m<=0)return m; if(request->method==-1) { closesocket(s); } else if(request->method==HTTP_POST||request->method==HTTP_PUT) { if(tunnel->in_fd==INVALID_SOCKET) { printf("输入数据TCP连接已经建立,远程主机为:[%s]:[%hu]\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port)); tunnel->in_fd=s; // tunnel->in_total_raw+=m; } else closesocket(s); } /* 客户端是准备传数据过来,即Post和Put操作; 如果服务端输入in_fd已经处于连接状态了,关闭此次TCP连接; 否则将其赋值给in_fd; */ else if(request->method==HTTP_GET) { if(tunnel->out_fd==INVALID_SOCKET) { char str[1024]; tunnel->out_fd=s; sprintf(str,"HTTP/1.1 200 OK\r\n" "Content-Length: %d\r\n" "Connection: close\r\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache, no-store, must-revalidate\r\n" "Expires: 0\r\n" "Content-Type: text/html\r\n" "\r\n",tunnel->content_length+1); if(write_all(tunnel->out_fd,str,strlen(str))<=0) { closesocket(tunnel->out_fd); tunnel->out_fd=INVALID_SOCKET; } else { printf("输出数据TCP连接已经建立,远程主机为:[%s]:[%hu]\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port)); tunnel->bytes=0; tunnel->buf_len=0; tunnel->buf_ptr=tunnel->buf; //tunnel->out_total_raw+=strlen(str); } } else closesocket(s); } else closesocket(s); http_destroy_request(request); } if(tunnel->in_fd==INVALID_SOCKET||tunnel->out_fd==INVALID_SOCKET) //隧道连接未能成功建立只建立了一半,断开隧道连接; { if(tunnel->in_fd!=INVALID_SOCKET)closesocket(tunnel->in_fd); tunnel->in_fd=INVALID_SOCKET; tunnel_out_disconnect(tunnel); return -1; } return 0; }
ssize_t http_parse_request (int fd, Http_request **request_) { Http_request *request; char *data; size_t len; ssize_t n; *request_ = NULL; request = malloc (sizeof (Http_request)); if (request == NULL) { log_error ("http_parse_request: out of memory"); return -1; } request->method = -1; request->uri = NULL; request->major_version = -1; request->minor_version = -1; request->header = NULL; n = read_until (fd, ' ', &data); if (n <= 0) { free (request); return n; } request->method = http_string_to_method (data, n - 1); if (request->method == -1) { log_error ("http_parse_request: expected an HTTP method"); free (data); free (request); return -1; } data[n - 1] = 0; log_verbose ("http_parse_request: method = \"%s\"", data); free (data); len = n; n = read_until (fd, ' ', &data); if (n <= 0) { free (request); return n; } data[n - 1] = 0; request->uri = data; len += n; log_verbose ("http_parse_request: uri = \"%s\"", request->uri); n = read_until (fd, '/', &data); if (n <= 0) { http_destroy_request (request); return n; } else if (n != 5 || memcmp (data, "HTTP", 4) != 0) { log_error ("http_parse_request: expected \"HTTP\""); free (data); http_destroy_request (request); return -1; } free (data); len = n; n = read_until (fd, '.', &data); if (n <= 0) { http_destroy_request (request); return n; } data[n - 1] = 0; request->major_version = atoi (data); log_verbose ("http_parse_request: major version = %d", request->major_version); free (data); len += n; n = read_until (fd, '\r', &data); if (n <= 0) { http_destroy_request (request); return n; } data[n - 1] = 0; request->minor_version = atoi (data); log_verbose ("http_parse_request: minor version = %d", request->minor_version); free (data); len += n; n = read_until (fd, '\n', &data); if (n <= 0) { http_destroy_request (request); return n; } free (data); if (n != 1) { log_error ("http_parse_request: invalid line ending"); http_destroy_request (request); return -1; } len += n; n = parse_header (fd, &request->header); if (n <= 0) { http_destroy_request (request); return n; } len += n; *request_ = request; return len; }