main.c 55.5 KB
Newer Older
1
/*
2
 * OpenConnect (SSL + DTLS) VPN client
3
 *
David Woodhouse's avatar
David Woodhouse committed
4
 * Copyright © 2008-2015 Intel Corporation.
5
 * Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
6
 * Copyright © 2013 John Morrissey <jwm@horde.net>
7
 *
8 9 10
 * Author: David Woodhouse <dwmw2@infradead.org>
 *
 * This program is free software; you can redistribute it and/or
David Woodhouse's avatar
David Woodhouse committed
11
 * modify it under the terms of the GNU Lesser General Public License
12
 * version 2.1, as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful, but
David Woodhouse's avatar
David Woodhouse committed
15 16 17
 * 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.
18 19
 */

20 21
#include <config.h>

22 23 24 25 26
#ifdef HAVE_GETLINE
/* Various BSD systems require this for getline() to be visible */
#define _WITH_GETLINE
#endif

27
#include <stdio.h>
28
#include <stdarg.h>
29
#include <stdlib.h>
30
#include <signal.h>
31
#include <string.h>
32 33 34
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
35 36 37
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
38 39
#include <sys/types.h>
#include <getopt.h>
40
#include <time.h>
41
#include <locale.h>
42 43 44

#ifdef LIBPROXY_HDR
#include LIBPROXY_HDR
45
#endif
46

47
#include "openconnect-internal.h"
48

49
#ifdef _WIN32
50
#include <shlwapi.h>
51 52
#include <wtypes.h>
#include <wincon.h>
53 54 55 56
#else
#include <sys/utsname.h>
#include <pwd.h>
#include <termios.h>
57
#endif
58

59 60 61 62 63 64
#ifdef HAVE_NL_LANGINFO
#include <langinfo.h>

static const char *legacy_charset;
#endif

65
static int write_new_config(void *_vpninfo,
66
			    const char *buf, int buflen);
67 68
static void __attribute__ ((format(printf, 3, 4)))
    write_progress(void *_vpninfo, int level, const char *fmt, ...);
69
static int validate_peer_cert(void *_vpninfo, const char *reason);
70 71
static int process_auth_form_cb(void *_vpninfo,
				struct oc_auth_form *form);
72 73
static void init_token(struct openconnect_info *vpninfo,
		       oc_token_mode_t token_mode, const char *token_str);
74

75 76
/* A sanity check that the openconnect executable is running against a
   library of the same version */
77
#define openconnect_version_str openconnect_binary_version
78
#include <version.c>
79
#undef openconnect_version_str
80

81 82
static int verbose = PRG_INFO;
static int timestamp;
83
int background;
84 85 86
static int do_passphrase_from_fsid;
static int non_inter;
static int cookieonly;
87
static int allow_stdin_read;
88

89
static char *token_filename;
90
static char *server_cert = NULL;
91

92 93 94 95 96 97 98
static char *username;
static char *password;
static char *authgroup;
static int authgroup_set;
static int last_form_empty;

static int sig_cmd_fd;
99

100 101
#ifdef __ANDROID__
#include <android/log.h>
102 103
static void __attribute__ ((format(printf, 3, 4)))
    syslog_progress(void *_vpninfo, int level, const char *fmt, ...)
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
{
	static int l[4] = {
		ANDROID_LOG_ERROR,	/* PRG_ERR   */
		ANDROID_LOG_INFO,	/* PRG_INFO  */
		ANDROID_LOG_DEBUG,	/* PRG_DEBUG */
		ANDROID_LOG_DEBUG	/* PRG_TRACE */
	};
	va_list args, args2;

	if (verbose >= level) {
		va_start(args, fmt);
		va_copy(args2, args);
		__android_log_vprint(l[level], "openconnect", fmt, args);
		/* Android wants it to stderr too, so the GUI can scrape
		   it and display it as well as going to syslog */
		vfprintf(stderr, fmt, args2);
		va_end(args);
		va_end(args2);
	}
}
124
#define openlog(...)  /* */
125
#elif defined(_WIN32) || defined(__native_client__)
126 127 128 129
/*
 * FIXME: Perhaps we could implement syslog_progress() using these APIs:
 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa364148%28v=vs.85%29.aspx
 */
130
#else /* !__ANDROID__ && !_WIN32 && !__native_client__ */
131
#include <syslog.h>
132 133
static void  __attribute__ ((format(printf, 3, 4)))
    syslog_progress(void *_vpninfo, int level, const char *fmt, ...)
134 135 136 137 138 139 140 141 142 143 144 145
{
	int priority = level ? LOG_INFO : LOG_NOTICE;
	va_list args;

	if (verbose >= level) {
		va_start(args, fmt);
		vsyslog(priority, fmt, args);
		va_end(args);
	}
}
#endif

146
enum {
147 148
	OPT_AUTHENTICATE = 0x100,
	OPT_AUTHGROUP,
149
	OPT_BASEMTU,
150
	OPT_CAFILE,
151
	OPT_COMPRESSION,
152
	OPT_CONFIGFILE,
153 154 155
	OPT_COOKIEONLY,
	OPT_COOKIE_ON_STDIN,
	OPT_CSD_USER,
Paul Brook's avatar
Paul Brook committed
156
	OPT_CSD_WRAPPER,
157 158
	OPT_DISABLE_IPV6,
	OPT_DTLS_CIPHERS,
159
	OPT_DUMP_HTTP,
160
	OPT_FORCE_DPD,
161
	OPT_GNUTLS_DEBUG,
162
	OPT_JUNIPER,
163 164 165 166 167
	OPT_KEY_PASSWORD_FROM_FSID,
	OPT_LIBPROXY,
	OPT_NO_CERT_CHECK,
	OPT_NO_DTLS,
	OPT_NO_HTTP_KEEPALIVE,
168
	OPT_NO_SYSTEM_TRUST,
169 170
	OPT_NO_PASSWD,
	OPT_NO_PROXY,
171
	OPT_NO_XMLPOST,
Steven Allen's avatar
Steven Allen committed
172
	OPT_PIDFILE,
173 174 175 176
	OPT_PASSWORD_ON_STDIN,
	OPT_PRINTCOOKIE,
	OPT_RECONNECT_TIMEOUT,
	OPT_SERVERCERT,
177
	OPT_RESOLVE,
178
	OPT_USERAGENT,
David Woodhouse's avatar
David Woodhouse committed
179
	OPT_NON_INTER,
180
	OPT_DTLS_LOCAL_PORT,
181 182
	OPT_TOKEN_MODE,
	OPT_TOKEN_SECRET,
183
	OPT_OS,
184
	OPT_TIMESTAMP,
185
	OPT_PFS,
186
	OPT_PROXY_AUTH,
187
	OPT_HTTP_AUTH,
188
	OPT_LOCAL_HOSTNAME,
David Woodhouse's avatar
David Woodhouse committed
189
	OPT_PROTOCOL,
190
	OPT_PASSTOS,
191 192
};

