1 /*
2 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2005, 2008, 2009, 2010, 2011,
3 * 2012, 2013, 2014, 2021
4 * Inferno Nettverk A/S, Norway. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. The above copyright notice, this list of conditions and the following
10 * disclaimer must appear in all copies of the software, derivative works
11 * or modified versions, and any portions thereof, aswell as in all
12 * supporting documentation.
13 * 2. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by
16 * Inferno Nettverk A/S, Norway.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * Inferno Nettverk A/S requests users of this software to return to
32 *
33 * Software Distribution Coordinator or sdc@inet.no
34 * Inferno Nettverk A/S
35 * Oslo Research Park
36 * Gaustadall�en 21
37 * NO-0349 Oslo
38 * Norway
39 *
40 * any improvements or extensions that they make and grant Inferno Nettverk A/S
41 * the rights to redistribute these changes.
42 *
43 */
44
45 #include "common.h"
46
47 static const char rcsid[] =
48 "$Id: httpproxy.c,v 1.73.4.2.6.2 2021/02/02 19:34:11 karls Exp $";
49
50 int
httpproxy_negotiate(s,packet,emsg,emsglen)51 httpproxy_negotiate(s, packet, emsg, emsglen)
52 int s;
53 socks_t *packet;
54 char *emsg;
55 size_t emsglen;
56 {
57 const char *function = "httpproxy_negotiate()";
58 char buf[MAXHOSTNAMELEN + 512] /* The + 512 is for http babble. */,
59 visbuf[sizeof(buf) * 4 + 1], *p;
60 char host[MAXSOCKSHOSTSTRING];
61 int checked, eof;
62 ssize_t rc;
63 size_t len, readsofar;
64 struct sockaddr_storage addr;
65 socklen_t addrlen;
66
67 slog(LOG_DEBUG, "%s", function);
68
69 sockshost2string(&packet->req.host, host, sizeof(host));
70
71 /*
72 * replace the dot that sockshost2string uses to separate port from host
73 * with http's ':'.
74 */
75 if ((p = strrchr(host, '.')) == NULL) {
76 snprintf(emsg, emsglen,
77 "did not find portnumber separator ('.') in string \"%s\"",
78 host);
79
80 swarnx("%s: %s", function, emsg);
81 return -1;
82 }
83
84 *p = ':';
85
86 len = snprintf(buf, sizeof(buf),
87 "CONNECT %s %s\r\n"
88 "User-agent: %s/client v%s\r\n"
89 "\r\n",
90 host,
91 proxyprotocol2string(packet->req.version),
92 PRODUCT,
93 VERSION);
94
95 slog(LOG_NEGOTIATE, "%s: sending to server: %s",
96 function, str2vis(buf, len, visbuf, sizeof(visbuf)));
97
98 if ((rc = socks_sendton(s, buf, len, len, 0, NULL, 0, NULL, NULL))
99 != (ssize_t)len) {
100 snprintf(emsg, emsglen,
101 "could not send request to proxy server. Sent %ld/%lu: %s",
102 (long)rc, (unsigned long)len, strerror(errno));
103
104 return -1;
105 }
106
107 /*
108 * read til we get the eof response so there's no junk left in buffer
109 * for client, then return the response code.
110 */
111 eof = checked = readsofar = 0;
112 do {
113 const char *eofresponse_str = "\r\n\r\n"; /*
114 * the CRLF terminating the
115 * line, and the CRLF
116 * terminating the entity body.
117 */
118 const char *eol_str = "\r\n";
119 char *eol, *bufp;
120 size_t linelen;
121
122 if ((rc = read(s, &buf[readsofar], sizeof(buf) - readsofar - 1)) <= 0) {
123 snprintf(emsg, emsglen,
124 "could not read response from proxy server. "
125 "read(2) returned %ld after having read %lu bytes",
126 (long)rc, (unsigned long)readsofar);
127 return -1;
128 }
129
130 len = (size_t)rc;
131
132 buf[readsofar + len] = NUL;
133
134 slog(LOG_NEGOTIATE, "%s: read from server: %s",
135 function, str2vis(&buf[readsofar], len, visbuf, sizeof(visbuf)));
136
137 readsofar += len;
138
139 if ((strstr(buf, eofresponse_str)) == NULL)
140 continue; /* don't bother to start parsing til we've got it all. */
141 else
142 eof = 1;
143
144 bufp = buf;
145 while ((eol = strstr(bufp, eol_str)) != NULL) {
146 /* check each line for the response we are looking for. */
147 *eol = NUL;
148 linelen = eol - bufp;
149
150 slog(LOG_DEBUG, "%s: checking line \"%s\"",
151 function, str2vis(bufp, linelen, visbuf, sizeof(visbuf)));
152
153 if (!checked) {
154 int error = 0;
155
156 switch (packet->req.version) {
157 case PROXY_HTTP_10:
158 case PROXY_HTTP_11: {
159 const char *ver_str
160 = proxyprotocol2string(packet->req.version);
161 size_t offset = strlen(ver_str);
162
163 if (linelen < offset + strlen(" 200")) {
164 snprintf(emsg, emsglen,
165 "response from proxy server is too short to"
166 "indicate success: \"%s\"",
167 visbuf);
168
169 error = 1;
170 break;
171 }
172
173 if (strncmp(bufp, ver_str, offset) != 0) {
174 snprintf(emsg, emsglen,
175 "HTTP version (\"%s\") in response from proxy "
176 "server does not match expected (\"%s\"). "
177 "Continuing anyway and hoping for the best ...",
178 visbuf, ver_str);
179 }
180
181 while (isspace((unsigned char)bufp[offset]))
182 ++offset;
183
184 if (!isdigit((unsigned char)bufp[offset])) {
185 char tmp[sizeof(visbuf)];
186
187 snprintf(emsg, emsglen,
188 "response from proxy server does not match. "
189 "Expected a number at offset %lu, but got \"%s\"",
190 (unsigned long)offset,
191 str2vis(&bufp[offset],
192 linelen - offset,
193 tmp,
194 sizeof(tmp)));
195 error = 1;
196 break;
197 }
198
199 packet->res.version = packet->req.version;
200
201 /* not really a portnumber, but close enough. */
202 if ((rc = string2portnumber(&bufp[offset], emsg, emsglen))
203 == -1) {
204 swarn("%s: could not find response code in http "
205 "response (\"%s\"): %s",
206 function, visbuf, emsg);
207
208 rc = HTTP_SUCCESS; /* hope for the best. */
209 }
210 else {
211 snprintf(emsg, emsglen,
212 "response code %ld from http server indicates "
213 "%s: \"%s\"",
214 (long)rc,
215 rc == HTTP_SUCCESS ? "success" : "failure",
216 visbuf);
217
218 slog(LOG_DEBUG, "%s: %s", function, emsg);
219 }
220
221 socks_set_responsevalue(&packet->res, (unsigned int)rc);
222
223 /*
224 * we have no idea what address the server will use on
225 * our behalf, so set it to what we use. Better than
226 * nothing, perhaps. :-/
227 */
228 addrlen = sizeof(addr);
229 if (getsockname(s, TOSA(&addr), &addrlen) != 0)
230 SWARN(s);
231
232 sockaddr2sockshost(&addr, &packet->res.host);
233
234 checked = 1;
235 break;
236 }
237
238 default:
239 SERRX(packet->req.version);
240 }
241
242 if (error) {
243 snprintf(emsg, emsglen,
244 "unknown response from proxy server: \"%s\"",
245 str2vis(bufp, linelen, visbuf, sizeof(visbuf)));
246 return -1;
247 }
248 }
249
250 /* shift out the line we just parsed, nothing of interest there. */
251 bufp += linelen;
252 }
253 } while (!eof);
254
255 if (checked)
256 return socks_get_responsevalue(&packet->res) == HTTP_SUCCESS ? 0 : -1;
257
258 slog(LOG_INFO, "%s: didn't get status code from proxy", function);
259 return -1;
260 }
261