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 "qemu-common.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 24 typedef 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 32 typedef struct Connection { 33 ProxyConnection root[1]; 34 ConnectorState state; 35 } Connection; 36 37 38 static void 39 connection_free( ProxyConnection* root ) 40 { 41 proxy_connection_done(root); 42 qemu_free(root); 43 } 44 45 46 47 #define HTTP_VERSION "1.1" 48 49 static int 50 connection_init( Connection* conn ) 51 { 52 HttpService* service = (HttpService*) conn->root->service; 53 ProxyConnection* root = conn->root; 54 stralloc_t* str = root->str; 55 56 proxy_connection_rewind(root); 57 stralloc_add_format(str, "CONNECT %s HTTP/" HTTP_VERSION "\r\n", 58 sock_address_to_string(&root->address)); 59 60 stralloc_add_bytes(str, service->footer, service->footer_len); 61 62 if (!socket_connect( root->socket, &service->server_addr )) { 63 /* immediate connection ?? */ 64 conn->state = STATE_SEND_HEADER; 65 PROXY_LOG("%s: immediate connection", root->name); 66 } 67 else { 68 if (errno == EINPROGRESS || errno == EWOULDBLOCK) { 69 conn->state = STATE_CONNECTING; 70 PROXY_LOG("%s: connecting", root->name); 71 } 72 else { 73 PROXY_LOG("%s: cannot connect to proxy: %s", root->name, errno_str); 74 return -1; 75 } 76 } 77 return 0; 78 } 79 80 81 static void 82 connection_select( ProxyConnection* root, 83 ProxySelect* sel ) 84 { 85 unsigned flags; 86 Connection* conn = (Connection*)root; 87 88 switch (conn->state) { 89 case STATE_RECEIVE_ANSWER_LINE1: 90 case STATE_RECEIVE_ANSWER_LINE2: 91 flags = PROXY_SELECT_READ; 92 break; 93 94 case STATE_CONNECTING: 95 case STATE_SEND_HEADER: 96 flags = PROXY_SELECT_WRITE; 97 break; 98 99 default: 100 flags = 0; 101 }; 102 proxy_select_set(sel, root->socket, flags); 103 } 104 105 static void 106 connection_poll( ProxyConnection* root, 107 ProxySelect* sel ) 108 { 109 DataStatus ret = DATA_NEED_MORE; 110 Connection* conn = (Connection*)root; 111 int fd = root->socket; 112 113 if (!proxy_select_poll(sel, fd)) 114 return; 115 116 switch (conn->state) 117 { 118 case STATE_CONNECTING: 119 PROXY_LOG("%s: connected to http proxy, sending header", root->name); 120 conn->state = STATE_SEND_HEADER; 121 break; 122 123 case STATE_SEND_HEADER: 124 ret = proxy_connection_send(root, fd); 125 if (ret == DATA_COMPLETED) { 126 conn->state = STATE_RECEIVE_ANSWER_LINE1; 127 PROXY_LOG("%s: header sent, receiving first answer line", root->name); 128 } 129 break; 130 131 case STATE_RECEIVE_ANSWER_LINE1: 132 case STATE_RECEIVE_ANSWER_LINE2: 133 ret = proxy_connection_receive_line(root, root->socket); 134 if (ret == DATA_COMPLETED) { 135 if (conn->state == STATE_RECEIVE_ANSWER_LINE1) { 136 int http1, http2, codenum; 137 const char* line = root->str->s; 138 139 if ( sscanf(line, "HTTP/%d.%d %d", &http1, &http2, &codenum) != 3 ) { 140 PROXY_LOG( "%s: invalid answer from proxy: '%s'", 141 root->name, line ); 142 ret = DATA_ERROR; 143 break; 144 } 145 146 /* success is 2xx */ 147 if (codenum/2 != 100) { 148 PROXY_LOG( "%s: connection refused, error=%d", 149 root->name, codenum ); 150 proxy_connection_free( root, 0, PROXY_EVENT_CONNECTION_REFUSED ); 151 return; 152 } 153 PROXY_LOG("%s: receiving second answer line", root->name); 154 conn->state = STATE_RECEIVE_ANSWER_LINE2; 155 proxy_connection_rewind(root); 156 } else { 157 /* ok, we're connected */ 158 PROXY_LOG("%s: connection succeeded", root->name); 159 proxy_connection_free( root, 1, PROXY_EVENT_CONNECTED ); 160 } 161 } 162 break; 163 164 default: 165 PROXY_LOG("%s: invalid state for read event: %d", root->name, conn->state); 166 } 167 168 if (ret == DATA_ERROR) { 169 proxy_connection_free( root, 0, PROXY_EVENT_SERVER_ERROR ); 170 } 171 } 172 173 174 175 ProxyConnection* 176 http_connector_connect( HttpService* service, 177 SockAddress* address ) 178 { 179 Connection* conn; 180 int s; 181 182 s = socket_create_inet( SOCKET_STREAM ); 183 if (s < 0) 184 return NULL; 185 186 conn = qemu_mallocz(sizeof(*conn)); 187 if (conn == NULL) { 188 socket_close(s); 189 return NULL; 190 } 191 192 proxy_connection_init( conn->root, s, address, service->root, 193 connection_free, 194 connection_select, 195 connection_poll ); 196 197 if ( connection_init( conn ) < 0 ) { 198 connection_free( conn->root ); 199 return NULL; 200 } 201 202 return conn->root; 203 } 204