193 194 195 196 197 198 199 200 201 202
#ifdef __sun__
/*
 * The 'name' field in Solaris 'struct option' lacks the 'const', and causes
 * lots of warnings unless we cast it... https://www.illumos.org/issues/1881
*/
#define OPTION(name, arg, abbrev) {(char *)name, arg, NULL, abbrev}
#else
#define OPTION(name, arg, abbrev) {name, arg, NULL, abbrev}
#endif

203
static const struct option long_options[] = {
204
#ifndef _WIN32
205 206
	OPTION("background", 0, 'b'),
	OPTION("pid-file", 1, OPT_PIDFILE),
207 208 209 210 211
	OPTION("setuid", 1, 'U'),
	OPTION("script-tun", 0, 'S'),
	OPTION("syslog", 0, 'l'),
	OPTION("csd-user", 1, OPT_CSD_USER),
	OPTION("csd-wrapper", 1, OPT_CSD_WRAPPER),
212 213
#endif
	OPTION("pfs", 0, OPT_PFS),
214 215 216
	OPTION("certificate", 1, 'c'),
	OPTION("sslkey", 1, 'k'),
	OPTION("cookie", 1, 'C'),
217
	OPTION("compression", 1, OPT_COMPRESSION),
218
	OPTION("deflate", 0, 'd'),
219
	OPTION("juniper", 0, OPT_JUNIPER),
220 221 222 223
	OPTION("no-deflate", 0, 'D'),
	OPTION("cert-expire-warning", 1, 'e'),
	OPTION("usergroup", 1, 'g'),
	OPTION("help", 0, 'h'),
224
	OPTION("http-auth", 1, OPT_HTTP_AUTH),
225 226
	OPTION("interface", 1, 'i'),
	OPTION("mtu", 1, 'm'),
227
	OPTION("base-mtu", 1, OPT_BASEMTU),
228
	OPTION("script", 1, 's'),
229
	OPTION("timestamp", 0, OPT_TIMESTAMP),
230
	OPTION("passtos", 0, OPT_PASSTOS),
231 232
	OPTION("key-password", 1, 'p'),
	OPTION("proxy", 1, 'P'),
233
	OPTION("proxy-auth", 1, OPT_PROXY_AUTH),
234 235 236 237
	OPTION("user", 1, 'u'),
	OPTION("verbose", 0, 'v'),
	OPTION("version", 0, 'V'),
	OPTION("cafile", 1, OPT_CAFILE),
238
	OPTION("config", 1, OPT_CONFIGFILE),
239
	OPTION("no-dtls", 0, OPT_NO_DTLS),
240
	OPTION("authenticate", 0, OPT_AUTHENTICATE),
241 242 243 244 245 246 247 248 249 250 251 252
	OPTION("cookieonly", 0, OPT_COOKIEONLY),
	OPTION("printcookie", 0, OPT_PRINTCOOKIE),
	OPTION("quiet", 0, 'q'),
	OPTION("queue-len", 1, 'Q'),
	OPTION("xmlconfig", 1, 'x'),
	OPTION("cookie-on-stdin", 0, OPT_COOKIE_ON_STDIN),
	OPTION("passwd-on-stdin", 0, OPT_PASSWORD_ON_STDIN),
	OPTION("no-passwd", 0, OPT_NO_PASSWD),
	OPTION("reconnect-timeout", 1, OPT_RECONNECT_TIMEOUT),
	OPTION("dtls-ciphers", 1, OPT_DTLS_CIPHERS),
	OPTION("authgroup", 1, OPT_AUTHGROUP),
	OPTION("servercert", 1, OPT_SERVERCERT),
253
	OPTION("resolve", 1, OPT_RESOLVE),
254 255
	OPTION("key-password-from-fsid", 0, OPT_KEY_PASSWORD_FROM_FSID),
	OPTION("useragent", 1, OPT_USERAGENT),
256
	OPTION("local-hostname", 1, OPT_LOCAL_HOSTNAME),
257 258 259 260 261 262 263
	OPTION("disable-ipv6", 0, OPT_DISABLE_IPV6),
	OPTION("no-proxy", 0, OPT_NO_PROXY),
	OPTION("libproxy", 0, OPT_LIBPROXY),
	OPTION("no-http-keepalive", 0, OPT_NO_HTTP_KEEPALIVE),
	OPTION("no-cert-check", 0, OPT_NO_CERT_CHECK),
	OPTION("force-dpd", 1, OPT_FORCE_DPD),
	OPTION("non-inter", 0, OPT_NON_INTER),
264
	OPTION("dtls-local-port", 1, OPT_DTLS_LOCAL_PORT),
265
	OPTION("token-mode", 1, OPT_TOKEN_MODE),
266
	OPTION("token-secret", 1, OPT_TOKEN_SECRET),
267
	OPTION("os", 1, OPT_OS),
268
	OPTION("no-xmlpost", 0, OPT_NO_XMLPOST),
269
	OPTION("dump-http-traffic", 0, OPT_DUMP_HTTP),
270
	OPTION("no-system-trust", 0, OPT_NO_SYSTEM_TRUST),
David Woodhouse's avatar
David Woodhouse committed
271
	OPTION("protocol", 1, OPT_PROTOCOL),
272 273 274
#ifdef OPENCONNECT_GNUTLS
	OPTION("gnutls-debug", 1, OPT_GNUTLS_DEBUG),
#endif
275
	OPTION(NULL, 0, 0)
276 277
};

278 279 280 281 282 283 284
#ifdef OPENCONNECT_GNUTLS
static void oc_gnutls_log_func(int level, const char *str)
{
	fputs(str, stderr);
}
#endif

285
#ifdef _WIN32
286 287
static int __attribute__ ((format(printf, 2, 0)))
    vfprintf_utf8(FILE *f, const char *fmt, va_list args)
288 289 290 291 292 293 294 295 296 297 298 299 300 301
{
	HANDLE h = GetStdHandle(f == stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
	wchar_t wbuf[1024];
	char buf[1024];
	int chars, wchars;

	buf[sizeof(buf) - 1] = 0;
	chars = _vsnprintf(buf, sizeof(buf) - 1, fmt, args);
	wchars = MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf)/2);
	WriteConsoleW(h, wbuf, wchars, NULL, NULL);

	return chars;
}

302 303
static int __attribute__ ((format(printf, 2, 3)))
    fprintf_utf8(FILE *f, const char *fmt, ...)
