Skip to content

Latest commit

 

History

History
1571 lines (1370 loc) · 38.5 KB

http.c

File metadata and controls

1571 lines (1370 loc) · 38.5 KB
 
Oct 1, 2008
Oct 1, 2008
1
/*
Nov 20, 2008
Nov 20, 2008
2
* OpenConnect (SSL + DTLS) VPN client
Oct 1, 2008
Oct 1, 2008
3
*
Jan 26, 2015
Jan 26, 2015
4
* Copyright © 2008-2015 Intel Corporation.
Apr 9, 2009
Apr 9, 2009
5
* Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
Oct 1, 2008
Oct 1, 2008
6
*
Nov 20, 2008
Nov 20, 2008
7
8
9
* Author: David Woodhouse <dwmw2@infradead.org>
*
* This program is free software; you can redistribute it and/or
Oct 4, 2008
Oct 4, 2008
10
* modify it under the terms of the GNU Lesser General Public License
Nov 20, 2008
Nov 20, 2008
11
* version 2.1, as published by the Free Software Foundation.
Oct 1, 2008
Oct 1, 2008
12
*
Nov 20, 2008
Nov 20, 2008
13
* This program is distributed in the hope that it will be useful, but
Oct 4, 2008
Oct 4, 2008
14
15
16
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
Oct 1, 2008
Oct 1, 2008
17
18
*/
Jul 1, 2014
Jul 1, 2014
19
20
#include <config.h>
Oct 1, 2008
Oct 1, 2008
21
22
23
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
Jun 1, 2009
Jun 1, 2009
24
#include <string.h>
Oct 1, 2008
Oct 1, 2008
25
#include <ctype.h>
May 29, 2012
May 29, 2012
26
27
28
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
Oct 28, 2012
Oct 28, 2012
29
#include <stdarg.h>
Oct 1, 2008
Oct 1, 2008
30
Aug 2, 2019
Aug 2, 2019
31
32
#include <libxml/uri.h>
Mar 9, 2011
Mar 9, 2011
33
#include "openconnect-internal.h"
Oct 1, 2008
Oct 1, 2008
34
Jun 18, 2014
Jun 18, 2014
35
36
static int proxy_write(struct openconnect_info *vpninfo, char *buf, size_t len);
static int proxy_read(struct openconnect_info *vpninfo, char *buf, size_t len);
Feb 22, 2010
Feb 22, 2010
37
Oct 28, 2012
Oct 28, 2012
38
39
#define BUF_CHUNK_SIZE 4096
Jun 19, 2014
Jun 19, 2014
40
struct oc_text_buf *buf_alloc(void)
Oct 28, 2012
Oct 28, 2012
41
42
43
44
{
return calloc(1, sizeof(struct oc_text_buf));
}
Dec 13, 2016
Dec 13, 2016
45
void buf_append_urlencoded(struct oc_text_buf *buf, const char *str)
Jul 24, 2014
Jul 24, 2014
46
47
{
while (str && *str) {
Jul 28, 2014
Jul 28, 2014
48
unsigned char c = *str;
Dec 13, 2016
Dec 13, 2016
49
if (c < 0x80 && (isalnum((int)(c)) || c=='-' || c=='_' || c=='.' || c=='~'))
Jul 24, 2014
Jul 24, 2014
50
51
buf_append_bytes(buf, str, 1);
else
Jul 28, 2014
Jul 28, 2014
52
53
buf_append(buf, "%%%02x", c);
Jul 24, 2014
Jul 24, 2014
54
55
56
57
str++;
}
}
May 31, 2018
May 31, 2018
58
59
60
61
62
63
64
65
66
67
68
69
70
void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str)
{
while (str && *str) {
unsigned char c = *str;
if (c=='<' || c=='>' || c=='&' || c=='"' || c=='\'')
buf_append(buf, "&#x%02x;", c);
else
buf_append_bytes(buf, str, 1);
str++;
}
}
Dec 13, 2016
Dec 13, 2016
71
72
73
74
75
76
77
78
79
void buf_append_hex(struct oc_text_buf *buf, const void *str, unsigned len)
{
const unsigned char *data = str;
unsigned i;
for (i = 0; i < len; i++)
buf_append(buf, "%02x", (unsigned)data[i]);
}
Jul 24, 2014
Jul 24, 2014
80
81
82
83
84
85
void buf_truncate(struct oc_text_buf *buf)
{
if (!buf)
return;
if (buf->data)
Dec 21, 2018
Dec 21, 2018
86
87
88
memset(buf->data, 0, buf->pos);
buf->pos = 0;
Jul 24, 2014
Jul 24, 2014
89
90
}
Jun 23, 2014
Jun 23, 2014
91
int buf_ensure_space(struct oc_text_buf *buf, int len)
Jun 20, 2014
Jun 20, 2014
92
{
May 11, 2019
May 11, 2019
93
unsigned int new_buf_len;
Jun 20, 2014
Jun 20, 2014
94
Oct 11, 2019
Oct 11, 2019
95
96
97
if (!buf)
return -ENOMEM;
Jun 20, 2014
Jun 20, 2014
98
99
100
101
102
new_buf_len = (buf->pos + len + BUF_CHUNK_SIZE - 1) & ~(BUF_CHUNK_SIZE - 1);
if (new_buf_len <= buf->buf_len)
return 0;
May 11, 2019
May 11, 2019
103
if (new_buf_len > INT_MAX) {
Jun 20, 2014
Jun 20, 2014
104
105
106
107
108
109
110
111
112
113
114
115
buf->error = -E2BIG;
return buf->error;
} else {
realloc_inplace(buf->data, new_buf_len);
if (!buf->data)
buf->error = -ENOMEM;
else
buf->buf_len = new_buf_len;
}
return buf->error;
}
Jun 19, 2014
Jun 19, 2014
116
117
void __attribute__ ((format (printf, 2, 3)))
buf_append(struct oc_text_buf *buf, const char *fmt, ...)
Oct 28, 2012
Oct 28, 2012
118
119
120
121
122
123
{
va_list ap;
if (!buf || buf->error)
return;
Jun 20, 2014
Jun 20, 2014
124
125
if (buf_ensure_space(buf, 1))
return;
Oct 28, 2012
Oct 28, 2012
126
127
128
129
130
131
132
133
134
135
136
137
138
while (1) {
int max_len = buf->buf_len - buf->pos, ret;
va_start(ap, fmt);
ret = vsnprintf(buf->data + buf->pos, max_len, fmt, ap);
va_end(ap);
if (ret < 0) {
buf->error = -EIO;
break;
} else if (ret < max_len) {
buf->pos += ret;
break;
Jun 20, 2014
Jun 20, 2014
139
140
} else if (buf_ensure_space(buf, ret))
break;
Oct 28, 2012
Oct 28, 2012
141
142
143
}
}
Jun 19, 2014
Jun 19, 2014
144
145
146
147
148
void buf_append_bytes(struct oc_text_buf *buf, const void *bytes, int len)
{
if (!buf || buf->error)
return;
Jun 20, 2014
Jun 20, 2014
149
if (buf_ensure_space(buf, len + 1))
Jun 19, 2014
Jun 19, 2014
150
return;
Jun 20, 2014
Jun 20, 2014
151
Jun 19, 2014
Jun 19, 2014
152
153
memcpy(buf->data + buf->pos, bytes, len);
buf->pos += len;
Jun 20, 2014
Jun 20, 2014
154
buf->data[buf->pos] = 0;
Jun 19, 2014
Jun 19, 2014
155
156
}
Jul 28, 2014
Jul 28, 2014
157
158
159
160
161
162
163
164
165
166
167
void buf_append_from_utf16le(struct oc_text_buf *buf, const void *_utf16)
{
const unsigned char *utf16 = _utf16;
unsigned char utf8[4];
int c;
if (!utf16)
return;
while (utf16[0] || utf16[1]) {
if ((utf16[1] & 0xfc) == 0xd8 && (utf16[3] & 0xfc) == 0xdc) {
Jan 26, 2015
Jan 26, 2015
168
169
c = ((load_le16(utf16) & 0x3ff) << 10)|
(load_le16(utf16 + 2) & 0x3ff);
Jul 28, 2014
Jul 28, 2014
170
171
172
c += 0x10000;
utf16 += 4;
} else {
Jan 26, 2015
Jan 26, 2015
173
c = load_le16(utf16);
Jul 28, 2014
Jul 28, 2014
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
utf16 += 2;
}
if (c < 0x80) {
utf8[0] = c;
buf_append_bytes(buf, utf8, 1);
} else if (c < 0x800) {
utf8[0] = 0xc0 | (c >> 6);
utf8[1] = 0x80 | (c & 0x3f);
buf_append_bytes(buf, utf8, 2);
} else if (c < 0x10000) {
utf8[0] = 0xe0 | (c >> 12);
utf8[1] = 0x80 | ((c >> 6) & 0x3f);
utf8[2] = 0x80 | (c & 0x3f);
buf_append_bytes(buf, utf8, 3);
} else {
utf8[0] = 0xf0 | (c >> 18);
utf8[1] = 0x80 | ((c >> 12) & 0x3f);
utf8[2] = 0x80 | ((c >> 6) & 0x3f);
utf8[3] = 0x80 | (c & 0x3f);
buf_append_bytes(buf, utf8, 4);
}
}
utf8[0] = 0;
buf_append_bytes(buf, utf8, 1);
}
Feb 4, 2015
Feb 4, 2015
201
int get_utf8char(const char **p)
Jul 28, 2014
Jul 28, 2014
202
{
Feb 4, 2015
Feb 4, 2015
203
const char *utf8 = *p;
Jul 28, 2014
Jul 28, 2014
204
unsigned char c;
Feb 4, 2015
Feb 4, 2015
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
int utfchar, nr_extra, min;
c = *(utf8++);
if (c < 128) {
utfchar = c;
nr_extra = 0;
min = 0;
} else if ((c & 0xe0) == 0xc0) {
utfchar = c & 0x1f;
nr_extra = 1;
min = 0x80;
} else if ((c & 0xf0) == 0xe0) {
utfchar = c & 0x0f;
nr_extra = 2;
min = 0x800;
} else if ((c & 0xf8) == 0xf0) {
utfchar = c & 0x07;
nr_extra = 3;
min = 0x10000;
} else {
return -EILSEQ;
}
Jul 28, 2014
Jul 28, 2014
227
Feb 4, 2015
Feb 4, 2015
228
while (nr_extra--) {
Jul 28, 2014
Jul 28, 2014
229
c = *(utf8++);
Feb 4, 2015
Feb 4, 2015
230
if ((c & 0xc0) != 0x80)
Jul 31, 2014
Jul 31, 2014
231
return -EILSEQ;
Jul 28, 2014
Jul 28, 2014
232
Feb 4, 2015
Feb 4, 2015
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
utfchar <<= 6;
utfchar |= (c & 0x3f);
}
if (utfchar > 0x10ffff || utfchar < min)
return -EILSEQ;
*p = utf8;
return utfchar;
}
int buf_append_utf16le(struct oc_text_buf *buf, const char *utf8)
{
int utfchar, len = 0;
/* Ick. Now I'm implementing my own UTF8 handling too. Perhaps it's
time to bite the bullet and start requiring something like glib? */
while (*utf8) {
utfchar = get_utf8char(&utf8);
if (utfchar < 0) {
Jul 31, 2014
Jul 31, 2014
252
if (buf)
Feb 4, 2015
Feb 4, 2015
253
254
buf->error = utfchar;
return utfchar;
Jul 31, 2014
Jul 31, 2014
255
256
257
}
if (!buf)
continue;
Jul 28, 2014
Jul 28, 2014
258
259
260
261
if (utfchar >= 0x10000) {
utfchar -= 0x10000;
if (buf_ensure_space(buf, 4))
Jul 29, 2014
Jul 29, 2014
262
return buf_error(buf);
Jan 26, 2015
Jan 26, 2015
263
264
265
store_le16(buf->data + buf->pos, (utfchar >> 10) | 0xd800);
store_le16(buf->data + buf->pos + 2, (utfchar & 0x3ff) | 0xdc00);
buf->pos += 4;
Jul 28, 2014
Jul 28, 2014
266
267
268
len += 4;
} else {
if (buf_ensure_space(buf, 2))
Jul 29, 2014
Jul 29, 2014
269
return buf_error(buf);
Jan 26, 2015
Jan 26, 2015
270
271
store_le16(buf->data + buf->pos, utfchar);
buf->pos += 2;
Jul 28, 2014
Jul 28, 2014
272
273
274
len += 2;
}
}
Jul 29, 2014
Jul 29, 2014
275
Jul 31, 2014
Jul 31, 2014
276
277
278
279
/* We were only being used for validation */
if (!buf)
return 0;
Jul 29, 2014
Jul 29, 2014
280
281
/* Ensure UTF16 is NUL-terminated */
if (buf_ensure_space(buf, 2))
Jul 31, 2014
Jul 31, 2014
282
return buf_error(buf);
Jul 29, 2014
Jul 29, 2014
283
284
buf->data[buf->pos] = buf->data[buf->pos + 1] = 0;
Jul 28, 2014
Jul 28, 2014
285
286
287
return len;
}
Jun 19, 2014
Jun 19, 2014
288
int buf_error(struct oc_text_buf *buf)
Oct 28, 2012
Oct 28, 2012
289
290
291
292
{
return buf ? buf->error : -ENOMEM;
}
Jun 19, 2014
Jun 19, 2014
293
int buf_free(struct oc_text_buf *buf)
Oct 28, 2012
Oct 28, 2012
294
295
296
297
{
int error = buf_error(buf);
if (buf) {
Dec 21, 2018
Dec 21, 2018
298
buf_truncate(buf);
Oct 28, 2012
Oct 28, 2012
299
300
301
302
303
304
305
306
if (buf->data)
free(buf->data);
free(buf);
}
return error;
}
Oct 1, 2008
Oct 1, 2008
307
/*
Apr 9, 2009
Apr 9, 2009
308
* We didn't really want to have to do this for ourselves -- one might have
Oct 1, 2008
Oct 1, 2008
309
310
311
312
313
314
* thought that it would be available in a library somewhere. But neither
* cURL nor Neon have reliable cross-platform ways of either using a cert
* from the TPM, or just reading from / writing to a transport which is
* provided by their caller.
*/
Jan 26, 2015
Jan 26, 2015
315
316
int http_add_cookie(struct openconnect_info *vpninfo, const char *option,
const char *value, int replace)
Nov 6, 2009
Nov 6, 2009
317
{
Jan 15, 2014
Jan 15, 2014
318
struct oc_vpn_option *new, **this;
Nov 6, 2009
Nov 6, 2009
319
320
321
322
if (*value) {
new = malloc(sizeof(*new));
if (!new) {
Sep 22, 2011
Sep 22, 2011
323
324
vpn_progress(vpninfo, PRG_ERR,
_("No memory for allocating cookies\n"));
Nov 6, 2009
Nov 6, 2009
325
326
327
328
329
return -ENOMEM;
}
new->next = NULL;
new->option = strdup(option);
new->value = strdup(value);
Nov 6, 2009
Nov 6, 2009
330
331
332
333
334
335
if (!new->option || !new->value) {
free(new->option);
free(new->value);
free(new);
return -ENOMEM;
}
Nov 6, 2009
Nov 6, 2009
336
337
338
} else {
/* Kill cookie; don't replace it */
new = NULL;
Feb 28, 2015
Feb 28, 2015
339
340
341
/* This would be meaningless */
if (!replace)
return -EINVAL;
Nov 6, 2009
Nov 6, 2009
342
343
344
}
for (this = &vpninfo->cookies; *this; this = &(*this)->next) {
if (!strcmp(option, (*this)->option)) {
Jan 26, 2015
Jan 26, 2015
345
346
347
348
349
350
if (!replace) {
free(new->value);
free(new->option);
free(new);
return 0;
}
Nov 6, 2009
Nov 6, 2009
351
352
353
354
355
/* Replace existing cookie */
if (new)
new->next = (*this)->next;
else
new = (*this)->next;
Mar 10, 2013
Mar 10, 2013
356
Nov 6, 2009
Nov 6, 2009
357
358
359
360
361
362
363
364
365
366
367
368
369
370
free((*this)->option);
free((*this)->value);
free(*this);
*this = new;
break;
}
}
if (new && !*this) {
*this = new;
new->next = NULL;
}
return 0;
}
Sep 13, 2019
Sep 13, 2019
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
/* Read one HTTP header line into hdrbuf, potentially allowing for
* continuation lines. Will never leave a character in 'nextchar' when
* an empty line (signifying end of headers) is received. Will only
* return success when hdrbuf is valid. */
static int read_http_header(struct openconnect_info *vpninfo, char *nextchar,
struct oc_text_buf *hdrbuf, int allow_cont)
{
int eol = 0;
int ret;
char c;
buf_truncate(hdrbuf);
c = *nextchar;
if (c) {
*nextchar = 0;
goto skip_first;
}
while (1) {
ret = vpninfo->ssl_read(vpninfo, &c, 1);
if (ret < 0)
return ret;
if (ret != 1)
return -EINVAL;
/* If we were looking for a continuation line and didn't get it,
* stash the character we *did* get into *nextchar for next time. */
if (eol && c != ' ' && c != '\t') {
*nextchar = c;
return buf_error(hdrbuf);
}
eol = 0;
skip_first:
if (c == '\n') {
if (!buf_error(hdrbuf) && hdrbuf->pos &&
hdrbuf->data[hdrbuf->pos - 1] == '\r') {
hdrbuf->pos--;
hdrbuf->data[hdrbuf->pos] = 0;
}
/* For a non-empty header line, see if there's a continuation */
if (allow_cont && hdrbuf->pos) {
eol = 1;
continue;
}
return buf_error(hdrbuf);
}
buf_append_bytes(hdrbuf, &c, 1);
}
return buf_error(hdrbuf);
}
Jan 9, 2010
Jan 9, 2010
427
428
429
#define BODY_HTTP10 -1
#define BODY_CHUNKED -2
Jan 26, 2015
Jan 26, 2015
430
431
432
int process_http_response(struct openconnect_info *vpninfo, int connect,
int (*header_cb)(struct openconnect_info *, char *, char *),
struct oc_text_buf *body)
Oct 1, 2008
Oct 1, 2008
433
{
Sep 13, 2019
Sep 13, 2019
434
435
struct oc_text_buf *hdrbuf = buf_alloc();
char nextchar = 0;
Jan 9, 2010
Jan 9, 2010
436
437
int bodylen = BODY_HTTP10;
int closeconn = 0;
Jul 24, 2014
Jul 24, 2014
438
int result;
Sep 13, 2019
Sep 13, 2019
439
int ret = -EINVAL;
Oct 1, 2008
Oct 1, 2008
440
441
int i;
Jul 24, 2014
Jul 24, 2014
442
443
buf_truncate(body);
Oct 11, 2019
Oct 11, 2019
444
445
446
447
/* Ensure it has *something* in it, so that we can dereference hdrbuf->data
* later without checking (for anything except buf_error(hdrbuf), which is
* what read_http_header() uses for its return code anyway). */
buf_append_bytes(hdrbuf, "\0", 1);
Jan 3, 2010
Jan 3, 2010
448
cont:
Sep 13, 2019
Sep 13, 2019
449
450
451
ret = read_http_header(vpninfo, &nextchar, hdrbuf, 0);
if (ret) {
vpn_progress(vpninfo, PRG_ERR, _("Error reading HTTP response: %s\n"),
Oct 9, 2019
Oct 9, 2019
452
strerror(-ret));
Sep 13, 2019
Sep 13, 2019
453
goto err;
Oct 1, 2008
Oct 1, 2008
454
455
}
Sep 13, 2019
Sep 13, 2019
456
if (!strncmp(hdrbuf->data, "HTTP/1.0 ", 9))
Oct 1, 2008
Oct 1, 2008
457
closeconn = 1;
Mar 10, 2013
Mar 10, 2013
458
Sep 13, 2019
Sep 13, 2019
459
460
if ((!closeconn && strncmp(hdrbuf->data, "HTTP/1.1 ", 9)) ||
!(result = atoi(hdrbuf->data + 9))) {
Sep 22, 2011
Sep 22, 2011
461
vpn_progress(vpninfo, PRG_ERR,
Sep 13, 2019
Sep 13, 2019
462
463
464
_("Failed to parse HTTP response '%s'\n"), hdrbuf->data);
ret = -EINVAL;
goto err;
Oct 1, 2008
Oct 1, 2008
465
466
}
Jul 24, 2014
Jul 24, 2014
467
vpn_progress(vpninfo, (result == 200 || result == 407) ? PRG_DEBUG : PRG_INFO,
Sep 13, 2019
Sep 13, 2019
468
_("Got HTTP response: %s\n"), hdrbuf->data);
Oct 1, 2008
Oct 1, 2008
469
470
/* Eat headers... */
Sep 13, 2019
Sep 13, 2019
471
while (1) {
Oct 1, 2008
Oct 1, 2008
472
char *colon;
Sep 13, 2019
Sep 13, 2019
473
char *hdrline;
Oct 1, 2008
Oct 1, 2008
474
Sep 13, 2019
Sep 13, 2019
475
476
477
ret = read_http_header(vpninfo, &nextchar, hdrbuf, 1);
if (ret) {
vpn_progress(vpninfo, PRG_ERR, _("Error reading HTTP response: %s\n"),
Oct 9, 2019
Oct 9, 2019
478
strerror(-ret));
Sep 13, 2019
Sep 13, 2019
479
goto err;
Oct 1, 2008
Oct 1, 2008
480
}
Sep 13, 2019
Sep 13, 2019
481
482
483
484
485
486
487
488
489
490
/* Default error case */
ret = -EINVAL;
/* Empty line ends headers */
if (!hdrbuf->pos)
break;
hdrline = hdrbuf->data;
colon = strchr(hdrline, ':');
Oct 1, 2008
Oct 1, 2008
491
if (!colon) {
Sep 22, 2011
Sep 22, 2011
492
vpn_progress(vpninfo, PRG_ERR,
Sep 13, 2019
Sep 13, 2019
493
_("Ignoring unknown HTTP response line '%s'\n"), hdrline);
Oct 1, 2008
Oct 1, 2008
494
495
496
497
498
499
continue;
}
*(colon++) = 0;
if (*colon == ' ')
colon++;
Aug 7, 2010
Aug 7, 2010
500
501
/* Handle Set-Cookie first so that we can avoid printing the
webvpn cookie in the verbose debug output */
Sep 13, 2019
Sep 13, 2019
502
if (!strcasecmp(hdrline, "Set-Cookie")) {
Aug 7, 2010
Aug 7, 2010
503
char *semicolon = strchr(colon, ';');
Sep 15, 2011
Sep 15, 2011
504
505
const char *print_equals;
char *equals = strchr(colon, '=');
Aug 7, 2010
Aug 7, 2010
506
507
508
509
510
if (semicolon)
*semicolon = 0;
if (!equals) {
Sep 22, 2011
Sep 22, 2011
511
vpn_progress(vpninfo, PRG_ERR,
Sep 13, 2019
Sep 13, 2019
512
513
514
_("Invalid cookie offered: %s\n"), hdrline);
ret = -EINVAL;
goto err;
Aug 7, 2010
Aug 7, 2010
515
516
517
518
}
*(equals++) = 0;
print_equals = equals;
Nov 30, 2010
Nov 30, 2010
519
520
521
/* Don't print the webvpn cookie unless it's empty; we don't
want people posting it in public with debugging output */
if (!strcmp(colon, "webvpn") && *equals)
Sep 22, 2011
Sep 22, 2011
522
print_equals = _("<elided>");
Jun 13, 2014
Jun 13, 2014
523
vpn_progress(vpninfo, PRG_DEBUG, "%s: %s=%s%s%s\n",
Sep 13, 2019
Sep 13, 2019
524
hdrline, colon, print_equals, semicolon ? ";" : "",
Mar 10, 2013
Mar 10, 2013
525
semicolon ? (semicolon+1) : "");
Aug 7, 2010
Aug 7, 2010
526
Sep 25, 2011
Sep 25, 2011
527
528
529
530
531
532
533
/* The server tends to ask for the username and password as
usual, even if we've already failed because it didn't like
our cert. Thankfully it does give us this hint... */
if (!strcmp(colon, "ClientCertAuthFailed"))
vpn_progress(vpninfo, PRG_ERR,
_("SSL certificate authentication failed\n"));
Jan 26, 2015
Jan 26, 2015
534
ret = http_add_cookie(vpninfo, colon, equals, 1);
Sep 13, 2019
Sep 13, 2019
535
536
if (ret)
goto err;
Aug 7, 2010
Aug 7, 2010
537
} else {
Sep 13, 2019
Sep 13, 2019
538
vpn_progress(vpninfo, PRG_DEBUG, "%s: %s\n", hdrline, colon);
Aug 7, 2010
Aug 7, 2010
539
540
}
Sep 13, 2019
Sep 13, 2019
541
if (!strcasecmp(hdrline, "Connection")) {
Jan 9, 2010
Jan 9, 2010
542
543
if (!strcasecmp(colon, "Close"))
closeconn = 1;
Feb 28, 2010
Feb 28, 2010
544
545
546
547
548
549
#if 0
/* This might seem reasonable, but in fact it breaks
certificate authentication with some servers. If
they give an HTTP/1.0 response, even if they
explicitly give a Connection: Keep-Alive header,
just close the connection. */
Jan 9, 2010
Jan 9, 2010
550
551
else if (!strcasecmp(colon, "Keep-Alive"))
closeconn = 0;
Feb 28, 2010
Feb 28, 2010
552
#endif
Jan 9, 2010
Jan 9, 2010
553
}
Sep 13, 2019
Sep 13, 2019
554
if (!strcasecmp(hdrline, "Location")) {
Oct 1, 2008
Oct 1, 2008
555
vpninfo->redirect_url = strdup(colon);
Sep 10, 2019
Sep 10, 2019
556
if (!vpninfo->redirect_url) {
Sep 13, 2019
Sep 13, 2019
557
558
ret = -ENOMEM;
goto err;
Sep 10, 2019
Sep 10, 2019
559
}
Oct 1, 2008
Oct 1, 2008
560
}
Sep 13, 2019
Sep 13, 2019
561
if (!strcasecmp(hdrline, "Content-Length")) {
Oct 1, 2008
Oct 1, 2008
562
bodylen = atoi(colon);
Feb 22, 2010
Feb 22, 2010
563
if (bodylen < 0) {
Sep 22, 2011
Sep 22, 2011
564
565
566
vpn_progress(vpninfo, PRG_ERR,
_("Response body has negative size (%d)\n"),
bodylen);
Sep 13, 2019
Sep 13, 2019
567
568
ret = -EINVAL;
goto err;
Oct 1, 2008
Oct 1, 2008
569
570
}
}
Sep 13, 2019
Sep 13, 2019
571
if (!strcasecmp(hdrline, "Transfer-Encoding")) {
Jan 9, 2010
Jan 9, 2010
572
if (!strcasecmp(colon, "chunked"))
Jan 9, 2010
Jan 9, 2010
573
bodylen = BODY_CHUNKED;
Oct 1, 2008
Oct 1, 2008
574
else {
Sep 22, 2011
Sep 22, 2011
575
576
577
vpn_progress(vpninfo, PRG_ERR,
_("Unknown Transfer-Encoding: %s\n"),
colon);
Sep 13, 2019
Sep 13, 2019
578
579
ret = -EINVAL;
goto err;
Oct 1, 2008
Oct 1, 2008
580
581
}
}
Jun 18, 2014
Jun 18, 2014
582
if (header_cb)
Sep 13, 2019
Sep 13, 2019
583
header_cb(vpninfo, hdrline, colon);
Oct 1, 2008
Oct 1, 2008
584
585
586
}
/* Handle 'HTTP/1.1 100 Continue'. Not that we should ever see it */
Jul 24, 2014
Jul 24, 2014
587
if (result == 100)
Oct 1, 2008
Oct 1, 2008
588
589
goto cont;
Jun 7, 2019
Jun 7, 2019
590
/* On successful CONNECT or upgrade, there is no body. Return success */
Sep 13, 2019
Sep 13, 2019
591
592
if (connect && (result == 200 || result == 101)) {
buf_free(hdrbuf);
Jul 24, 2014
Jul 24, 2014
593
return result;
Sep 13, 2019
Sep 13, 2019
594
}
Jun 18, 2014
Jun 18, 2014
595
Jan 3, 2010
Jan 3, 2010
596
/* Now the body, if there is one */
Jun 13, 2014
Jun 13, 2014
597
vpn_progress(vpninfo, PRG_DEBUG, _("HTTP body %s (%d)\n"),
Mar 10, 2013
Mar 10, 2013
598
599
bodylen == BODY_HTTP10 ? "http 1.0" :
bodylen == BODY_CHUNKED ? "chunked" : "length: ",
Sep 22, 2011
Sep 22, 2011
600
bodylen);
Jan 3, 2010
Jan 3, 2010
601
Oct 1, 2008
Oct 1, 2008
602
603
/* If we were given Content-Length, it's nice and easy... */
if (bodylen > 0) {
Sep 10, 2019
Sep 10, 2019
604
if (buf_ensure_space(body, bodylen + 1)) {
Sep 13, 2019
Sep 13, 2019
605
606
ret = buf_error(body);
goto err;
Sep 10, 2019
Sep 10, 2019
607
}
Jul 24, 2014
Jul 24, 2014
608
609
610
while (body->pos < bodylen) {
i = vpninfo->ssl_read(vpninfo, body->data + body->pos, bodylen - body->pos);
Oct 1, 2008
Oct 1, 2008
611
if (i < 0) {
Sep 22, 2011
Sep 22, 2011
612
613
vpn_progress(vpninfo, PRG_ERR,
_("Error reading HTTP response body\n"));
Sep 13, 2019
Sep 13, 2019
614
615
ret = i;
goto err;
Oct 1, 2008
Oct 1, 2008
616
}
Jul 24, 2014
Jul 24, 2014
617
body->pos += i;
Oct 1, 2008
Oct 1, 2008
618
}
Jan 9, 2010
Jan 9, 2010
619
} else if (bodylen == BODY_CHUNKED) {
Sep 13, 2019
Sep 13, 2019
620
char clen_buf[16];
Jan 9, 2010
Jan 9, 2010
621
/* ... else, chunked */
Sep 13, 2019
Sep 13, 2019
622
while ((i = vpninfo->ssl_gets(vpninfo, clen_buf, sizeof(clen_buf)))) {
Sep 11, 2019
Sep 11, 2019
623
624
int lastchunk = 0;
long chunklen;
Oct 1, 2008
Oct 1, 2008
625
626
if (i < 0) {
Sep 22, 2011
Sep 22, 2011
627
628
vpn_progress(vpninfo, PRG_ERR,
_("Error fetching chunk header\n"));
Sep 13, 2019
Sep 13, 2019
629
630
ret = i;
goto err;
Jan 9, 2010
Jan 9, 2010
631
}
Sep 13, 2019
Sep 13, 2019
632
chunklen = strtol(clen_buf, NULL, 16);
Jan 9, 2010
Jan 9, 2010
633
634
635
636
if (!chunklen) {
lastchunk = 1;
goto skip;
}
Sep 11, 2019
Sep 11, 2019
637
638
639
if (chunklen < 0) {
vpn_progress(vpninfo, PRG_ERR,
_("HTTP chunk length is negative (%ld)\n"), chunklen);
Sep 13, 2019
Sep 13, 2019
640
641
ret = -EINVAL;
goto err;
Sep 11, 2019
Sep 11, 2019
642
643
644
645
}
if (chunklen >= INT_MAX) {
vpn_progress(vpninfo, PRG_ERR,
_("HTTP chunk length is too large (%ld)\n"), chunklen);
Sep 13, 2019
Sep 13, 2019
646
647
ret = -EINVAL;
goto err;
Sep 11, 2019
Sep 11, 2019
648
}
Sep 10, 2019
Sep 10, 2019
649
if (buf_ensure_space(body, chunklen + 1)) {
Sep 13, 2019
Sep 13, 2019
650
651
ret = buf_error(body);
goto err;
Sep 10, 2019
Sep 10, 2019
652
}
Jan 9, 2010
Jan 9, 2010
653
while (chunklen) {
Jul 24, 2014
Jul 24, 2014
654
i = vpninfo->ssl_read(vpninfo, body->data + body->pos, chunklen);
Jan 9, 2010
Jan 9, 2010
655
if (i < 0) {
Sep 22, 2011
Sep 22, 2011
656
657
vpn_progress(vpninfo, PRG_ERR,
_("Error reading HTTP response body\n"));
Sep 13, 2019
Sep 13, 2019
658
659
ret = i;
goto err;
Jan 9, 2010
Jan 9, 2010
660
661
}
chunklen -= i;
Jul 24, 2014
Jul 24, 2014
662
body->pos += i;
Jan 9, 2010
Jan 9, 2010
663
664
}
skip:
Sep 13, 2019
Sep 13, 2019
665
if ((i = vpninfo->ssl_gets(vpninfo, clen_buf, sizeof(clen_buf)))) {
Jan 9, 2010
Jan 9, 2010
666
if (i < 0) {
Sep 22, 2011
Sep 22, 2011
667
668
vpn_progress(vpninfo, PRG_ERR,
_("Error fetching HTTP response body\n"));
Sep 13, 2019
Sep 13, 2019
669
ret = i;
Jan 9, 2010
Jan 9, 2010
670
} else {
Sep 22, 2011
Sep 22, 2011
671
672
vpn_progress(vpninfo, PRG_ERR,
_("Error in chunked decoding. Expected '', got: '%s'"),
Sep 13, 2019
Sep 13, 2019
673
674
clen_buf);
ret = -EINVAL;
Jan 9, 2010
Jan 9, 2010
675
}
Sep 13, 2019
Sep 13, 2019
676
goto err;
Oct 1, 2008
Oct 1, 2008
677
}
Feb 22, 2010
Feb 22, 2010
678
Jan 9, 2010
Jan 9, 2010
679
680
681
682
683
if (lastchunk)
break;
}
} else if (bodylen == BODY_HTTP10) {
if (!closeconn) {
Sep 22, 2011
Sep 22, 2011
684
685
vpn_progress(vpninfo, PRG_ERR,
_("Cannot receive HTTP 1.0 body without closing connection\n"));
Feb 4, 2013
Feb 4, 2013
686
openconnect_close_https(vpninfo, 0);
Oct 1, 2008
Oct 1, 2008
687
688
689
return -EINVAL;
}
Jul 24, 2014
Jul 24, 2014
690
/* HTTP 1.0 response. Just eat all we can in 4KiB chunks */
Jan 9, 2010
Jan 9, 2010
691
while (1) {
Sep 10, 2019
Sep 10, 2019
692
if (buf_ensure_space(body, 4096 + 1)) {
Sep 13, 2019
Sep 13, 2019
693
694
ret = buf_error(body);
goto err;
Sep 10, 2019
Sep 10, 2019
695
}
Jul 24, 2014
Jul 24, 2014
696
697
i = vpninfo->ssl_read(vpninfo, body->data + body->pos, 4096);
if (i < 0) {
May 12, 2012
May 12, 2012
698
/* Error */
Sep 13, 2019
Sep 13, 2019
699
700
ret = i;
goto err;
Jul 24, 2014
Jul 24, 2014
701
} else if (!i)
Jan 9, 2010
Jan 9, 2010
702
break;
Jul 24, 2014
Jul 24, 2014
703
704
705
/* Got more data */
body->pos += i;
Jan 9, 2010
Jan 9, 2010
706
}
Oct 1, 2008
Oct 1, 2008
707
}
Jan 9, 2010
Jan 9, 2010
708
Jul 24, 2014
Jul 24, 2014
709
body->data[body->pos] = 0;
Sep 13, 2019
Sep 13, 2019
710
711
712
713
714
715
716
ret = result;
if (closeconn || vpninfo->no_http_keepalive) {
err:
openconnect_close_https(vpninfo, 0);
}
buf_free(hdrbuf);
Oct 8, 2019
Oct 8, 2019
717
return ret;
Oct 1, 2008
Oct 1, 2008
718
719
}
Sep 29, 2014
Sep 29, 2014
720
int internal_parse_url(const char *url, char **res_proto, char **res_host,
Mar 9, 2011
Mar 9, 2011
721
int *res_port, char **res_path, int default_port)
Dec 25, 2009
Dec 25, 2009
722
{
Sep 29, 2014
Sep 29, 2014
723
724
725
const char *orig_host, *orig_path;
char *host, *port_str;
int port, proto_len = 0;
Dec 25, 2009
Dec 25, 2009
726
Sep 29, 2014
Sep 29, 2014
727
728
729
730
orig_host = strstr(url, "://");
if (orig_host) {
proto_len = orig_host - url;
orig_host += 3;
Jan 1, 2010
Jan 1, 2010
731
Sep 29, 2014
Sep 29, 2014
732
if (strprefix_match(url, proto_len, "https"))
Jan 1, 2010
Jan 1, 2010
733
port = 443;
Sep 29, 2014
Sep 29, 2014
734
else if (strprefix_match(url, proto_len, "http"))
Jan 1, 2010
Jan 1, 2010
735
port = 80;
Sep 29, 2014
Sep 29, 2014
736
737
738
else if (strprefix_match(url, proto_len, "socks") ||
strprefix_match(url, proto_len, "socks4") ||
strprefix_match(url, proto_len, "socks5"))
Jan 1, 2010
Jan 1, 2010
739
740
741
742
743
744
port = 1080;
else
return -EPROTONOSUPPORT;
} else {
if (default_port) {
port = default_port;
Sep 29, 2014
Sep 29, 2014
745
orig_host = url;
Jan 1, 2010
Jan 1, 2010
746
747
748
} else
return -EINVAL;
}
Dec 25, 2009
Dec 25, 2009
749
Sep 29, 2014
Sep 29, 2014
750
751
752
753
754
755
756
757
orig_path = strchr(orig_host, '/');
if (orig_path) {
host = strndup(orig_host, orig_path - orig_host);
orig_path++;
} else
host = strdup(orig_host);
if (!host)
return -ENOMEM;
Dec 25, 2009
Dec 25, 2009
758
759
760
761
762
763
764
765
766
767
768
769
770
port_str = strrchr(host, ':');
if (port_str) {
char *end;
int new_port = strtol(port_str + 1, &end, 10);
if (!*end) {
*port_str = 0;
port = new_port;
}
}
if (res_proto)
Sep 29, 2014
Sep 29, 2014
771
*res_proto = proto_len ? strndup(url, proto_len) : NULL;
Dec 25, 2009
Dec 25, 2009
772
if (res_host)
Sep 29, 2014
Sep 29, 2014
773
774
775
*res_host = host;
else
free(host);
Dec 25, 2009
Dec 25, 2009
776
777
778
if (res_port)
*res_port = port;
if (res_path)
Sep 29, 2014
Sep 29, 2014
779
780
*res_path = (orig_path && *orig_path) ? strdup(orig_path) : NULL;
Dec 25, 2009
Dec 25, 2009
781
782
783
return 0;
}
Oct 10, 2014
Oct 10, 2014
784
void openconnect_clear_cookies(struct openconnect_info *vpninfo)
Oct 27, 2012
Oct 27, 2012
785
{
Jan 15, 2014
Jan 15, 2014
786
struct oc_vpn_option *opt, *next;
Oct 27, 2012
Oct 27, 2012
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
for (opt = vpninfo->cookies; opt; opt = next) {
next = opt->next;
free(opt->option);
free(opt->value);
free(opt);
}
vpninfo->cookies = NULL;
}
/* Return value:
* < 0, on error
* = 0, on success (go ahead and retry with the latest vpninfo->{hostname,urlpath,port,...})
*/
Jan 26, 2015
Jan 26, 2015
802
int handle_redirect(struct openconnect_info *vpninfo)
Oct 27, 2012
Oct 27, 2012
803
{
Oct 28, 2012
Oct 28, 2012
804
805
vpninfo->redirect_type = REDIR_TYPE_LOCAL;
Oct 27, 2012
Oct 27, 2012
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
if (!strncmp(vpninfo->redirect_url, "https://", 8)) {
/* New host. Tear down the existing connection and make a new one */
char *host;
int port;
int ret;
free(vpninfo->urlpath);
vpninfo->urlpath = NULL;
ret = internal_parse_url(vpninfo->redirect_url, NULL, &host, &port, &vpninfo->urlpath, 0);
if (ret) {
vpn_progress(vpninfo, PRG_ERR,
_("Failed to parse redirected URL '%s': %s\n"),
vpninfo->redirect_url, strerror(-ret));
free(vpninfo->redirect_url);
vpninfo->redirect_url = NULL;
return ret;
}
if (strcasecmp(vpninfo->hostname, host) || port != vpninfo->port) {
Dec 31, 2013
Dec 31, 2013
826
openconnect_set_hostname(vpninfo, host);
Oct 27, 2012
Oct 27, 2012
827
828
829
830
vpninfo->port = port;
/* Kill the existing connection, and a new one will happen */
openconnect_close_https(vpninfo, 0);
Oct 10, 2014
Oct 10, 2014
831
openconnect_clear_cookies(vpninfo);
Oct 28, 2012
Oct 28, 2012
832
vpninfo->redirect_type = REDIR_TYPE_NEWHOST;
Nov 4, 2014
Nov 4, 2014
833
834
}
free(host);
Oct 27, 2012
Oct 27, 2012
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
free(vpninfo->redirect_url);
vpninfo->redirect_url = NULL;
return 0;
} else if (strstr(vpninfo->redirect_url, "://")) {
vpn_progress(vpninfo, PRG_ERR,
_("Cannot follow redirection to non-https URL '%s'\n"),
vpninfo->redirect_url);
free(vpninfo->redirect_url);
vpninfo->redirect_url = NULL;
return -EINVAL;
} else if (vpninfo->redirect_url[0] == '/') {
/* Absolute redirect within same host */
free(vpninfo->urlpath);
vpninfo->urlpath = strdup(vpninfo->redirect_url + 1);
free(vpninfo->redirect_url);
vpninfo->redirect_url = NULL;
return 0;
} else {
char *lastslash = NULL;
if (vpninfo->urlpath)
lastslash = strrchr(vpninfo->urlpath, '/');
if (!lastslash) {
free(vpninfo->urlpath);
vpninfo->urlpath = vpninfo->redirect_url;
vpninfo->redirect_url = NULL;
} else {
char *oldurl = vpninfo->urlpath;
*lastslash = 0;
vpninfo->urlpath = NULL;
if (asprintf(&vpninfo->urlpath, "%s/%s",
oldurl, vpninfo->redirect_url) == -1) {
int err = -errno;
vpn_progress(vpninfo, PRG_ERR,
_("Allocating new path for relative redirect failed: %s\n"),
strerror(-err));
return err;
}
free(oldurl);
free(vpninfo->redirect_url);
vpninfo->redirect_url = NULL;
}
return 0;
}
}
Jan 25, 2015
Jan 25, 2015
882
void dump_buf(struct openconnect_info *vpninfo, char prefix, char *buf)
May 30, 2013
May 30, 2013
883
884
885
886
887
888
889
890
891
892
893
894
895
896
{
while (*buf) {
char *eol = buf;
char eol_char = 0;
while (*eol) {
if (*eol == '\r' || *eol == '\n') {
eol_char = *eol;
*eol = 0;
break;
}
eol++;
}
Jun 13, 2014
Jun 13, 2014
897
vpn_progress(vpninfo, PRG_DEBUG, "%c %s\n", prefix, buf);
May 30, 2013
May 30, 2013
898
899
900
901
902
903
904
905
906
907
if (!eol_char)
break;
*eol = eol_char;
buf = eol + 1;
if (eol_char == '\r' && *buf == '\n')
buf++;
}
}
Aug 14, 2017
Aug 14, 2017
908
909
void dump_buf_hex(struct openconnect_info *vpninfo, int loglevel, char prefix, unsigned char *buf, int len)
{
Jun 7, 2019
Jun 7, 2019
910
struct oc_text_buf *line = buf_alloc();
Jun 7, 2019
Jun 7, 2019
911
int i, j;
Aug 14, 2017
Aug 14, 2017
912
Jun 7, 2019
Jun 7, 2019
913
for (i = 0; i < len; i+=16) {
Jun 7, 2019
Jun 7, 2019
914
915
buf_truncate(line);
buf_append(line, "%04x:", i);
Jun 7, 2019
Jun 7, 2019
916
for (j = i; j < i+16; j++) {
Jun 7, 2019
Jun 7, 2019
917
918
919
if (!(j & 7))
buf_append(line, " ");
Jun 7, 2019
Jun 7, 2019
920
if (j < len)
Jun 7, 2019
Jun 7, 2019
921
buf_append(line, " %02x", buf[j]);
Jun 7, 2019
Jun 7, 2019
922
else
Jun 7, 2019
Jun 7, 2019
923
buf_append(line, " ");
Aug 14, 2017
Aug 14, 2017
924
}
Jun 7, 2019
Jun 7, 2019
925
buf_append(line, " |");
Jun 7, 2019
Jun 7, 2019
926
for (j = i; j < i+16 && j < len; j++)
Jun 7, 2019
Jun 7, 2019
927
928
929
930
931
buf_append(line, "%c", isprint(buf[j])? buf[j] : '.');
buf_append(line, "|");
if (buf_error(line))
break;
vpn_progress(vpninfo, loglevel, "%c %s\n", prefix, line->data);
Aug 14, 2017
Aug 14, 2017
932
}
Jun 7, 2019
Jun 7, 2019
933
buf_free(line);
Aug 14, 2017
Aug 14, 2017
934
935
}
Dec 29, 2019
Dec 29, 2019
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
static int https_socket_closed(struct openconnect_info *vpninfo)
{
char c;
if (!openconnect_https_connected(vpninfo))
return 1;
if (recv(vpninfo->ssl_fd, &c, 1, MSG_PEEK) == 0) {
vpn_progress(vpninfo, PRG_DEBUG,
_("HTTPS socket closed by peer; reopening\n"));
openconnect_close_https(vpninfo, 0);
return 1;
}
return 0;
}
Oct 28, 2012
Oct 28, 2012
954
955
956
957
958
959
960
961
962
963
/* Inputs:
* method: GET or POST
* vpninfo->hostname: Host DNS name
* vpninfo->port: TCP port, typically 443
* vpninfo->urlpath: Relative path, e.g. /+webvpn+/foo.html
* request_body_type: Content type for a POST (e.g. text/html). Can be NULL.
* request_body: POST content
* form_buf: Callee-allocated buffer for server content
*
* Return value:
Jan 24, 2009
Jan 24, 2009
964
* < 0, on error
Oct 28, 2012
Oct 28, 2012
965
* >=0, on success, indicating the length of the data in *form_buf
Jan 24, 2009
Jan 24, 2009
966
*/
Jan 26, 2015
Jan 26, 2015
967
968
969
int do_https_request(struct openconnect_info *vpninfo, const char *method,
const char *request_body_type, struct oc_text_buf *request_body,
char **form_buf, int fetch_redirect)
Oct 1, 2008
Oct 1, 2008
970
{
Oct 9, 2019
Oct 9, 2019
971
struct oc_text_buf *buf;
Jul 24, 2014
Jul 24, 2014
972
int result;
May 22, 2013
May 22, 2013
973
int rq_retry;
Feb 3, 2014
Feb 3, 2014
974
int rlen, pad;
Nov 29, 2017
Nov 29, 2017
975
int i, auth = 0;
Feb 24, 2015
Feb 24, 2015
976
int max_redirects = 10;
Oct 15, 2012
Oct 15, 2012
977
Jul 27, 2014
Jul 27, 2014
978
if (request_body_type && buf_error(request_body))
Jul 24, 2014
Jul 24, 2014
979
980
return buf_error(request_body);
Oct 9, 2019
Oct 9, 2019
981
982
buf = buf_alloc();
May 22, 2013
May 22, 2013
983
redirected:
Feb 24, 2015
Feb 24, 2015
984
985
986
987
988
if (max_redirects-- <= 0) {
result = -EIO;
goto out;
}
Oct 28, 2012
Oct 28, 2012
989
990
vpninfo->redirect_type = REDIR_TYPE_NONE;
Oct 28, 2012
Oct 28, 2012
991
992
993
if (*form_buf) {
free(*form_buf);
*form_buf = NULL;
Nov 24, 2010
Nov 24, 2010
994
}
Oct 1, 2008
Oct 1, 2008
995
996
/*
Jan 14, 2016
Jan 14, 2016
997
998
999
1000
* A long time ago, I *wanted* to use an HTTP client library like cURL
* for this. But we need a *lot* of control over the underlying SSL
* transport, and we also have to do horrid tricks like the Juniper NC
* 'GET' request that actaully behaves like a 'CONNECT'.