udyfi

Small client for dy.fi (and possibly other services) DNS renewals
git clone https://git.inz.fi/udyfi/
Log | Files | Refs

udyfi.c (20505B)


      1 /*
      2  * MIT License
      3  *
      4  * Copyright (c) 2016-2023 Santtu Lakkala
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     22  * DEALINGS IN THE SOFTWARE.
     23  */
     24 #include <sys/cdefs.h>
     25 #undef __BSD_VISIBLE
     26 #define __BSD_VISIBLE 1
     27 #include <sys/socket.h>
     28 #include <sys/select.h>
     29 #include <signal.h>
     30 #include <setjmp.h>
     31 #include <poll.h>
     32 #include <fcntl.h>
     33 #include <unistd.h>
     34 #include <netdb.h>
     35 #include <string.h>
     36 #include <stdio.h>
     37 #include <errno.h>
     38 #include <pwd.h>
     39 #include <stdint.h>
     40 #include <stdlib.h>
     41 #include <stdbool.h>
     42 #include <limits.h>
     43 #ifdef USE_IFADDRS
     44 #define __USE_MISC
     45 #include <net/if.h>
     46 #include <ifaddrs.h>
     47 #endif
     48 #include <time.h>
     49 #if defined(USE_LIBTLS)
     50 #include <tls.h>
     51 #elif defined(USE_OPENSSL)
     52 #undef USE_OPENSSL
     53 #define USE_LIBTLS
     54 #include "minitls.h"
     55 #endif
     56 #include "arg.h"
     57 
     58 static jmp_buf terminate;
     59 char *argv0;
     60 
     61 #ifdef USE_LIBTLS
     62 #define IF_SSL(...) __VA_ARGS__
     63 #else
     64 #define IF_SSL(...)
     65 #endif
     66 
     67 #define MINUTES(x) ((x) * 60)
     68 #define HOURS(x) (MINUTES(x) * 60)
     69 #define DAYS(x) (HOURS(x) * 24)
     70 #include "config.h"
     71 
     72 static char **strsplit(char *s, int *c)
     73 {
     74 	size_t n = 1;
     75 	char *r = s;
     76 	char *w = s;
     77 	char **rv = malloc((n + 1) * sizeof(*rv));
     78 	enum { NONE, ERR, SQUOT = '\'', DQUOT = '"' } state = NONE;
     79 
     80 	rv[0] = s;
     81 
     82 	while (*r) {
     83 		switch (*r) {
     84 		case ' ':
     85 			if (state == NONE) {
     86 				*w++ = '\0';
     87 				r++;
     88 				while (*r == ' ')
     89 					r++;
     90 				if (!*r)
     91 					continue;
     92 				rv = realloc(rv, (++n + 1) * sizeof(*rv));
     93 				rv[n - 1] = w;
     94 				continue;
     95 			}
     96 			break;
     97 
     98 		case '\'':
     99 		case '"':
    100 			if (state == NONE)
    101 				state = *r;
    102 			else if ((char)state == *r)
    103 				state = NONE;
    104 			else
    105 				break;
    106 
    107 			r++;
    108 			continue;
    109 
    110 		case '\\':
    111 			if (state != SQUOT) {
    112 				if (!*++r) {
    113 					state = ERR;
    114 					continue;
    115 				}
    116 			}
    117 			break;
    118 		}
    119 		*w++ = *r++;
    120 	}
    121 
    122 	if (state != NONE) {
    123 		free(rv);
    124 		return NULL;
    125 	}
    126 	*w = '\0';
    127 	rv[n] = NULL;
    128 	*c = n;
    129 	return rv;
    130 }
    131 
    132 static void sig_handler(int signum)
    133 {
    134 	(void)signum;
    135 	longjmp(terminate, 0);
    136 }
    137 
    138 static void sighup_handler(int signum)
    139 {
    140 	(void)signum;
    141 }
    142 
    143 static ssize_t http_get(const char *server,
    144 			const char *port,
    145 			const char *path,
    146 			const char * restrict headers,
    147 			const char *iface,
    148 			int *af,
    149 			char *buffer,
    150 			size_t bufsize
    151 			IF_SSL(, struct tls *ctx)
    152 			)
    153 {
    154 	struct addrinfo *res;
    155 	struct addrinfo *i;
    156 	struct addrinfo *k;
    157 	struct addrinfo hints = { .ai_family = *af, .ai_socktype = SOCK_STREAM };
    158 	struct addrinfo *local = NULL;
    159 #ifdef USE_IFADDRS
    160 	struct ifaddrs *addrs = NULL;
    161 	struct ifaddrs *j;
    162 #endif
    163 	ssize_t r;
    164 	ssize_t s;
    165 	int sock;
    166 
    167 	if (iface) {
    168 #if USE_IFADDRS
    169 		bool if_found = false;
    170 		if ((r = getifaddrs(&addrs)))
    171 			return -1;
    172 		for (j = addrs; j; j = j->ifa_next) {
    173 			if (!strcmp(j->ifa_name, iface)) {
    174 				if_found = true;
    175 				if (((j->ifa_flags & (IFF_UP |
    176 						     IFF_BROADCAST |
    177 						     IFF_LOOPBACK)) ==
    178 				     (IFF_UP | IFF_BROADCAST)))
    179 					break;
    180 			}
    181 		}
    182 		if (if_found && !j) {
    183 			freeifaddrs(addrs);
    184 			return -1;
    185 		}
    186 
    187 		if (!if_found) {
    188 			freeifaddrs(addrs);
    189 			addrs = NULL;
    190 
    191 #endif
    192 			r = getaddrinfo(iface, NULL, &hints, &local);
    193 			if (r < 0)
    194 				return -1;
    195 #if USE_IFADDRS
    196 		}
    197 #endif
    198 	}
    199 
    200 	if ((r = getaddrinfo(server, port, &hints, &res))) {
    201 #if USE_IFADDRS
    202 		if (addrs)
    203 			freeifaddrs(addrs);
    204 #endif
    205 		if (local)
    206 			freeaddrinfo(local);
    207 		return -1;
    208 	}
    209 
    210 	for (i = res; i; i = i->ai_next) {
    211 		sock = socket(i->ai_family, i->ai_socktype, i->ai_protocol);
    212 
    213 #if USE_IFADDRS
    214 		if (addrs) {
    215 			for (j = addrs; j; j = j->ifa_next) {
    216 				if (!strcmp(j->ifa_name, iface) &&
    217 				    (((j->ifa_flags & (IFF_UP |
    218 						       IFF_BROADCAST |
    219 						       IFF_LOOPBACK)) ==
    220 				      (IFF_UP | IFF_BROADCAST))) &&
    221 				     j->ifa_addr->sa_family == i->ai_family)
    222 					break;
    223 			}
    224 			if (j) {
    225 				if ((r = bind(sock,
    226 					      j->ifa_addr, sizeof(struct sockaddr_storage)))) {
    227 					close(sock);
    228 					continue;
    229 				}
    230 			}
    231 		} else
    232 #endif
    233 		if (local) {
    234 			for (k = local; k; k = k->ai_next) {
    235 				if (k->ai_family == i->ai_family &&
    236 				    !bind(sock, k->ai_addr, k->ai_addrlen))
    237 					break;
    238 			}
    239 			if (!k) {
    240 				close(sock);
    241 				continue;
    242 			}
    243 		}
    244 		
    245 		*af = i->ai_family;
    246 		if (!connect(sock, i->ai_addr, i->ai_addrlen))
    247 			break;
    248 		close(sock);
    249 	}
    250 
    251 	freeaddrinfo(res);
    252 #if USE_IFADDRS
    253 	if (addrs)
    254 		freeifaddrs(addrs);
    255 #endif
    256 	if (local)
    257 		freeaddrinfo(local);
    258 
    259 	if (!i)
    260 		return -1;
    261 
    262 	if ((r = snprintf(buffer, bufsize,
    263 			  "GET %s HTTP/1.0\r\nHost: %s\r\n%s\n",
    264 			  path, server, headers ? headers : "")) >= (int)bufsize) {
    265 		close(sock);
    266 		return -1;
    267 	}
    268 
    269 #ifdef USE_LIBTLS
    270 	if (ctx) {
    271 		if (tls_connect_socket(ctx, sock, server)) {
    272 			close(sock);
    273 			return -1;
    274 		}
    275 
    276 		if ((s = tls_write(ctx, buffer, r)) < r) {
    277 			tls_close(ctx);
    278 			tls_reset(ctx);
    279 			tls_configure(ctx, NULL);
    280 			close(sock);
    281 			return -1;
    282 		}
    283 	} else
    284 #endif
    285 	if ((s = write(sock, buffer, r)) < r) {
    286 		close(sock);
    287 		return -1;
    288 	}
    289 
    290 	s = 0;
    291 	do {
    292 #ifdef USE_LIBTLS
    293 		if (ctx)
    294 			r = tls_read(ctx, buffer + s, bufsize - s - 1);
    295 		else
    296 #endif
    297 		r = read(sock, buffer + s, bufsize - s - 1);
    298 		if (r < 0) {
    299 #ifdef USE_LIBTLS
    300 			if (ctx) {
    301 				if (r == TLS_WANT_POLLIN) {
    302 					poll(&(struct pollfd){ .fd = sock, .events = POLLIN }, 1, 0);
    303 					continue;
    304 				}
    305 
    306 				tls_close(ctx);
    307 				tls_reset(ctx);
    308 				tls_configure(ctx, NULL);
    309 			}
    310 #endif
    311 			close(sock);
    312 			return -1;
    313 		}
    314 		s += r;
    315 	} while (r && s < (int)bufsize);
    316 #ifdef USE_LIBTLS
    317 	if (ctx) {
    318 		tls_close(ctx);
    319 		tls_reset(ctx);
    320 		tls_configure(ctx, NULL);
    321 	}
    322 #endif
    323 	close(sock);
    324 	buffer[s] = '\0';
    325 
    326 	return s;
    327 }
    328 
    329 static char *add_header(char *buffer, char *buf_end, const char *name, const char *value) {
    330 	static const char sep[2] = ": ";
    331 	static const char end[2] = "\r\n";
    332 	size_t nl;
    333 	size_t vl;
    334 
    335 	if (!buffer)
    336 		return NULL;
    337 
    338 	nl = strlen(name);
    339 	vl = strlen(value);
    340 
    341 	if (buffer + nl + vl + sizeof(sep) + sizeof(end) >= buf_end)
    342 		return NULL;
    343 	buffer = (char *)memcpy(buffer, name, nl) + nl;
    344 	buffer = (char *)memcpy(buffer, sep, sizeof(sep)) + sizeof(sep);
    345 	buffer = (char *)memcpy(buffer, value, vl) + vl;
    346 	buffer = (char *)memcpy(buffer, end, sizeof(end)) + sizeof(end);
    347 	*buffer = '\0';
    348 
    349 	return buffer;
    350 }
    351 
    352 static int check_ip(const char *server,
    353 		    const char *port,
    354 		    const char *iface,
    355 		    int af,
    356 		    const char *ua,
    357 		    char *ip_buffer,
    358 		    size_t ip_size
    359 		    IF_SSL(, struct tls *ctx)
    360 		   )
    361 {
    362 	char header_buffer[1024];
    363 	char buffer[1024];
    364 	const char *ip;
    365 	size_t iplen;
    366 
    367 	if (!add_header(header_buffer, 1[&header_buffer], "User-Agent", ua))
    368 		return -1;
    369 
    370 	if (http_get(server, port, "/",
    371 		     header_buffer,
    372 		     iface,
    373 		     &af,
    374 		     buffer,
    375 		     sizeof(buffer)
    376 		     IF_SSL(, ctx)
    377 		     ) < 0)
    378 		return -1;
    379 
    380 	if ((ip = strstr(buffer, "Current IP Address: ")))
    381 		ip += sizeof("Current IP Address: ") - 1;
    382 	else if ((ip = strstr(buffer, "\r\n\r\n")))
    383 		ip += sizeof("\r\n\r\n") - 1;
    384 	else
    385 		return -1;
    386 
    387 	switch (af) {
    388 	case AF_INET:
    389 	default:
    390 		iplen = strspn(ip, "0123456789.");
    391 		break;
    392 	case AF_INET6:
    393 		iplen = strspn(ip, "0123456789a-fA-F:");
    394 		break;
    395 	}
    396 	if (ip[-1] == '\n' && ip[iplen])
    397 		return -1;
    398 	if (snprintf(ip_buffer, ip_size, "%.*s", (int)iplen, ip) >= (int)ip_size)
    399 		return -1;
    400 
    401 	return 0;
    402 }
    403 
    404 static int auth_token(const char *user,
    405 			const char *password,
    406 			char *buffer,
    407 			size_t b_len)
    408 {
    409 	static const char *base64 =
    410 		"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    411 		"abcdefghijklmnopqrstuvwxyz"
    412 		"0123456789+/";
    413 	size_t ulen = strlen(user);
    414 	size_t plen = strlen(password);
    415 	size_t i;
    416 	size_t bits = 0;
    417 	uint32_t d = 0;
    418 	char *wp = buffer;
    419 
    420 	if ((ulen + plen + 3) / 3 * 4 >= b_len)
    421 		return -1;
    422 
    423 	for (i = 0; i < ulen; i++) {
    424 		d = (d << 8) | user[i];
    425 		bits += 8;
    426 		while (bits >= 6)
    427 			*wp++ = base64[(d >> (bits -= 6)) & 0x3f];
    428 	}
    429 	d = (d << 8) | ':';
    430 	bits += 8;
    431 	for (i = 0; i < plen; i++) {
    432 		d = (d << 8) | password[i];
    433 		bits += 8;
    434 		while (bits >= 6)
    435 			*wp++ = base64[(d >> (bits -= 6)) & 0x3f];
    436 	}
    437 
    438 	if (bits)
    439 		*wp++ = base64[(d << (6 - bits)) & 0x3f];
    440 	while ((wp - buffer) & 3)
    441 		*wp++ = '=';
    442 	*wp = '\0';
    443 
    444 	return wp - buffer;
    445 }
    446 
    447 static const char *find_body(const char *response,
    448 			     size_t len, size_t *blen)
    449 {
    450 	size_t i;
    451 
    452 	for (i = 0; i < len - 1; i++) {
    453 		if (response[i] == '\n') {
    454 			if (response[i + 1] == '\n') {
    455 				*blen = len - i - 2;
    456 				return response + i + 2;
    457 			} else if (i < len - 2 && response[i + 1] == '\r' &&
    458 				 response[i + 2] == '\n') {
    459 				*blen = len - i - 3;
    460 				return response + i + 3;
    461 			}
    462 		}
    463 	}
    464 
    465 	return NULL;
    466 }
    467 
    468 enum update_status {
    469 	UP_BADAUTH,
    470 	UP_NOHOST,
    471 	UP_NOTFQDN,
    472 	UP_BADIP,
    473 	UP_NOCHG,
    474 	UP_GOOD,
    475 	UP_DNSERR,
    476 	UP_ABUSE,
    477 	UP_BADAGENT,
    478 	UP_911,
    479 	UP_UNKNOWN,
    480 	UP_REDIRECT,
    481 	UP_INTERNAL,
    482 	UP_NETWORK
    483 };
    484 
    485 static const char *statusstr[] = {
    486 	"badauth",
    487 	"nohost",
    488 	"notfqdn",
    489 	"badip ",
    490 	"nochg",
    491 	"good ",
    492 	"dnserr",
    493 	"abuse",
    494 	"badagent",
    495 	"911"
    496 };
    497 
    498 static unsigned short http_code(const char *buffer, const char *bend)
    499 {
    500 	const char match[] = "HTTP/1.";
    501 	unsigned int rv = 0;
    502 
    503 	if (buffer + sizeof(match) >= bend || memcmp(buffer, match, sizeof(match) - 1))
    504 		return 0;
    505 	buffer += sizeof(match);
    506 	if (buffer[-1] != '0' && buffer[-1] != '1')
    507 		return 0;
    508 	if (buffer >= bend || *buffer++ != ' ')
    509 		return 0;
    510 	for (; buffer < bend; buffer++) {
    511 		if (*buffer == ' ')
    512 			break;
    513 		if (*buffer >= '0' && *buffer <= '9')
    514 			rv = rv * 10 + (*buffer - '0');
    515 		else
    516 			return 0;
    517 
    518 		if (rv > USHRT_MAX)
    519 			return 0;
    520 	}
    521 
    522 	return rv;
    523 }
    524 
    525 static int update_ip(const char *server,
    526 		     const char *port,
    527 		     const char *hosts,
    528 		     const char *auth,
    529 		     const char *iface,
    530 		     int af,
    531 		     const char *ua
    532 		     IF_SSL(, struct tls *ctx)
    533 		     )
    534 {
    535 	char buffer[1024];
    536 	char path_buffer[1024];
    537 	char header_buffer[1024];
    538 	int r;
    539 	size_t bl;
    540 	const char *body;
    541 	unsigned short code;
    542 	char *bend = 1[&header_buffer];
    543 
    544 	char *bpos = add_header(header_buffer, bend, "User-Agent", ua);
    545 	bpos = add_header(bpos, bend, "Authorization", auth);
    546 
    547 	if (!bpos)
    548 		return UP_INTERNAL;
    549 
    550 	r = snprintf(path_buffer, sizeof(path_buffer),
    551 		       "/nic/update?hostname=%s", hosts);
    552 	if (r >= (int)sizeof(path_buffer))
    553 		return UP_INTERNAL;
    554 
    555 	if ((r = http_get(server, port, path_buffer,
    556 			  header_buffer, iface, &af,
    557 			  buffer, sizeof(buffer)
    558 		          IF_SSL(, ctx)
    559 		     )) < 0)
    560 		return UP_NETWORK;
    561 
    562 	code = http_code(buffer, buffer + strlen(buffer));
    563 	if (!code || code >= 500)
    564 		return UP_911;
    565 	if (code == 401)
    566 		return UP_BADAUTH;
    567 	if (code == 403)
    568 		return UP_NOHOST;
    569 	if (code > 400)
    570 		return UP_UNKNOWN;
    571 	if (code >= 300)
    572 		return UP_REDIRECT;
    573 
    574 	if ((body = find_body(buffer, r,
    575 			     &bl))) {
    576 		for (r = 0; r < UP_UNKNOWN; r++) {
    577 			if (!strncmp(body, statusstr[r],
    578 				     strlen(statusstr[r])))
    579 				break;
    580 		}
    581 	} else {
    582 		r = UP_UNKNOWN;
    583 	}
    584 
    585 	return r;
    586 }
    587 
    588 static void usage(void) {
    589 	fprintf(stderr,
    590 		"Usage: %s "
    591 		"-u <username> "
    592 		"-p <password> "
    593 		"-f <passwordfile> "
    594 		"[-i <"
    595 #ifdef USE_IFADDRS
    596 		"interface/"
    597 #endif
    598 		"local address>] "
    599 		"[-U <username>] "
    600 		"[-c <checkip server>] "
    601 		"[-C <checkip port>] "
    602 		"[-d <dyndns server>] "
    603 		"[-D <dyndns port>] "
    604 		"[-F <config file>] "
    605 		"[-b] "
    606 		"[-P <pidfile>] "
    607 		IF_SSL(
    608 		"[-s] "
    609 		"[-S] "
    610 		"[-z] "
    611 		"[-Z] "
    612 		)
    613 		"[-6] "
    614 		"[-4] "
    615 		"host[,host...]\n",
    616 		argv0);
    617 }
    618 
    619 static void write_pidfile(const char *pidfile, pid_t pid)
    620 {
    621 	FILE *pdf = fopen(pidfile, "w");
    622 	if (pdf) {
    623 		fprintf(pdf, "%ld\n", (long)pid);
    624 		fclose(pdf);
    625 	}
    626 }
    627 
    628 static void daemonize(const char *pidfile) {
    629 	pid_t pid;
    630 	if ((pid = fork())) {
    631 		if (pid < 0)
    632 			exit(EXIT_FAILURE);
    633 		if (pidfile) {
    634 			write_pidfile(pidfile, pid);
    635 		}
    636 		exit(EXIT_SUCCESS);
    637 	}
    638 	setsid();
    639 	fclose(stdin);
    640 	fclose(stdout);
    641 }
    642 
    643 int main(int argc, char **argv)
    644 {
    645 	const char *username = NULL;
    646 	const char * volatile pidfile = NULL;
    647 	const char *iface = NULL;
    648 	const char *dropuser = NULL;
    649 	const char *domains = NULL;
    650 	const char *ua = default_ua;
    651 	char password[256] = "";
    652 	char authbuf[1024] = "Basic ";
    653 	int fd;
    654 	time_t next_refresh = 0;
    655 	time_t last_error = 0;
    656 	char ipbuf1[64];
    657 	char ipbuf2[64] = "";
    658 	char *ip = ipbuf1;
    659 	char *prev_ip = ipbuf2;
    660 	int background = 0;
    661 	int af = AF_UNSPEC;
    662 	volatile int ret = EXIT_SUCCESS;
    663 	FILE * volatile log = stderr;
    664 	const char *logfile = NULL;
    665 	char *arg;
    666 	sigset_t sigset_hup;
    667 	sigset_t sigset_dfl;
    668 
    669 	struct {
    670 		char **argv;
    671 		int argc;
    672 		char *arg;
    673 	} cfgfile = { 0 };
    674 
    675 #ifdef USE_LIBTLS
    676 	int port_set = 0;
    677 	int check_port_set = 0;
    678 	bool use_ssl = dyndns_ssl;
    679 	bool check_ssl = checkip_ssl;
    680 	struct tls * volatile ctx = NULL;
    681 #endif
    682 
    683 	sigemptyset(&sigset_hup);
    684 	sigaddset(&sigset_hup, SIGHUP);
    685 
    686 	for (argv0 = *argv, argv++, argc--;
    687 	     argc && argv[0][0] == '-' && argv[0][1];
    688 	     argc--, argv++) {
    689 		arg = &argv[0][1];
    690 
    691 cfgtoggle:
    692 		if (argv[0][1] == '-' && argv[0][1] == '\0') {
    693 			argv++;
    694 			argc--;
    695 			break;
    696 		}
    697 
    698 		while (*arg) {
    699 			switch (*arg++) {
    700 			case 'a':
    701 				ua = EARGF(usage());
    702 				break;
    703 			case 'i':
    704 				iface = EARGF(usage());
    705 				break;
    706 			case 'u':
    707 				username = EARGF(usage());
    708 				break;
    709 			case 'U':
    710 				dropuser = EARGF(usage());
    711 				break;
    712 			case 'p': {
    713 				char *pass = EARGF(usage());
    714 				snprintf(password, sizeof(password),
    715 					 "%s", pass);
    716 				memset(pass, 0, strlen(pass));
    717 				break;
    718 			}
    719 			case 'F': {
    720 				FILE *f;
    721 				char *data = NULL;
    722 				size_t n = 0;
    723 				ssize_t r;
    724 
    725 				if (cfgfile.argv) {
    726 					fprintf(stderr, "Nested configuration files not supported\n");
    727 					return EXIT_FAILURE;
    728 				}
    729 				if (!(f = fopen(EARGF(usage()), "r"))) {
    730 					fprintf(stderr, "Failed to open configuration file\n");
    731 					return EXIT_FAILURE;
    732 				}
    733 
    734 				r = getline(&data, &n, f);
    735 				fclose(f);
    736 
    737 				if (r < 0) {
    738 					fprintf(stderr, "Failed to read configuration file\n");
    739 					return EXIT_FAILURE;
    740 				}
    741 
    742 				while (r && (data[r - 1] == '\r' || data[r - 1] == '\n'))
    743 					r--;
    744 				data[r] = '\0';
    745 
    746 				if (!r) {
    747 					free(data);
    748 					break;
    749 				}
    750 
    751 				cfgfile.argv = argv;
    752 				cfgfile.argc = argc;
    753 				cfgfile.arg = arg;
    754 
    755 				argv = strsplit(data, &argc);
    756 				if (!argv) {
    757 					fprintf(stderr, "Failed to parse configuration file\n");
    758 					free(data);
    759 					return EXIT_FAILURE;
    760 				}
    761 				arg = argv[0];
    762 				if (*arg != '-')
    763 					continue;
    764 				arg++;
    765 				goto cfgtoggle;
    766 			}
    767 			case 'f':
    768 				  if ((fd = open(EARGF(usage()), O_RDONLY)) >= 0) {
    769 					  int r = read(fd, password, sizeof(password) - 1);
    770 					  close(fd);
    771 
    772 					  if (r < 0) {
    773 						  fprintf(stderr, "Failed to read password from file\n");
    774 						  return EXIT_FAILURE;
    775 					  }
    776 
    777 					  while (r && (password[r - 1] == '\n' || password[r - 1] == '\r'))
    778 						  r--;
    779 					  password[r] = '\0';
    780 				  } else {
    781 					  fprintf(stderr, "Failed to open password file\n");
    782 					  return EXIT_FAILURE;
    783 				  }
    784 				  break;
    785 			case 'c':
    786 				  checkip_service = EARGF(usage());
    787 				  break;
    788 			case '4':
    789 				  af = AF_INET;
    790 				  break;
    791 			case '6':
    792 				  af = AF_INET6;
    793 				  break;
    794 			case 'C':
    795 				  checkip_port = EARGF(usage());
    796 				  IF_SSL(check_port_set = 1;)
    797 					  break;
    798 			case 'd':
    799 				  dyndns_service = EARGF(usage());
    800 				  break;
    801 			case 'D':
    802 				  dyndns_port = EARGF(usage());
    803 				  IF_SSL(port_set = 1;)
    804 					  break;
    805 			case 'b':
    806 				  background = 1;
    807 				  break;
    808 			case 'P':
    809 				  pidfile = EARGF(usage());
    810 				  break;
    811 			case 'l':
    812 				  logfile = EARGF(usage());
    813 				  break;
    814 #ifdef USE_LIBTLS
    815 			case 's':
    816 				  use_ssl = true;
    817 				  break;
    818 			case 'S':
    819 				  check_ssl = true;
    820 				  break;
    821 			case 'z':
    822 				  use_ssl = false;
    823 				  break;
    824 			case 'Z':
    825 				  check_ssl = false;
    826 				  break;
    827 #endif
    828 			default:
    829 				  usage();
    830 				  return EXIT_FAILURE;
    831 			}
    832 		}
    833 	}
    834 
    835 	if (argc)
    836 		domains = argv[0];
    837 	if (cfgfile.argv) {
    838 		argv = cfgfile.argv;
    839 		argc = cfgfile.argc;
    840 		arg = cfgfile.arg;
    841 
    842 		cfgfile.argv = NULL;
    843 
    844 		goto cfgtoggle;
    845 	}
    846 
    847 #if defined(USE_LIBTLS)
    848 	if (use_ssl || check_ssl) {
    849 		ctx = tls_client();
    850 
    851 		if (!ctx) {
    852 			fprintf(stderr, "SSL/TLS initialization failed.\n");
    853 			return EXIT_FAILURE;
    854 		}
    855 
    856 		if (use_ssl && !port_set)
    857 			dyndns_port = "443";
    858 		if (check_ssl && !check_port_set)
    859 			checkip_port = "443";
    860 	}
    861 #endif
    862 
    863 	if (!domains || !username) {
    864 		usage();
    865 		return EXIT_FAILURE;
    866 	}
    867 
    868 	auth_token(username, password, authbuf + strlen(authbuf), sizeof(authbuf) - strlen(authbuf));
    869 	memset(password, 0, sizeof(password));
    870 
    871 	srand(time(NULL));
    872 
    873 	if (logfile)
    874 		log = fopen(logfile, "a");
    875 
    876 	if (background)
    877 		daemonize(pidfile);
    878 	else if (pidfile)
    879 		write_pidfile(pidfile, getpid());
    880 
    881 	if (dropuser) {
    882 		struct passwd *ent = getpwnam(dropuser);
    883 		if (!ent) {
    884 			fprintf(log, "Failed to get user\n");
    885 			ret = EXIT_FAILURE;
    886 			goto out;
    887 		}
    888 
    889 		if (setgid(ent->pw_gid) ||
    890 		    setuid(ent->pw_uid)) {
    891 			fprintf(log, "Failed to change user\n");
    892 			ret = EXIT_FAILURE;
    893 			goto out;
    894 		}
    895 	}
    896 
    897 	fprintf(log, "udyfi started\n");
    898 
    899 	if (setjmp(terminate))
    900 		goto out;
    901 
    902 	signal(SIGINT, sig_handler);
    903 	signal(SIGTERM, sig_handler);
    904 	sigaction(SIGHUP, &(struct sigaction){ .sa_handler = sighup_handler }, NULL);
    905 
    906 	sigprocmask(SIG_BLOCK, &sigset_hup, &sigset_dfl);
    907 
    908 	for (;;) {
    909 		time_t now = time(NULL);
    910 
    911 		if (now >= last_error + error_fallback &&
    912 		    check_ip(checkip_service, checkip_port, iface, af, ua,
    913 			     ip, sizeof(ipbuf1)
    914 			     IF_SSL(, check_ssl ? ctx : NULL)
    915 			     ) >= 0) {
    916 			char *tmp;
    917 
    918 			if ((strcmp(ip, prev_ip) || now > next_refresh)) {
    919 				switch (update_ip(dyndns_service, dyndns_port,
    920 						  domains,
    921 						  authbuf, iface, af, ua
    922 						  IF_SSL(, use_ssl ? ctx : NULL)
    923 						 )) {
    924 				case UP_BADAUTH:
    925 					fprintf(log, "FATAL: "
    926 						"Authentication error\n");
    927 					ret = EXIT_FAILURE;
    928 					goto out;
    929 				case UP_NOHOST:
    930 					fprintf(log, "FATAL: "
    931 						"No hostnames specified\n");
    932 					ret = EXIT_FAILURE;
    933 					goto out;
    934 				case UP_NOTFQDN:
    935 					fprintf(log, "FATAL: "
    936 						"Malformed hostnames\n");
    937 					ret = EXIT_FAILURE;
    938 					goto out;
    939 				case UP_BADIP:
    940 					fprintf(log, "WARNING: "
    941 						"Server rejected our IP\n");
    942 					break;
    943 				case UP_NOCHG:
    944 					fprintf(log, "IP %s refreshed\n",
    945 						ip);
    946 					break;
    947 				case UP_GOOD:
    948 					fprintf(log, "%s now point to %s\n",
    949 						domains, ip);
    950 					break;
    951 				case UP_911:
    952 				case UP_DNSERR:
    953 					fprintf(log, "WARNING: "
    954 						"Temporary service error\n");
    955 					last_error = now;
    956 					goto retry;
    957 				case UP_ABUSE:
    958 					fprintf(log, "FATAL: "
    959 						"We are flagged as abuse\n");
    960 					ret = EXIT_FAILURE;
    961 					goto out;
    962 				case UP_BADAGENT:
    963 					fprintf(log, "FATAL: "
    964 						"We are flagged as bad agent\n");
    965 					ret = EXIT_FAILURE;
    966 					goto out;
    967 				case UP_UNKNOWN:
    968 					fprintf(log, "WARNING: "
    969 						"Unknown status reply\n");
    970 					break;
    971 				case UP_REDIRECT:
    972 					fprintf(log, "FATAL: "
    973 						"Server send a redirection, configuration error?\n");
    974 					ret = EXIT_FAILURE;
    975 					goto out;
    976 				case UP_INTERNAL:
    977 					fprintf(log, "FATAL: "
    978 						"Internal error, likely run "
    979 						"out of buffer\n");
    980 					ret = EXIT_FAILURE;
    981 					goto out;
    982 				case UP_NETWORK:
    983 					fprintf(log, "WARNING: "
    984 						"Networking error\n");
    985 					goto retry;
    986 				}
    987 
    988 				next_refresh = now + minimum_refresh +
    989 					rand() * minimum_refresh_rand /
    990 					RAND_MAX;
    991 				fprintf(log, "Next forced update at %s",
    992 					ctime(&next_refresh));
    993 				fflush(log);
    994 
    995 				tmp = ip;
    996 				ip = prev_ip;
    997 				prev_ip = tmp;
    998 			}
    999 		} else {
   1000 			fprintf(log, "IP check failed.\n");
   1001 			fflush(log);
   1002 		}
   1003 
   1004 retry:
   1005 		pselect(0, NULL, NULL, NULL, &(struct timespec){
   1006 			.tv_sec = (last_error + error_fallback < now ?
   1007 				   check_interval + rand() * check_interval_rand / RAND_MAX :
   1008 				   last_error + error_fallback - now + rand() * error_fallback_rand / RAND_MAX)
   1009 			}, &sigset_dfl);
   1010 	}
   1011 
   1012 out:
   1013 #ifdef USE_LIBTLS
   1014 	if (ctx)
   1015 		tls_free(ctx);
   1016 #endif
   1017 
   1018 	if (pidfile)
   1019 		unlink(pidfile);
   1020 
   1021 	if (log != stderr)
   1022 		fclose(log);
   1023 
   1024 	return ret;
   1025 }