304 305 306 307 308 309 310 311 312 313
{
	va_list args;
	int ret;

	va_start(args, fmt);
	ret = vfprintf_utf8(f, fmt, args);
	va_end(args);

	return ret;
}
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331

static wchar_t **argv_w;

/* This isn't so much "convert" the arg to UTF-8, as go grubbing
 * around in the real UTF-16 command line and find the corresponding
 * argument *there*, and convert *that* to UTF-8. Ick. But the
 * alternative is to implement wgetopt(), and that's even more horrid. */
static char *convert_arg_to_utf8(char **argv, char *arg)
{
	char *utf8;
	int chars;
	int offset;

	if (!argv_w) {
		int argc_w;

		argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
		if (!argv_w) {
332 333 334 335
			char *errstr = openconnect__win32_strerror(GetLastError());
			fprintf(stderr, _("CommandLineToArgvW() failed: %s\n"),
				errstr);
			free(errstr);
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
			exit(1);
		}
	}

	offset = arg - argv[optind - 1];

	/* Sanity check */
	if (offset < 0 || offset >= strlen(argv[optind - 1]) ||
	    (offset && (argv[optind - 1][offset-1] != '=' ||
			argv_w[optind - 1][offset - 1] != '='))) {
		fprintf(stderr, _("Fatal error in command line handling\n"));
		exit(1);
	}

	chars = WideCharToMultiByte(CP_UTF8, 0, argv_w[optind-1] + offset, -1,
				    NULL, 0, NULL, NULL);
	utf8 = malloc(chars);
	if (!utf8)
		return arg;

	WideCharToMultiByte(CP_UTF8, 0, argv_w[optind-1] + offset, -1, utf8,
			    chars, NULL, NULL);
	return utf8;
}

361 362 363 364
#undef fprintf
#undef vfprintf
#define fprintf fprintf_utf8
#define vfprintf vfprintf_utf8
365
#define is_arg_utf8(str) (0)
366

367
static void read_stdin(char **string, int hidden, int allow_fail)
368 369
{
	CONSOLE_READCONSOLE_CONTROL rcc = { sizeof(rcc), 0, 13, 0 };
370
	HANDLE conh, stdinh = GetStdHandle(STD_INPUT_HANDLE);
371 372 373 374
	DWORD cmode, nr_read;
	wchar_t wbuf[1024];
	char *buf;

375 376 377 378 379 380 381 382 383 384 385 386 387
	conh = stdinh;
	if (!GetConsoleMode(conh, &cmode)) {
		/* STDIN is not a console? Try opening it explicitly */
		conh = CreateFile("CONIN$", GENERIC_READ, FILE_SHARE_READ,
				  0,OPEN_EXISTING, 0, 0);
		if (conh == INVALID_HANDLE_VALUE || !GetConsoleMode(conh, &cmode)) {
			char *errstr = openconnect__win32_strerror(GetLastError());
			fprintf(stderr, _("Failed to open CONIN$: %s\n"), errstr);
			free(errstr);
			*string = NULL;
			goto out;
		}
	}
388
	if (hidden) {
389
		SetConsoleMode(conh, cmode & (~ENABLE_ECHO_INPUT));
390 391
	}

392
	if (!ReadConsoleW(conh, wbuf, sizeof(wbuf)/2, &nr_read, &rcc)) {
393 394 395
		char *errstr = openconnect__win32_strerror(GetLastError());
		fprintf(stderr, _("ReadConsole() failed: %s\n"), errstr);
		free(errstr);
396
		*string = NULL;
397 398 399 400 401 402 403 404 405 406
		goto out;
	}

	if (nr_read >= 2 && wbuf[nr_read - 1] == 10 && wbuf[nr_read - 2] == 13) {
		wbuf[nr_read - 2] = 0;
		nr_read -= 2;
	}

	nr_read = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, NULL, 0, NULL, NULL);
	if (!nr_read) {
407
		char *errstr = openconnect__win32_strerror(GetLastError());
408
		fprintf(stderr, _("Error converting console input: %s\n"),
409 410
			errstr);
		free(errstr);
411 412 413 414 415 416 417 418 419
		goto out;
	}
	buf = malloc(nr_read);
	if (!buf) {
		fprintf(stderr, _("Allocation failure for string from stdin\n"));
		exit(1);
	}

	if (!WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, nr_read, NULL, NULL)) {
420 421 422 423
		char *errstr = openconnect__win32_strerror(GetLastError());
		fprintf(stderr, _("Error converting console input: %s\n"),
			errstr);
		free(errstr);
424 425 426 427 428 429 430 431
		free(buf);
		goto out;
	}

	*string = buf;

out:
	if (hidden) {
432
		SetConsoleMode(conh, cmode);
433 434
		fprintf(stderr, "\n");
	}
435 436
	if (conh != stdinh && conh != INVALID_HANDLE_VALUE)
		CloseHandle(conh);
437 438
}

439
#elif defined(HAVE_ICONV)
440 441
#include <iconv.h>

442 443 444 445 446 447 448 449 450 451 452
static int is_ascii(char *str)
{
	while (str && *str) {
		if ((unsigned char)*str > 0x7f)
			return 0;
		str++;
	}

	return 1;
}

453 454
static int __attribute__ ((format(printf, 2, 0)))
    vfprintf_utf8(FILE *f, const char *fmt, va_list args)
455 456 457 458 459
{
	char *utf8_str;
	iconv_t ic;
	int ret;
	char outbuf[80];
460 461
	ICONV_CONST char *ic_in;
	char *ic_out;
462 463 464 465 466 467 468 469 470
	size_t insize, outsize;

	if (!legacy_charset)
		return vfprintf(f, fmt, args);

	ret = vasprintf(&utf8_str, fmt, args);
	if (ret < 0)
		return -1;

471 472 473
	if (is_ascii(utf8_str))
		return fwrite(utf8_str, 1, strlen(utf8_str), f);

474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
	ic = iconv_open(legacy_charset, "UTF-8");
	if (ic == (iconv_t) -1) {
		/* Better than nothing... */
		ret = fprintf(f, "%s", utf8_str);
		free(utf8_str);
		return ret;
	}

	ic_in = utf8_str;
	insize = strlen(utf8_str);
	ret = 0;

	while (insize) {
		ic_out = outbuf;
		outsize = sizeof(outbuf) - 1;

490
		if (iconv(ic, &ic_in, &insize, &ic_out, &outsize) == (size_t)-1) {
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
			if (errno == EILSEQ) {
				do {
					ic_in++;
					insize--;
				} while (insize && (ic_in[0] & 0xc0) == 0x80);
				ic_out[0] = '?';
				outsize--;
			} else if (errno != E2BIG)
				break;
		}
		ret += fwrite(outbuf, 1, sizeof(outbuf) - 1 - outsize, f);
	}

	iconv_close(ic);

	return ret;
}

