version 1.17.2.2, 2020/04/13 07:45:20 |
version 1.18, 2018/08/26 07:46:36 |
|
|
/* $NetBSD$ */ |
/* $NetBSD$ */ |
/* $OpenBSD: misc.c,v 1.146 2020/01/28 01:49:36 djm Exp $ */ |
/* $OpenBSD: misc.c,v 1.131 2018/07/27 05:13:02 dtucker Exp $ */ |
|
|
/* |
/* |
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
* Copyright (c) 2000 Markus Friedl. All rights reserved. |
* Copyright (c) 2005,2006 Damien Miller. All rights reserved. |
* Copyright (c) 2005,2006 Damien Miller. All rights reserved. |
Line 40 __RCSID("$NetBSD$"); |
|
Line 41 __RCSID("$NetBSD$"); |
|
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <netinet/ip.h> |
#include <netinet/ip.h> |
#include <netinet/tcp.h> |
#include <netinet/tcp.h> |
#include <arpa/inet.h> |
|
|
|
#include <ctype.h> |
#include <ctype.h> |
#include <errno.h> |
#include <errno.h> |
Line 50 __RCSID("$NetBSD$"); |
|
Line 50 __RCSID("$NetBSD$"); |
|
#include <pwd.h> |
#include <pwd.h> |
#include <libgen.h> |
#include <libgen.h> |
#include <limits.h> |
#include <limits.h> |
#include <poll.h> |
|
#include <signal.h> |
#include <signal.h> |
#include <stdarg.h> |
#include <stdarg.h> |
#include <stdio.h> |
#include <stdio.h> |
Line 88 set_nonblock(int fd) |
|
Line 87 set_nonblock(int fd) |
|
int val; |
int val; |
|
|
val = fcntl(fd, F_GETFL); |
val = fcntl(fd, F_GETFL); |
if (val == -1) { |
if (val < 0) { |
error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); |
error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); |
return (-1); |
return (-1); |
} |
} |
Line 112 unset_nonblock(int fd) |
|
Line 111 unset_nonblock(int fd) |
|
int val; |
int val; |
|
|
val = fcntl(fd, F_GETFL); |
val = fcntl(fd, F_GETFL); |
if (val == -1) { |
if (val < 0) { |
error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); |
error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); |
return (-1); |
return (-1); |
} |
} |
Line 222 set_rdomain(int fd, const char *name) |
|
Line 221 set_rdomain(int fd, const char *name) |
|
#endif |
#endif |
} |
} |
|
|
/* |
|
* Wait up to *timeoutp milliseconds for events on fd. Updates |
|
* *timeoutp with time remaining. |
|
* Returns 0 if fd ready or -1 on timeout or error (see errno). |
|
*/ |
|
static int |
|
waitfd(int fd, int *timeoutp, short events) |
|
{ |
|
struct pollfd pfd; |
|
struct timeval t_start; |
|
int oerrno, r; |
|
|
|
monotime_tv(&t_start); |
|
pfd.fd = fd; |
|
pfd.events = events; |
|
for (; *timeoutp >= 0;) { |
|
r = poll(&pfd, 1, *timeoutp); |
|
oerrno = errno; |
|
ms_subtract_diff(&t_start, timeoutp); |
|
errno = oerrno; |
|
if (r > 0) |
|
return 0; |
|
else if (r == -1 && errno != EAGAIN) |
|
return -1; |
|
else if (r == 0) |
|
break; |
|
} |
|
/* timeout */ |
|
errno = ETIMEDOUT; |
|
return -1; |
|
} |
|
|
|
/* |
|
* Wait up to *timeoutp milliseconds for fd to be readable. Updates |
|
* *timeoutp with time remaining. |
|
* Returns 0 if fd ready or -1 on timeout or error (see errno). |
|
*/ |
|
int |
|
waitrfd(int fd, int *timeoutp) { |
|
return waitfd(fd, timeoutp, POLLIN); |
|
} |
|
|
|
/* |
|
* Attempt a non-blocking connect(2) to the specified address, waiting up to |
|
* *timeoutp milliseconds for the connection to complete. If the timeout is |
|
* <=0, then wait indefinitely. |
|
* |
|
* Returns 0 on success or -1 on failure. |
|
*/ |
|
int |
|
timeout_connect(int sockfd, const struct sockaddr *serv_addr, |
|
socklen_t addrlen, int *timeoutp) |
|
{ |
|
int optval = 0; |
|
socklen_t optlen = sizeof(optval); |
|
|
|
/* No timeout: just do a blocking connect() */ |
|
if (timeoutp == NULL || *timeoutp <= 0) |
|
return connect(sockfd, serv_addr, addrlen); |
|
|
|
set_nonblock(sockfd); |
|
if (connect(sockfd, serv_addr, addrlen) == 0) { |
|
/* Succeeded already? */ |
|
unset_nonblock(sockfd); |
|
return 0; |
|
} else if (errno != EINPROGRESS) |
|
return -1; |
|
|
|
if (waitfd(sockfd, timeoutp, POLLIN | POLLOUT) == -1) |
|
return -1; |
|
|
|
/* Completed or failed */ |
|
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { |
|
debug("getsockopt: %s", strerror(errno)); |
|
return -1; |
|
} |
|
if (optval != 0) { |
|
errno = optval; |
|
return -1; |
|
} |
|
unset_nonblock(sockfd); |
|
return 0; |
|
} |
|
|
|
/* Characters considered whitespace in strsep calls. */ |
/* Characters considered whitespace in strsep calls. */ |
#define WHITESPACE " \t\r\n" |
#define WHITESPACE " \t\r\n" |
#define QUOTE "\"" |
#define QUOTE "\"" |
Line 397 pwcopy(struct passwd *pw) |
|
Line 312 pwcopy(struct passwd *pw) |
|
int |
int |
a2port(const char *s) |
a2port(const char *s) |
{ |
{ |
struct servent *se; |
|
long long port; |
long long port; |
const char *errstr; |
const char *errstr; |
|
|
port = strtonum(s, 0, 65535, &errstr); |
port = strtonum(s, 0, 65535, &errstr); |
if (errstr == NULL) |
if (errstr != NULL) |
return (int)port; |
return -1; |
if ((se = getservbyname(s, "tcp")) != NULL) |
return (int)port; |
return ntohs(se->s_port); |
|
return -1; |
|
} |
} |
|
|
int |
int |
Line 539 put_host_port(const char *host, u_short |
|
Line 451 put_host_port(const char *host, u_short |
|
|
|
if (port == 0 || port == SSH_DEFAULT_PORT) |
if (port == 0 || port == SSH_DEFAULT_PORT) |
return(xstrdup(host)); |
return(xstrdup(host)); |
if (asprintf(&hoststr, "[%s]:%d", host, (int)port) == -1) |
if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) |
fatal("put_host_port: asprintf: %s", strerror(errno)); |
fatal("put_host_port: asprintf: %s", strerror(errno)); |
debug3("put_host_port: %s", hoststr); |
debug3("put_host_port: %s", hoststr); |
return hoststr; |
return hoststr; |
Line 553 put_host_port(const char *host, u_short |
|
Line 465 put_host_port(const char *host, u_short |
|
* The delimiter char, if present, is stored in delim. |
* The delimiter char, if present, is stored in delim. |
* If this is the last field, *cp is set to NULL. |
* If this is the last field, *cp is set to NULL. |
*/ |
*/ |
char * |
static char * |
hpdelim2(char **cp, char *delim) |
hpdelim2(char **cp, char *delim) |
{ |
{ |
char *s, *old; |
char *s, *old; |
|
|
percent_expand(const char *string, ...) |
percent_expand(const char *string, ...) |
{ |
{ |
#define EXPAND_MAX_KEYS 16 |
#define EXPAND_MAX_KEYS 16 |
u_int num_keys, i; |
u_int num_keys, i, j; |
struct { |
struct { |
const char *key; |
const char *key; |
const char *repl; |
const char *repl; |
} keys[EXPAND_MAX_KEYS]; |
} keys[EXPAND_MAX_KEYS]; |
struct sshbuf *buf; |
char buf[4096]; |
va_list ap; |
va_list ap; |
int r; |
|
char *ret; |
|
|
|
if ((buf = sshbuf_new()) == NULL) |
|
fatal("%s: sshbuf_new failed", __func__); |
|
|
|
/* Gather keys */ |
/* Gather keys */ |
va_start(ap, string); |
va_start(ap, string); |
Line 1072 percent_expand(const char *string, ...) |
|
Line 979 percent_expand(const char *string, ...) |
|
va_end(ap); |
va_end(ap); |
|
|
/* Expand string */ |
/* Expand string */ |
|
*buf = '\0'; |
for (i = 0; *string != '\0'; string++) { |
for (i = 0; *string != '\0'; string++) { |
if (*string != '%') { |
if (*string != '%') { |
append: |
append: |
if ((r = sshbuf_put_u8(buf, *string)) != 0) { |
buf[i++] = *string; |
fatal("%s: sshbuf_put_u8: %s", |
if (i >= sizeof(buf)) |
__func__, ssh_err(r)); |
fatal("%s: string too long", __func__); |
} |
buf[i] = '\0'; |
continue; |
continue; |
} |
} |
string++; |
string++; |
Line 1087 percent_expand(const char *string, ...) |
|
Line 995 percent_expand(const char *string, ...) |
|
goto append; |
goto append; |
if (*string == '\0') |
if (*string == '\0') |
fatal("%s: invalid format", __func__); |
fatal("%s: invalid format", __func__); |
for (i = 0; i < num_keys; i++) { |
for (j = 0; j < num_keys; j++) { |
if (strchr(keys[i].key, *string) != NULL) { |
if (strchr(keys[j].key, *string) != NULL) { |
if ((r = sshbuf_put(buf, keys[i].repl, |
i = strlcat(buf, keys[j].repl, sizeof(buf)); |
strlen(keys[i].repl))) != 0) { |
if (i >= sizeof(buf)) |
fatal("%s: sshbuf_put: %s", |
fatal("%s: string too long", __func__); |
__func__, ssh_err(r)); |
|
} |
|
break; |
break; |
} |
} |
} |
} |
if (i >= num_keys) |
if (j >= num_keys) |
fatal("%s: unknown key %%%c", __func__, *string); |
fatal("%s: unknown key %%%c", __func__, *string); |
} |
} |
if ((ret = sshbuf_dup_string(buf)) == NULL) |
return (xstrdup(buf)); |
fatal("%s: sshbuf_dup_string failed", __func__); |
|
sshbuf_free(buf); |
|
return ret; |
|
#undef EXPAND_MAX_KEYS |
#undef EXPAND_MAX_KEYS |
} |
} |
|
|
Line 1137 tun_open(int tun, int mode, char **ifnam |
|
Line 1040 tun_open(int tun, int mode, char **ifnam |
|
return -1; |
return -1; |
} |
} |
|
|
if (fd == -1) { |
if (fd < 0) { |
debug("%s: %s open: %s", __func__, name, strerror(errno)); |
debug("%s: %s open: %s", __func__, name, strerror(errno)); |
return -1; |
return -1; |
} |
} |
Line 1234 tohex(const void *vp, size_t l) |
|
Line 1137 tohex(const void *vp, size_t l) |
|
return (r); |
return (r); |
} |
} |
|
|
/* |
|
* Extend string *sp by the specified format. If *sp is not NULL (or empty), |
|
* then the separator 'sep' will be prepended before the formatted arguments. |
|
* Extended strings are heap allocated. |
|
*/ |
|
void |
|
xextendf(char **sp, const char *sep, const char *fmt, ...) |
|
{ |
|
va_list ap; |
|
char *tmp1, *tmp2; |
|
|
|
va_start(ap, fmt); |
|
xvasprintf(&tmp1, fmt, ap); |
|
va_end(ap); |
|
|
|
if (*sp == NULL || **sp == '\0') { |
|
free(*sp); |
|
*sp = tmp1; |
|
return; |
|
} |
|
xasprintf(&tmp2, "%s%s%s", *sp, sep == NULL ? "" : sep, tmp1); |
|
free(tmp1); |
|
free(*sp); |
|
*sp = tmp2; |
|
} |
|
|
|
|
|
u_int64_t |
u_int64_t |
get_u64(const void *vp) |
get_u64(const void *vp) |
{ |
{ |
Line 1424 bandwidth_limit_init(struct bwlimit *bw, |
|
Line 1300 bandwidth_limit_init(struct bwlimit *bw, |
|
{ |
{ |
bw->buflen = buflen; |
bw->buflen = buflen; |
bw->rate = kbps; |
bw->rate = kbps; |
bw->thresh = buflen; |
bw->thresh = bw->rate; |
bw->lamt = 0; |
bw->lamt = 0; |
timerclear(&bw->bwstart); |
timerclear(&bw->bwstart); |
timerclear(&bw->bwend); |
timerclear(&bw->bwend); |
} |
} |
|
|
/* Callback from read/write loop to insert bandwidth-limiting delays */ |
/* Callback from read/write loop to insert bandwidth-limiting delays */ |
void |
void |
Line 1437 bandwidth_limit(struct bwlimit *bw, size |
|
Line 1313 bandwidth_limit(struct bwlimit *bw, size |
|
u_int64_t waitlen; |
u_int64_t waitlen; |
struct timespec ts, rm; |
struct timespec ts, rm; |
|
|
bw->lamt += read_len; |
|
if (!timerisset(&bw->bwstart)) { |
if (!timerisset(&bw->bwstart)) { |
monotime_tv(&bw->bwstart); |
monotime_tv(&bw->bwstart); |
return; |
return; |
} |
} |
|
|
|
bw->lamt += read_len; |
if (bw->lamt < bw->thresh) |
if (bw->lamt < bw->thresh) |
return; |
return; |
|
|
Line 1525 static const struct { |
|
Line 1402 static const struct { |
|
{ "cs6", IPTOS_DSCP_CS6 }, |
{ "cs6", IPTOS_DSCP_CS6 }, |
{ "cs7", IPTOS_DSCP_CS7 }, |
{ "cs7", IPTOS_DSCP_CS7 }, |
{ "ef", IPTOS_DSCP_EF }, |
{ "ef", IPTOS_DSCP_EF }, |
#ifdef IPTOS_DSCP_LE |
|
{ "le", IPTOS_DSCP_LE }, |
|
#endif |
|
{ "lowdelay", IPTOS_LOWDELAY }, |
{ "lowdelay", IPTOS_LOWDELAY }, |
{ "throughput", IPTOS_THROUGHPUT }, |
{ "throughput", IPTOS_THROUGHPUT }, |
{ "reliability", IPTOS_RELIABILITY }, |
{ "reliability", IPTOS_RELIABILITY }, |
Line 1592 unix_listener(const char *path, int back |
|
Line 1466 unix_listener(const char *path, int back |
|
} |
} |
|
|
sock = socket(PF_UNIX, SOCK_STREAM, 0); |
sock = socket(PF_UNIX, SOCK_STREAM, 0); |
if (sock == -1) { |
if (sock < 0) { |
saved_errno = errno; |
saved_errno = errno; |
error("%s: socket: %.100s", __func__, strerror(errno)); |
error("%s: socket: %.100s", __func__, strerror(errno)); |
errno = saved_errno; |
errno = saved_errno; |
Line 1602 unix_listener(const char *path, int back |
|
Line 1476 unix_listener(const char *path, int back |
|
if (unlink(path) != 0 && errno != ENOENT) |
if (unlink(path) != 0 && errno != ENOENT) |
error("unlink(%s): %.100s", path, strerror(errno)); |
error("unlink(%s): %.100s", path, strerror(errno)); |
} |
} |
if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { |
if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { |
saved_errno = errno; |
saved_errno = errno; |
error("%s: cannot bind to path %s: %s", |
error("%s: cannot bind to path %s: %s", |
__func__, path, strerror(errno)); |
__func__, path, strerror(errno)); |
Line 1610 unix_listener(const char *path, int back |
|
Line 1484 unix_listener(const char *path, int back |
|
errno = saved_errno; |
errno = saved_errno; |
return -1; |
return -1; |
} |
} |
if (listen(sock, backlog) == -1) { |
if (listen(sock, backlog) < 0) { |
saved_errno = errno; |
saved_errno = errno; |
error("%s: cannot listen on path %s: %s", |
error("%s: cannot listen on path %s: %s", |
__func__, path, strerror(errno)); |
__func__, path, strerror(errno)); |
Line 1880 safe_path(const char *name, struct stat |
|
Line 1754 safe_path(const char *name, struct stat |
|
} |
} |
strlcpy(buf, cp, sizeof(buf)); |
strlcpy(buf, cp, sizeof(buf)); |
|
|
if (stat(buf, &st) == -1 || |
if (stat(buf, &st) < 0 || |
(st.st_uid != 0 && st.st_uid != uid) || |
(st.st_uid != 0 && st.st_uid != uid) || |
(st.st_mode & 022) != 0) { |
(st.st_mode & 022) != 0) { |
snprintf(err, errlen, |
snprintf(err, errlen, |
Line 1915 safe_path_fd(int fd, const char *file, s |
|
Line 1789 safe_path_fd(int fd, const char *file, s |
|
struct stat st; |
struct stat st; |
|
|
/* check the open file to avoid races */ |
/* check the open file to avoid races */ |
if (fstat(fd, &st) == -1) { |
if (fstat(fd, &st) < 0) { |
snprintf(err, errlen, "cannot stat file %s: %s", |
snprintf(err, errlen, "cannot stat file %s: %s", |
file, strerror(errno)); |
file, strerror(errno)); |
return -1; |
return -1; |
|
|
return 0; |
return 0; |
} |
} |
|
|
/* |
|
* Verify that a environment variable name (not including initial '$') is |
|
* valid; consisting of one or more alphanumeric or underscore characters only. |
|
* Returns 1 on valid, 0 otherwise. |
|
*/ |
|
int |
|
valid_env_name(const char *name) |
|
{ |
|
const char *cp; |
|
|
|
if (name[0] == '\0') |
|
return 0; |
|
for (cp = name; *cp != '\0'; cp++) { |
|
if (!isalnum((u_char)*cp) && *cp != '_') |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
const char * |
const char * |
atoi_err(const char *nptr, int *val) |
atoi_err(const char *nptr, int *val) |
{ |
{ |
Line 2107 format_absolute_time(uint64_t t, char *b |
|
Line 1962 format_absolute_time(uint64_t t, char *b |
|
localtime_r(&tt, &tm); |
localtime_r(&tt, &tm); |
strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm); |
strftime(buf, len, "%Y-%m-%dT%H:%M:%S", &tm); |
} |
} |
|
|
/* check if path is absolute */ |
|
int |
|
path_absolute(const char *path) |
|
{ |
|
return (*path == '/') ? 1 : 0; |
|
} |
|
|
|
void |
|
skip_space(char **cpp) |
|
{ |
|
char *cp; |
|
|
|
for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) |
|
; |
|
*cpp = cp; |
|
} |
|
|
|
/* authorized_key-style options parsing helpers */ |
|
|
|
/* |
|
* Match flag 'opt' in *optsp, and if allow_negate is set then also match |
|
* 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0 |
|
* if negated option matches. |
|
* If the option or negated option matches, then *optsp is updated to |
|
* point to the first character after the option. |
|
*/ |
|
int |
|
opt_flag(const char *opt, int allow_negate, const char **optsp) |
|
{ |
|
size_t opt_len = strlen(opt); |
|
const char *opts = *optsp; |
|
int negate = 0; |
|
|
|
if (allow_negate && strncasecmp(opts, "no-", 3) == 0) { |
|
opts += 3; |
|
negate = 1; |
|
} |
|
if (strncasecmp(opts, opt, opt_len) == 0) { |
|
*optsp = opts + opt_len; |
|
return negate ? 0 : 1; |
|
} |
|
return -1; |
|
} |
|
|
|
char * |
|
opt_dequote(const char **sp, const char **errstrp) |
|
{ |
|
const char *s = *sp; |
|
char *ret; |
|
size_t i; |
|
|
|
*errstrp = NULL; |
|
if (*s != '"') { |
|
*errstrp = "missing start quote"; |
|
return NULL; |
|
} |
|
s++; |
|
if ((ret = malloc(strlen((s)) + 1)) == NULL) { |
|
*errstrp = "memory allocation failed"; |
|
return NULL; |
|
} |
|
for (i = 0; *s != '\0' && *s != '"';) { |
|
if (s[0] == '\\' && s[1] == '"') |
|
s++; |
|
ret[i++] = *s++; |
|
} |
|
if (*s == '\0') { |
|
*errstrp = "missing end quote"; |
|
free(ret); |
|
return NULL; |
|
} |
|
ret[i] = '\0'; |
|
s++; |
|
*sp = s; |
|
return ret; |
|
} |
|
|
|
int |
|
opt_match(const char **opts, const char *term) |
|
{ |
|
if (strncasecmp((*opts), term, strlen(term)) == 0 && |
|
(*opts)[strlen(term)] == '=') { |
|
*opts += strlen(term) + 1; |
|
return 1; |
|
} |
|
return 0; |
|
} |
|
|
|
sshsig_t |
|
ssh_signal(int signum, sshsig_t handler) |
|
{ |
|
struct sigaction sa, osa; |
|
|
|
/* mask all other signals while in handler */ |
|
bzero(&sa, sizeof(sa)); |
|
sa.sa_handler = handler; |
|
sigfillset(&sa.sa_mask); |
|
if (signum != SIGALRM) |
|
sa.sa_flags = SA_RESTART; |
|
if (sigaction(signum, &sa, &osa) == -1) { |
|
debug3("sigaction(%s): %s", strsignal(signum), strerror(errno)); |
|
return SIG_ERR; |
|
} |
|
return osa.sa_handler; |
|
} |
|