proxy_http_connector.c revision df7881f07f53b041dc0568be8528e9dbb74994cc
1/* Copyright (C) 2007-2008 The Android Open Source Project 2** 3** This software is licensed under the terms of the GNU General Public 4** License version 2, as published by the Free Software Foundation, and 5** may be copied, distributed, and modified under those terms. 6** 7** This program is distributed in the hope that it will be useful, 8** but WITHOUT ANY WARRANTY; without even the implied warranty of 9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10** GNU General Public License for more details. 11*/ 12#include "proxy_http_int.h" 13#include <errno.h> 14#include <stdio.h> 15#include <string.h> 16#include "vl.h" 17 18/* A HttpConnector implements a non-HTTP proxied connection 19 * through the CONNECT method. Many firewalls are configured 20 * to reject these for port 80, so these connections should 21 * use a HttpRewriter instead. 22 */ 23 24typedef enum { 25 STATE_NONE = 0, 26 STATE_CONNECTING, /* connecting to the server */ 27 STATE_SEND_HEADER, /* connected, sending header to the server */ 28 STATE_RECEIVE_ANSWER_LINE1, 29 STATE_RECEIVE_ANSWER_LINE2 /* connected, reading server's answer */ 30} ConnectorState; 31 32typedef struct Connection { 33 ProxyConnection root[1]; 34 ConnectorState state; 35} Connection; 36 37 38static void 39connection_free( ProxyConnection* root ) 40{ 41 proxy_connection_done(root); 42 qemu_free(root); 43} 44 45 46 47#define HTTP_VERSION "1.1" 48 49static int 50connection_init( Connection* conn ) 51{ 52 HttpService* service = (HttpService*) conn->root->service; 53 ProxyConnection* root = conn->root; 54 stralloc_t* str = root->str; 55 int ret; 56 uint32_t address = ntohl(root->address.sin_addr.s_addr); 57 int port = ntohs(root->address.sin_port); 58 59 proxy_connection_rewind(root); 60 stralloc_add_format(str, "CONNECT %d.%d.%d.%d:%d HTTP/" HTTP_VERSION "\r\n", 61 (address >> 24) & 0xff, (address >> 16) & 0xff, 62 (address >> 8) & 0xff, address & 0xff, port); 63 64 stralloc_add_bytes(str, service->footer, service->footer_len); 65 66 do { 67 ret = connect( root->socket, 68 (struct sockaddr*) &service->server_addr, 69 sizeof(service->server_addr) ); 70 } while (ret < 0 && socket_errno == EINTR); 71 72 if (ret == 0) { 73 /* immediate connection ?? */ 74 conn->state = STATE_SEND_HEADER; 75 PROXY_LOG("%s: immediate connection", root->name); 76 } 77 else { 78 if (socket_errno == EINPROGRESS || socket_errno == EWOULDBLOCK) { 79 conn->state = STATE_CONNECTING; 80 PROXY_LOG("%s: connecting", root->name); 81 } 82 else { 83 PROXY_LOG("%s: cannot connect to proxy: %s", root->name, socket_errstr()); 84 return -1; 85 } 86 } 87 return 0; 88} 89 90 91static void 92connection_select( ProxyConnection* root, 93 ProxySelect* sel ) 94{ 95 unsigned flags; 96 Connection* conn = (Connection*)root; 97 98 switch (conn->state) { 99 case STATE_RECEIVE_ANSWER_LINE1: 100 case STATE_RECEIVE_ANSWER_LINE2: 101 flags = PROXY_SELECT_READ; 102 break; 103 104 case STATE_CONNECTING: 105 case STATE_SEND_HEADER: 106 flags = PROXY_SELECT_WRITE; 107 break; 108 109 default: 110 flags = 0; 111 }; 112 proxy_select_set(sel, root->socket, flags); 113} 114 115static void 116connection_poll( ProxyConnection* root, 117 ProxySelect* sel ) 118{ 119 DataStatus ret = DATA_NEED_MORE; 120 Connection* conn = (Connection*)root; 121 int fd = root->socket; 122 123 if (!proxy_select_poll(sel, fd)) 124 return; 125 126 switch (conn->state) 127 { 128 case STATE_CONNECTING: 129 PROXY_LOG("%s: connected to http proxy, sending header", root->name); 130 conn->state = STATE_SEND_HEADER; 131 break; 132 133 case STATE_SEND_HEADER: 134 ret = proxy_connection_send(root, fd); 135 if (ret == DATA_COMPLETED) { 136 conn->state = STATE_RECEIVE_ANSWER_LINE1; 137 PROXY_LOG("%s: header sent, receiving first answer line", root->name); 138 } 139 break; 140 141 case STATE_RECEIVE_ANSWER_LINE1: 142 case STATE_RECEIVE_ANSWER_LINE2: 143 ret = proxy_connection_receive_line(root, root->socket); 144 if (ret == DATA_COMPLETED) { 145 if (conn->state == STATE_RECEIVE_ANSWER_LINE1) { 146 int http1, http2, codenum; 147 const char* line = root->str->s; 148 149 if ( sscanf(line, "HTTP/%d.%d %d", &http1, &http2, &codenum) != 3 ) { 150 PROXY_LOG( "%s: invalid answer from proxy: '%s'", 151 root->name, line ); 152 ret = DATA_ERROR; 153 break; 154 } 155 156 /* success is 2xx */ 157 if (codenum/2 != 100) { 158 PROXY_LOG( "%s: connection refused, error=%d", 159 root->name, codenum ); 160 proxy_connection_free( root, 0, PROXY_EVENT_CONNECTION_REFUSED ); 161 return; 162 } 163 PROXY_LOG("%s: receiving second answer line", root->name); 164 conn->state = STATE_RECEIVE_ANSWER_LINE2; 165 proxy_connection_rewind(root); 166 } else { 167 /* ok, we're connected */ 168 PROXY_LOG("%s: connection succeeded", root->name); 169 proxy_connection_free( root, 1, PROXY_EVENT_CONNECTED ); 170 } 171 } 172 break; 173 174 default: 175 PROXY_LOG("%s: invalid state for read event: %d", root->name, conn->state); 176 } 177 178 if (ret == DATA_ERROR) { 179 proxy_connection_free( root, 0, PROXY_EVENT_SERVER_ERROR ); 180 } 181} 182 183 184 185ProxyConnection* 186http_connector_connect( HttpService* service, 187 struct sockaddr_in* address ) 188{ 189 Connection* conn; 190 int s; 191 192 s = socket(AF_INET, SOCK_STREAM, 0); 193 if (s < 0) 194 return NULL; 195 196 conn = qemu_mallocz(sizeof(*conn)); 197 if (conn == NULL) { 198 socket_close(s); 199 return NULL; 200 } 201 202 proxy_connection_init( conn->root, s, address, service->root, 203 connection_free, 204 connection_select, 205 connection_poll ); 206 207 if ( connection_init( conn ) < 0 ) { 208 connection_free( conn->root ); 209 return NULL; 210 } 211 212 return conn->root; 213} 214