509 510
static int __attribute__ ((format(printf, 2, 3)))
    fprintf_utf8(FILE *f, const char *fmt, ...)
511 512 513 514 515 516 517 518 519 520 521
{
	va_list args;
	int ret;

	va_start(args, fmt);
	ret = vfprintf_utf8(f, fmt, args);
	va_end(args);

	return ret;
}

522
static char *convert_to_utf8(char *legacy, int free_it)
523 524 525
{
	char *utf8_str;
	iconv_t ic;
526 527
	ICONV_CONST char *ic_in;
	char *ic_out;
528 529
	size_t insize, outsize;

530
	if (!legacy_charset || is_ascii(legacy))
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
		return legacy;

	ic = iconv_open("UTF-8", legacy_charset);
	if (ic == (iconv_t) -1)
		return legacy;

	insize = strlen(legacy) + 1;
	ic_in = legacy;

	outsize = insize;
	ic_out = utf8_str = malloc(outsize);
	if (!utf8_str) {
	enomem:
		iconv_close(ic);
		return legacy;
	}

	while (insize) {
549
		if (iconv(ic, &ic_in, &insize, &ic_out, &outsize) == (size_t)-1) {
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
			if (errno == E2BIG) {
				int outlen = ic_out - utf8_str;
				realloc_inplace(utf8_str, outlen + 10);
				if (!utf8_str)
					goto enomem;
				ic_out = utf8_str + outlen;
				outsize = 10;
			} else {
				/* Should never happen */
				perror("iconv");
				free(utf8_str);
				goto enomem;
			}
		}
	}

	iconv_close(ic);
567 568
	if (free_it)
		free(legacy);
569 570 571
	return utf8_str;
}

572 573
#define fprintf fprintf_utf8
#define vfprintf vfprintf_utf8
574 575
#define convert_arg_to_utf8(av, l) convert_to_utf8((l), 0)
#define is_arg_utf8(a) (!legacy_charset || is_ascii(a))
576
#else
577 578 579
#define convert_to_utf8(l,f) (l)
#define convert_arg_to_utf8(av, l) (l)
#define is_arg_utf8(a) (1)
580 581
#endif

582 583 584 585 586 587
static void helpmessage(void)
{
	printf(_("For assistance with OpenConnect, please see the web page at\n"
		 "  http://www.infradead.org/openconnect/mail.html\n"));
}

588 589
static void print_build_opts(void)
{
590 591
	const char *comma = ", ", *sep = comma + 1;

592
#if defined(OPENCONNECT_OPENSSL)
593
	printf(_("Using OpenSSL. Features present:"));
594
#elif defined(OPENCONNECT_GNUTLS)
595 596 597 598 599 600 601
	printf(_("Using GnuTLS. Features present:"));
#endif

	if (openconnect_has_tss_blob_support()) {
		printf("%sTPM", sep);
		sep = comma;
	}
602
#if defined(OPENCONNECT_OPENSSL) && defined(HAVE_ENGINE)
603 604 605 606 607 608 609 610 611
	else {
		printf("%sTPM (%s)", sep, _("OpenSSL ENGINE not present"));
		sep = comma;
	}
#endif
	if (openconnect_has_pkcs11_support()) {
		printf("%sPKCS#11", sep);
		sep = comma;
	}
612
	if (openconnect_has_stoken_support()) {
613 614 615
		printf("%sRSA software token", sep);
		sep = comma;
	}
616 617 618 619 620
	switch(openconnect_has_oath_support()) {
	case 2:
		printf("%sHOTP software token", sep);
		sep = comma;
	case 1:
621
		printf("%sTOTP software token", sep);
622 623
		sep = comma;
	}
624 625 626 627
	if (openconnect_has_yubioath_support()) {
		printf("%sYubikey OATH", sep);
		sep = comma;
	}
628 629 630 631
	if (openconnect_has_system_key_support()) {
		printf("%sSystem keys", sep);
		sep = comma;
	}
632 633

#ifdef HAVE_DTLS
634 635 636 637 638 639 640 641 642
	printf("%sDTLS", sep);
#endif
#ifdef HAVE_ESP
	printf("%sESP", sep);
#endif
	printf("\n");

#if !defined(HAVE_DTLS) || !defined(HAVE_ESP)
	printf(_("WARNING: This binary lacks DTLS and/or ESP support. Performance will be impaired.\n"));
643 644 645
#endif
}

646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
static void print_supported_protocols(void)
{
	const char *comma = ", ", *sep = comma + 1;
	struct oc_vpn_proto *protos, *p;

	if (openconnect_get_supported_protocols(&protos)>=0) {
		printf(_("Supported protocols:"));
		for (p=protos; p->name; p++) {
			printf("%s%s%s", sep, p->name, p==protos ? _(" (default)") : "");
			sep = comma;
		}
		printf("\n");
		free(protos);
	}
}

static void print_supported_protocols_usage(void)
{
	struct oc_vpn_proto *protos, *p;

	if (openconnect_get_supported_protocols(&protos)>=0) {
		printf(_("\n    Set VPN protocol:\n"));
		for (p=protos; p->name; p++)
			printf("      --protocol=%-16s %s%s\n",
				   p->name, p->description, p==protos ? _(" (default)") : "");
		openconnect_free_supported_protocols(protos);
	}
}

675 676
#ifndef _WIN32
static const char default_vpncscript[] = DEFAULT_VPNCSCRIPT;
677
static void read_stdin(char **string, int hidden, int allow_fail)
678
{
679
	char *c, *buf = malloc(1025);
680
	int fd = fileno(stdin);
681
	struct termios t;
682

683 684 685 686
	if (!buf) {
		fprintf(stderr, _("Allocation failure for string from stdin\n"));
		exit(1);
	}
687

688 689 690 691 692 693
	if (hidden) {
		tcgetattr(fd, &t);
		t.c_lflag &= ~ECHO;
		tcsetattr(fd, TCSANOW, &t);
	}

694
	buf = fgets(buf, 1025, stdin);
695 696 697 698 699 700 701

	if (hidden) {
		t.c_lflag |= ECHO;
		tcsetattr(fd, TCSANOW, &t);
		fprintf(stderr, "\n");
	}

702
	if (!buf) {
703 704 705 706 707 708 709 710
		if (allow_fail) {
			*string = NULL;
			free(buf);
			return;
		} else {
			perror(_("fgets (stdin)"));
			exit(1);
		}
711 712
	}

713 714 715
	c = strchr(buf, '\n');
	if (c)
		*c = 0;
716

717
	*string = convert_to_utf8(buf, 1);
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
}

static void handle_signal(int sig)
{
	char cmd;

	switch (sig) {
	case SIGINT:
		cmd = OC_CMD_CANCEL;
		break;
	case SIGHUP:
		cmd = OC_CMD_DETACH;
		break;
	case SIGUSR2:
	default:
		cmd = OC_CMD_PAUSE;
		break;
	}

	if (write(sig_cmd_fd, &cmd, 1) < 0) {
	/* suppress warn_unused_result */
	}
}
#else /* _WIN32 */
742 743 744 745 746 747 748 749 750 751
static const char *default_vpncscript;
static void set_default_vpncscript(void)
{
	if (PathIsRelative(DEFAULT_VPNCSCRIPT)) {
		char *c = strrchr(_pgmptr, '\\');
		if (!c) {
			fprintf(stderr, _("Cannot process this executable path \"%s\""),
				_pgmptr);
			exit(1);
		}
752
		if (asprintf((char **)&default_vpncscript, "%.*s%s",
753 754 755 756 757 758 759 760 761 762
			     (c - _pgmptr + 1), _pgmptr, DEFAULT_VPNCSCRIPT) < 0) {
			fprintf(stderr, _("Allocation for vpnc-script path failed\n"));
			exit(1);
		}
	} else {
		default_vpncscript = "cscript " DEFAULT_VPNCSCRIPT;
	}
}
#endif

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
static struct oc_vpn_option *gai_overrides;

static int gai_override_cb(void *cbdata, const char *node,
			    const char *service, const struct addrinfo *hints,
			    struct addrinfo **res)
{
	struct openconnect_info *vpninfo = cbdata;
	struct oc_vpn_option *p = gai_overrides;

	while (p) {
		if (!strcmp(node, p->option)) {
			vpn_progress(vpninfo, PRG_TRACE, _("Override hostname '%s' to '%s'\n"),
				     node, p->value);
			node = p->value;
			break;
		}
		p = p->next;
	}

	return getaddrinfo(node, service, hints, res);
}

785
static void usage(void)
786
{
787
	printf(_("Usage:  openconnect [options] <server>\n"));
788
	printf(_("Open client for multiple VPN protocols, version %s\n\n"), openconnect_version_str);
789
	print_build_opts();
790
	printf("      --config=CONFIGFILE         %s\n", _("Read options from config file"));
791
#ifndef _WIN32
792
	printf("  -b, --background                %s\n", _("Continue in background after startup"));
793
	printf("      --pid-file=PIDFILE          %s\n", _("Write the daemon's PID to this file"));
794
#endif
795 796 797 798 799 800 801 802 803 804 805
	printf("  -c, --certificate=CERT          %s\n", _("Use SSL client certificate CERT"));
	printf("  -e, --cert-expire-warning=DAYS  %s\n", _("Warn when certificate lifetime < DAYS"));
	printf("  -k, --sslkey=KEY                %s\n", _("Use SSL private key file KEY"));
	printf("  -C, --cookie=COOKIE             %s\n", _("Use WebVPN cookie COOKIE"));
	printf("      --cookie-on-stdin           %s\n", _("Read cookie from standard input"));
	printf("  -d, --deflate                   %s\n", _("Enable compression (default)"));
	printf("  -D, --no-deflate                %s\n", _("Disable compression"));
	printf("      --force-dpd=INTERVAL        %s\n", _("Set minimum Dead Peer Detection interval"));
	printf("  -g, --usergroup=GROUP           %s\n", _("Set login usergroup"));
	printf("  -h, --help                      %s\n", _("Display help text"));
	printf("  -i, --interface=IFNAME          %s\n", _("Use IFNAME for tunnel interface"));
806
#ifndef _WIN32
807
	printf("  -l, --syslog                    %s\n", _("Use syslog for progress messages"));
808
#endif
809
	printf("      --timestamp                 %s\n", _("Prepend timestamp to progress messages"));
810
	printf("      --passtos                   %s\n", _("copy TOS / TCLASS when using DTLS"));
811
#ifndef _WIN32
812 813 814
	printf("  -U, --setuid=USER               %s\n", _("Drop privileges after connecting"));
	printf("      --csd-user=USER             %s\n", _("Drop privileges during CSD execution"));
	printf("      --csd-wrapper=SCRIPT        %s\n", _("Run SCRIPT instead of CSD binary"));
815
#endif
816
	printf("  -m, --mtu=MTU                   %s\n", _("Request MTU from server (legacy servers only)"));
817
	printf("      --base-mtu=MTU              %s\n", _("Indicate path MTU to/from server"));
818 819 820
	printf("  -p, --key-password=PASS         %s\n", _("Set key passphrase or TPM SRK PIN"));
	printf("      --key-password-from-fsid    %s\n", _("Key passphrase is fsid of file system"));
	printf("  -P, --proxy=URL                 %s\n", _("Set proxy server"));
821
	printf("      --proxy-auth=METHODS        %s\n", _("Set proxy authentication methods"));
822 823
	printf("      --no-proxy                  %s\n", _("Disable proxy"));
	printf("      --libproxy                  %s\n", _("Use libproxy to automatically configure proxy"));
David Woodhouse's avatar
David Woodhouse committed
824
#ifndef LIBPROXY_HDR
825
	printf("                                  %s\n", _("(NOTE: libproxy disabled in this build)"));
826
#endif
827
	printf("      --pfs                       %s\n", _("Require perfect forward secrecy"));
828 829 830
	printf("  -q, --quiet                     %s\n", _("Less output"));
	printf("  -Q, --queue-len=LEN             %s\n", _("Set packet queue limit to LEN pkts"));
	printf("  -s, --script=SCRIPT             %s\n", _("Shell command line for using a vpnc-compatible config script"));
831
	printf("                                  %s: \"%s\"\n", _("default"), default_vpncscript);
832
#ifndef _WIN32
833
	printf("  -S, --script-tun                %s\n", _("Pass traffic to 'script' program, not tun"));
834
#endif
835 836 837
	printf("  -u, --user=NAME                 %s\n", _("Set login username"));
	printf("  -V, --version                   %s\n", _("Report version number"));
	printf("  -v, --verbose                   %s\n", _("More output"));
838
	printf("      --dump-http-traffic         %s\n", _("Dump HTTP authentication traffic (implies --verbose"));
839 840
	printf("  -x, --xmlconfig=CONFIG          %s\n", _("XML config file"));
	printf("      --authgroup=GROUP           %s\n", _("Choose authentication login selection"));
841
	printf("      --authenticate              %s\n", _("Authenticate only and print login info"));
842 843 844 845 846 847 848 849 850
	printf("      --cookieonly                %s\n", _("Fetch webvpn cookie only; don't connect"));
	printf("      --printcookie               %s\n", _("Print webvpn cookie before connecting"));
	printf("      --cafile=FILE               %s\n", _("Cert file for server verification"));
	printf("      --disable-ipv6              %s\n", _("Do not ask for IPv6 connectivity"));
	printf("      --dtls-ciphers=LIST         %s\n", _("OpenSSL ciphers to support for DTLS"));
	printf("      --no-dtls                   %s\n", _("Disable DTLS"));
	printf("      --no-http-keepalive         %s\n", _("Disable HTTP connection re-use"));
	printf("      --no-passwd                 %s\n", _("Disable password/SecurID authentication"));
	printf("      --no-cert-check             %s\n", _("Do not require server SSL cert to be valid"));
851
	printf("      --no-system-trust           %s\n", _("Disable default system certificate authorities"));
852
	printf("      --no-xmlpost                %s\n", _("Do not attempt XML POST authentication"));
853 854
	printf("      --non-inter                 %s\n", _("Do not expect user input; exit if it is required"));
	printf("      --passwd-on-stdin           %s\n", _("Read password from standard input"));
855
	printf("      --token-mode=MODE           %s\n", _("Software token type: rsa, totp or hotp"));
856
	printf("      --token-secret=STRING       %s\n", _("Software token secret"));
857
#ifndef HAVE_LIBSTOKEN
858 859
	printf("                                  %s\n", _("(NOTE: libstoken (RSA SecurID) disabled in this build)"));
#endif
860 861
#ifndef HAVE_LIBPCSCLITE
	printf("                                  %s\n", _("(NOTE: Yubikey OATH disabled in this build)"));
862
#endif
863 864 865
	printf("      --reconnect-timeout         %s\n", _("Connection retry timeout in seconds"));
	printf("      --servercert=FINGERPRINT    %s\n", _("Server's certificate SHA1 fingerprint"));
	printf("      --useragent=STRING          %s\n", _("HTTP header User-Agent: field"));
866
	printf("      --local-hostname=STRING     %s\n", _("Local hostname to advertise to server"));
867
	printf("      --resolve=HOST:IP           %s\n", _("Use IP when connecting to HOST"));
868
	printf("      --os=STRING                 %s\n", _("OS type (linux,linux-64,win,...) to report"));
869
	printf("      --dtls-local-port=PORT      %s\n", _("Set local port for DTLS datagrams"));
870 871
	print_supported_protocols_usage();

872 873 874
	printf("\n");

	helpmessage();
875 876 877
	exit(1);
}

878

879 880 881
static FILE *config_file = NULL;
static int config_line_num = 0;

882 883
static char *xstrdup(const char *arg)
{
884 885 886 887 888 889
	char *ret;

	if (!arg)
		return NULL;

	ret = strdup(arg);
890 891 892 893 894 895 896 897

	if (!ret) {
		fprintf(stderr, _("Failed to allocate string\n"));
		exit(1);
	}
	return ret;
}

898 899 900 901
/* There are three ways to handle config_arg:
 *
 * 1. We only care about it transiently and it can be lost entirely
 *    (e.g. vpninfo->reconnect_timeout = atoi(config_arg);
902
 * 2. We need to keep it, but it's a static string and will never be freed
903 904 905
 *    so when it's part of argv[] we can use it in place (unless it needs
 *    converting to UTF-8), but when it comes from a file we have to strdup()
 *    because otherwise it'll be overwritten.
906 907
 *    For this we use the keep_config_arg() macro below.
 * 3. It may be freed during normal operation, so we have to use strdup()
908 909 910
 *    or convert_arg_to_utf8() even when it's an option from argv[].
 *    (e.g. vpninfo->cert_password).
 *    For this we use the dup_config_arg() macro below.
911
 */
912

913 914 915 916 917 918
#define keep_config_arg() \
	(config_file ? xstrdup(config_arg) : convert_arg_to_utf8(argv, config_arg))

#define dup_config_arg() \
	((config_file || is_arg_utf8(config_arg)) ? xstrdup(config_arg) : \
	 convert_arg_to_utf8(argv, config_arg))
919

920 921
static int next_option(int argc, char **argv, char **config_arg)
{
922 923 924 925 926
	/* These get re-used */
	static char *line_buf = NULL;
	static size_t line_size = 0;

	ssize_t llen;
927
	int opt, optlen = 0;
928
	const struct option *this;
929 930 931 932 933 934
	char *line;
	int ate_equals = 0;

 next:
	if (!config_file) {
		opt = getopt_long(argc, argv,
935
#ifdef _WIN32
936
				  "C:c:Dde:g:hi:k:m:P:p:Q:qs:u:Vvx:",
937
#else
938
				  "bC:c:Dde:g:hi:k:lm:P:p:Q:qSs:U:u:Vvx:",
939
#endif
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983
				  long_options, NULL);

		*config_arg = optarg;
		return opt;
	}

	llen = getline(&line_buf, &line_size, config_file);
	if (llen < 0) {
		if (feof(config_file)) {
			fclose(config_file);
			config_file = NULL;
			goto next;
		}
		fprintf(stderr, _("Failed to get line from config file: %s\n"),
			strerror(errno));
		exit(1);
	}
	line = line_buf;

	/* Strip the trailing newline (coping with DOS newlines) */
	if (llen && line[llen-1] == '\n')
		line[--llen] = 0;
	if (llen && line[llen-1] == '\r')
		line[--llen] = 0;

	/* Skip and leading whitespace */
	while (line[0] == ' ' || line[0] == '\t' || line[0] == '\r')
		line++;

	/* Ignore comments and empty lines */
	if (!line[0] || line[0] == '#') {
		config_line_num++;
		goto next;
	}

	/* Try to match on a known option... naïvely. This could be improved. */
	for (this = long_options; this->name; this++) {
		optlen = strlen(this->name);
		/* If the option isn't followed by whitespace or NUL, or
		   perhaps an equals sign if the option takes an argument,
		   then it's not a match */
		if (!strncmp(this->name, line, optlen) &&
		    (!line[optlen] || line[optlen] == ' ' || line[optlen] == '\t' ||
		     line[optlen] == '='))
984
			break;
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
	}
	if (!this->name) {
		char *l;

		for (l = line; *l && *l != ' ' && *l != '\t'; l++)
			;

		*l = 0;
		fprintf(stderr, _("Unrecognised option at line %d: '%s'\n"),
			config_line_num, line);
		return '?';
	}
	line += optlen;
	while (*line == ' ' || *line == '\t' ||
	       (*line == '=' && this->has_arg && !ate_equals && ++ate_equals))
		line++;

	if (!this->has_arg && *line) {
		fprintf(stderr, _("Option '%s' does not take an argument at line %d\n"),
			this->name, config_line_num);
		return '?';
1006
	} else if (this->has_arg == 1 && !*line) {
1007 1008 1009
		fprintf(stderr, _("Option '%s' requires an argument at line %d\n"),
			this->name, config_line_num);
		return '?';
1010 1011
	} else if (this->has_arg == 2 && !*line) {
		line = NULL;
1012 1013 1014 1015 1016
	}

	config_line_num++;
	*config_arg = line;
	return this->val;
1017 1018

}
1019

1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
#ifndef _WIN32
static void get_uids(const char *config_arg, uid_t *uid, gid_t *gid)
{
	char *strend;
	struct passwd *pw;
	int e;

	*uid = strtol(config_arg, &strend, 0);
	if (strend[0]) {
		pw = getpwnam(config_arg);
		if (!pw) {
			e = errno;
			fprintf(stderr, _("Invalid user \"%s\": %s\n"),
				config_arg, strerror(e));
			exit(1);
		}
		*uid = pw->pw_uid;
		*gid = pw->pw_gid;
	} else {
		pw = getpwuid(*uid);
		if (!pw) {
			e = errno;
			fprintf(stderr, _("Invalid user ID \"%d\": %s\n"),
				(int)*uid, strerror(e));
			exit(1);
		}
		*gid = pw->pw_gid;
	}
}
#endif

1051 1052
int main(int argc, char **argv)
{
1053
	struct openconnect_info *vpninfo;
1054
	char *urlpath = NULL;
1055 1056
	struct oc_vpn_option *gai;
	char *ip;
1057
	const char *compr = "";
1058
	char *proxy = getenv("https_proxy");
1059
	char *vpnc_script = NULL;
1060
	const struct oc_ip_info *ip_info;
1061
	int autoproxy = 0;
1062
	int opt;
Steven Allen's avatar
Steven Allen committed
1063 1064
	char *pidfile = NULL;
	FILE *fp = NULL;
1065
	char *config_arg;
1066
	char *config_filename;
1067
	char *token_str = NULL;
1068
	oc_token_mode_t token_mode = OC_TOKEN_MODE_NONE;
1069
	int reconnect_timeout = 300;
1070
	int ret;
1071 1072 1073
#ifdef HAVE_NL_LANGINFO
	char *charset;
#endif
1074
#ifndef _WIN32
1075
	struct sigaction sa;
1076
	struct utsname utsbuf;
1077
	int use_syslog = 0;
1078
#endif
1079

1080
#ifdef ENABLE_NLS
1081
	bindtextdomain("openconnect", LOCALEDIR);
1082
#endif
1083 1084 1085 1086

	setlocale(LC_ALL, "");

#ifdef HAVE_NL_LANGINFO
1087 1088 1089
	charset = nl_langinfo(CODESET);
	if (charset && strcmp(charset, "UTF-8"))
		legacy_charset = strdup(charset);
1090 1091 1092 1093 1094 1095 1096

#ifndef HAVE_ICONV
	if (legacy_charset)
		fprintf(stderr,
			_("WARNING: This version of openconnect was built without iconv\n"
			  "         support but you appear to be using the legacy character\n"
			  "         set \"%s\". Expect strangeness.\n"), legacy_charset);
1097 1098
#endif /* !HAVE_ICONV */
#endif /* HAVE_NL_LANGINFO */
1099

1100
	if (strcmp(openconnect_version_str, openconnect_binary_version)) {
1101 1102
		fprintf(stderr, _("WARNING: This version of openconnect is %s but\n"
				  "         the libopenconnect library is %s\n"),
1103
			openconnect_binary_version, openconnect_version_str);
1104
	}
1105

1106
	openconnect_init_ssl();
David Woodhouse's avatar
David Woodhouse committed
1107

1108 1109
	vpninfo = openconnect_vpninfo_new((char *)"Open AnyConnect VPN Agent",
		validate_peer_cert, NULL, process_auth_form_cb, write_progress, NULL);
David Woodhouse's avatar
David Woodhouse committed
1110
	if (!vpninfo) {
1111
		fprintf(stderr, _("Failed to allocate vpninfo structure\n"));
David Woodhouse's avatar
David Woodhouse committed
1112 1113
		exit(1);
	}
1114

1115
	vpninfo->cbdata = vpninfo;
1116 1117 1118
#ifdef _WIN32
	set_default_vpncscript();
#else
1119 1120
	vpninfo->use_tun_script = 0;
	vpninfo->uid = getuid();
1121
	vpninfo->gid = getgid();
1122

1123
	if (!uname(&utsbuf)) {
1124
		openconnect_set_localname(vpninfo, utsbuf.nodename);
1125
	}
1126
#endif
David Woodhouse's avatar
David Woodhouse committed
1127

1128 1129
	while ((opt = next_option(argc, argv, &config_arg))) {

1130 1131 1132 1133
		if (opt < 0)
			break;

		switch (opt) {
1134 1135 1136 1137 1138 1139 1140 1141
#ifndef _WIN32
		case 'b':
			background = 1;
			break;
		case 'l':
			use_syslog = 1;
			break;
		case 'S':
1142
			vpninfo->use_tun_script = 1;
1143
			break;
1144 1145
		case 'U':
			get_uids(config_arg, &vpninfo->uid, &vpninfo->gid);
1146
			break;
1147 1148
		case OPT_CSD_USER:
			get_uids(config_arg, &vpninfo->uid_csd, &vpninfo->gid_csd);
1149 1150 1151 1152 1153 1154
			vpninfo->uid_csd_given = 1;
			break;
		case OPT_CSD_WRAPPER:
			vpninfo->csd_wrapper = keep_config_arg();
			break;
#endif /* !_WIN32 */
David Woodhouse's avatar
David Woodhouse committed
1155 1156 1157 1158
		case OPT_PROTOCOL:
			if (openconnect_set_protocol(vpninfo, config_arg))
				exit(1);
			break;
1159 1160 1161 1162 1163
		case OPT_JUNIPER:
			fprintf(stderr, "WARNING: Juniper Network Connect support is experimental.\n");
			fprintf(stderr, "It will probably be superseded by Junos Pulse support.\n");
			openconnect_set_protocol(vpninfo, "nc");
			break;
1164 1165 1166 1167 1168
		case OPT_CONFIGFILE:
			if (config_file) {
				fprintf(stderr, _("Cannot use 'config' option inside config file\n"));
				exit(1);
			}
1169 1170 1171 1172
			config_filename = keep_config_arg(); /* Convert to UTF-8 */
			config_file = openconnect_fopen_utf8(vpninfo, config_filename, "r");
			if (config_filename != config_arg)
				free(config_filename);
1173 1174 1175 1176 1177 1178 1179 1180
			if (!config_file) {
				fprintf(stderr, _("Cannot open config file '%s': %s\n"),
					config_arg, strerror(errno));
				exit(1);
			}
			config_line_num = 1;
			/* The next option will come from the file... */
			break;
1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
		case OPT_COMPRESSION:
			if (!strcmp(config_arg, "none") ||
			    !strcmp(config_arg, "off"))
				openconnect_set_compression_mode(vpninfo, OC_COMPRESSION_MODE_NONE);
			else if (!strcmp(config_arg, "all"))
				openconnect_set_compression_mode(vpninfo, OC_COMPRESSION_MODE_ALL);
			else if (!strcmp(config_arg, "stateless"))
				openconnect_set_compression_mode(vpninfo, OC_COMPRESSION_MODE_STATELESS);
			else {
				fprintf(stderr, _("Invalid compression mode '%s'\n"),
					config_arg);
				exit(1);
			}
			break;
1195
		case OPT_CAFILE:
1196
			openconnect_set_cafile(vpninfo, dup_config_arg());
1197
			break;
Steven Allen's avatar
Steven Allen committed
1198
		case OPT_PIDFILE:
1199
			pidfile = keep_config_arg();
Steven Allen's avatar
Steven Allen committed
1200
			break;
1201
		case OPT_PFS:
1202
			openconnect_set_pfs(vpninfo, 1);
1203
			break;
1204
		case OPT_SERVERCERT:
1205 1206
			server_cert = keep_config_arg();
			openconnect_set_system_trust(vpninfo, 0);
1207
			break;
1208 1209 1210 1211 1212 1213
		case OPT_RESOLVE:
			ip = strchr(config_arg, ':');
			if (!ip) {
				fprintf(stderr, _("Missing colon in resolve option\n"));
				exit(1);
			}
1214
			gai = malloc(sizeof(*gai) + strlen(config_arg) + 1);
1215 1216 1217 1218 1219 1220 1221
			if (!gai) {
				fprintf(stderr, _("Failed to allocate memory\n"));
				exit(1);
			}
			gai->next = gai_overrides;
			gai_overrides = gai;
			gai->option = (void *)(gai + 1);
1222
			memcpy(gai->option, config_arg, strlen(config_arg) + 1);
1223 1224 1225
			gai->option[ip - config_arg] = 0;
			gai->value = gai->option + (ip - config_arg) + 1;
			break;
1226
		case OPT_NO_DTLS:
1227
			vpninfo->dtls_state = DTLS_DISABLED;
1228
			break;
1229
		case OPT_COOKIEONLY:
1230 1231
			cookieonly = 1;
			break;
1232
		case OPT_PRINTCOOKIE:
1233 1234
			cookieonly = 2;
			break;
1235 1236 1237
		case OPT_AUTHENTICATE:
			cookieonly = 3;
			break;
1238
		case OPT_COOKIE_ON_STDIN:
1239
			read_stdin(&vpninfo->cookie, 0, 0);
1240
			/* If the cookie is empty, ignore it */
1241
			if (!*vpninfo->cookie)
1242
				vpninfo->cookie = NULL;
1243
			break;
1244
		case OPT_PASSWORD_ON_STDIN:
1245 1246
			read_stdin(&password, 0, 0);
			allow_stdin_read = 1;
David Woodhouse's avatar
David Woodhouse committed
1247
			break;
1248
		case OPT_NO_PASSWD:
1249 1250
			vpninfo->nopasswd = 1;
			break;
1251
		case OPT_NO_XMLPOST:
1252
			openconnect_set_xmlpost(vpninfo, 0);
1253
			break;
David Woodhouse's avatar
David Woodhouse committed
1254 1255 1256
		case OPT_NON_INTER:
			non_inter = 1;
			break;
1257
		case OPT_RECONNECT_TIMEOUT:
1258
			reconnect_timeout = atoi(config_arg);
1259
			break;
1260
		case OPT_DTLS_CIPHERS:
1261
			vpninfo->dtls_ciphers = keep_config_arg();
1262
			break;
1263
		case OPT_AUTHGROUP:
1264
			authgroup = keep_config_arg();
1265
			break;
1266
		case 'C':
1267
			vpninfo->cookie = dup_config_arg();
1268
			break;
1269
		case 'c':
1270
			vpninfo->cert = dup_config_arg();
1271
			break;
1272
		case 'e':
1273
			vpninfo->cert_expire_warning = 86400 * atoi(config_arg);
1274
			break;
1275
		case 'k':
1276
			vpninfo->sslkey = dup_config_arg();
1277
			break;
1278
		case 'd':
1279
			vpninfo->req_compr = COMPR_ALL;
1280 1281
			break;
		case 'D':
1282
			vpninfo->req_compr = 0;
1283
			break;
David Woodhouse's avatar
David Woodhouse committed
1284
		case 'g':
1285
			free(urlpath);
1286
			urlpath = dup_config_arg();
David Woodhouse's avatar
David Woodhouse committed
1287
			break;
1288
		case 'h':
1289 1290
			usage();
		case 'i':
1291
			vpninfo->ifname = dup_config_arg();
1292
			break;
1293 1294 1295 1296 1297
		case 'm': {
			int mtu = atol(config_arg);
			if (mtu < 576) {
				fprintf(stderr, _("MTU %d too small\n"), mtu);
				mtu = 576;
1298
			}
1299
			openconnect_set_reqmtu(vpninfo, mtu);
1300
			break;
1301
		}
1302 1303 1304 1305 1306 1307 1308
		case OPT_BASEMTU:
			vpninfo->basemtu = atol(config_arg);
			if (vpninfo->basemtu < 576) {
				fprintf(stderr, _("MTU %d too small\n"), vpninfo->basemtu);
				vpninfo->basemtu = 576;
			}
			break;
1309
		case 'p':
1310
			vpninfo->cert_password = strdup(config_arg);
1311
			break;
1312
		case 'P':
1313
			proxy = keep_config_arg();
David Woodhouse's avatar
David Woodhouse committed
1314
			autoproxy = 0;
1315
			break;
1316
		case OPT_PROXY_AUTH:
1317
			openconnect_set_proxy_auth(vpninfo, config_arg);
1318
			break;
1319 1320 1321
		case OPT_HTTP_AUTH:
			openconnect_set_http_auth(vpninfo, config_arg);
			break;
1322
		case OPT_NO_PROXY:
1323
			autoproxy =