openssh/misc.c
deraadt@openbsd.org 2ae4f337b2 upstream commit
Replace <sys/param.h> with <limits.h> and other less
 dirty headers where possible.  Annotate <sys/param.h> lines with their
 current reasons.  Switch to PATH_MAX, NGROUPS_MAX, HOST_NAME_MAX+1,
 LOGIN_NAME_MAX, etc.  Change MIN() and MAX() to local definitions of
 MINIMUM() and MAXIMUM() where sensible to avoid pulling in the pollution.
 These are the files confirmed through binary verification. ok guenther,
 millert, doug (helped with the verification protocol)
2015-01-16 18:24:48 +11:00

1118 lines
23 KiB
C

/* $OpenBSD: misc.c,v 1.96 2015/01/16 06:40:12 deraadt Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2005,2006 Damien Miller. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "includes.h"
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#ifdef HAVE_PATHS_H
# include <paths.h>
#include <pwd.h>
#endif
#ifdef SSH_TUN_OPENBSD
#include <net/if.h>
#endif
#include "xmalloc.h"
#include "misc.h"
#include "log.h"
#include "ssh.h"
/* remove newline at end of string */
char *
chop(char *s)
{
char *t = s;
while (*t) {
if (*t == '\n' || *t == '\r') {
*t = '\0';
return s;
}
t++;
}
return s;
}
/* set/unset filedescriptor to non-blocking */
int
set_nonblock(int fd)
{
int val;
val = fcntl(fd, F_GETFL, 0);
if (val < 0) {
error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
return (-1);
}
if (val & O_NONBLOCK) {
debug3("fd %d is O_NONBLOCK", fd);
return (0);
}
debug2("fd %d setting O_NONBLOCK", fd);
val |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, val) == -1) {
debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd,
strerror(errno));
return (-1);
}
return (0);
}
int
unset_nonblock(int fd)
{
int val;
val = fcntl(fd, F_GETFL, 0);
if (val < 0) {
error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
return (-1);
}
if (!(val & O_NONBLOCK)) {
debug3("fd %d is not O_NONBLOCK", fd);
return (0);
}
debug("fd %d clearing O_NONBLOCK", fd);
val &= ~O_NONBLOCK;
if (fcntl(fd, F_SETFL, val) == -1) {
debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",
fd, strerror(errno));
return (-1);
}
return (0);
}
const char *
ssh_gai_strerror(int gaierr)
{
if (gaierr == EAI_SYSTEM && errno != 0)
return strerror(errno);
return gai_strerror(gaierr);
}
/* disable nagle on socket */
void
set_nodelay(int fd)
{
int opt;
socklen_t optlen;
optlen = sizeof opt;
if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) {
debug("getsockopt TCP_NODELAY: %.100s", strerror(errno));
return;
}
if (opt == 1) {
debug2("fd %d is TCP_NODELAY", fd);
return;
}
opt = 1;
debug2("fd %d setting TCP_NODELAY", fd);
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)
error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
}
/* Characters considered whitespace in strsep calls. */
#define WHITESPACE " \t\r\n"
#define QUOTE "\""
/* return next token in configuration line */
char *
strdelim(char **s)
{
char *old;
int wspace = 0;
if (*s == NULL)
return NULL;
old = *s;
*s = strpbrk(*s, WHITESPACE QUOTE "=");
if (*s == NULL)
return (old);
if (*s[0] == '\"') {
memmove(*s, *s + 1, strlen(*s)); /* move nul too */
/* Find matching quote */
if ((*s = strpbrk(*s, QUOTE)) == NULL) {
return (NULL); /* no matching quote */
} else {
*s[0] = '\0';
*s += strspn(*s + 1, WHITESPACE) + 1;
return (old);
}
}
/* Allow only one '=' to be skipped */
if (*s[0] == '=')
wspace = 1;
*s[0] = '\0';
/* Skip any extra whitespace after first token */
*s += strspn(*s + 1, WHITESPACE) + 1;
if (*s[0] == '=' && !wspace)
*s += strspn(*s + 1, WHITESPACE) + 1;
return (old);
}
struct passwd *
pwcopy(struct passwd *pw)
{
struct passwd *copy = xcalloc(1, sizeof(*copy));
copy->pw_name = xstrdup(pw->pw_name);
copy->pw_passwd = xstrdup(pw->pw_passwd);
#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
copy->pw_gecos = xstrdup(pw->pw_gecos);
#endif
copy->pw_uid = pw->pw_uid;
copy->pw_gid = pw->pw_gid;
#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
copy->pw_expire = pw->pw_expire;
#endif
#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE
copy->pw_change = pw->pw_change;
#endif
#ifdef HAVE_STRUCT_PASSWD_PW_CLASS
copy->pw_class = xstrdup(pw->pw_class);
#endif
copy->pw_dir = xstrdup(pw->pw_dir);
copy->pw_shell = xstrdup(pw->pw_shell);
return copy;
}
/*
* Convert ASCII string to TCP/IP port number.
* Port must be >=0 and <=65535.
* Return -1 if invalid.
*/
int
a2port(const char *s)
{
long long port;
const char *errstr;
port = strtonum(s, 0, 65535, &errstr);
if (errstr != NULL)
return -1;
return (int)port;
}
int
a2tun(const char *s, int *remote)
{
const char *errstr = NULL;
char *sp, *ep;
int tun;
if (remote != NULL) {
*remote = SSH_TUNID_ANY;
sp = xstrdup(s);
if ((ep = strchr(sp, ':')) == NULL) {
free(sp);
return (a2tun(s, NULL));
}
ep[0] = '\0'; ep++;
*remote = a2tun(ep, NULL);
tun = a2tun(sp, NULL);
free(sp);
return (*remote == SSH_TUNID_ERR ? *remote : tun);
}
if (strcasecmp(s, "any") == 0)
return (SSH_TUNID_ANY);
tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr);
if (errstr != NULL)
return (SSH_TUNID_ERR);
return (tun);
}
#define SECONDS 1
#define MINUTES (SECONDS * 60)
#define HOURS (MINUTES * 60)
#define DAYS (HOURS * 24)
#define WEEKS (DAYS * 7)
/*
* Convert a time string into seconds; format is
* a sequence of:
* time[qualifier]
*
* Valid time qualifiers are:
* <none> seconds
* s|S seconds
* m|M minutes
* h|H hours
* d|D days
* w|W weeks
*
* Examples:
* 90m 90 minutes
* 1h30m 90 minutes
* 2d 2 days
* 1w 1 week
*
* Return -1 if time string is invalid.
*/
long
convtime(const char *s)
{
long total, secs;
const char *p;
char *endp;
errno = 0;
total = 0;
p = s;
if (p == NULL || *p == '\0')
return -1;
while (*p) {
secs = strtol(p, &endp, 10);
if (p == endp ||
(errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) ||
secs < 0)
return -1;
switch (*endp++) {
case '\0':
endp--;
break;
case 's':
case 'S':
break;
case 'm':
case 'M':
secs *= MINUTES;
break;
case 'h':
case 'H':
secs *= HOURS;
break;
case 'd':
case 'D':
secs *= DAYS;
break;
case 'w':
case 'W':
secs *= WEEKS;
break;
default:
return -1;
}
total += secs;
if (total < 0)
return -1;
p = endp;
}
return total;
}
/*
* Returns a standardized host+port identifier string.
* Caller must free returned string.
*/
char *
put_host_port(const char *host, u_short port)
{
char *hoststr;
if (port == 0 || port == SSH_DEFAULT_PORT)
return(xstrdup(host));
if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0)
fatal("put_host_port: asprintf: %s", strerror(errno));
debug3("put_host_port: %s", hoststr);
return hoststr;
}
/*
* Search for next delimiter between hostnames/addresses and ports.
* Argument may be modified (for termination).
* Returns *cp if parsing succeeds.
* *cp is set to the start of the next delimiter, if one was found.
* If this is the last field, *cp is set to NULL.
*/
char *
hpdelim(char **cp)
{
char *s, *old;
if (cp == NULL || *cp == NULL)
return NULL;
old = s = *cp;
if (*s == '[') {
if ((s = strchr(s, ']')) == NULL)
return NULL;
else
s++;
} else if ((s = strpbrk(s, ":/")) == NULL)
s = *cp + strlen(*cp); /* skip to end (see first case below) */
switch (*s) {
case '\0':
*cp = NULL; /* no more fields*/
break;
case ':':
case '/':
*s = '\0'; /* terminate */
*cp = s + 1;
break;
default:
return NULL;
}
return old;
}
char *
cleanhostname(char *host)
{
if (*host == '[' && host[strlen(host) - 1] == ']') {
host[strlen(host) - 1] = '\0';
return (host + 1);
} else
return host;
}
char *
colon(char *cp)
{
int flag = 0;
if (*cp == ':') /* Leading colon is part of file name. */
return NULL;
if (*cp == '[')
flag = 1;
for (; *cp; ++cp) {
if (*cp == '@' && *(cp+1) == '[')
flag = 1;
if (*cp == ']' && *(cp+1) == ':' && flag)
return (cp+1);
if (*cp == ':' && !flag)
return (cp);
if (*cp == '/')
return NULL;
}
return NULL;
}
/* function to assist building execv() arguments */
void
addargs(arglist *args, char *fmt, ...)
{
va_list ap;
char *cp;
u_int nalloc;
int r;
va_start(ap, fmt);
r = vasprintf(&cp, fmt, ap);
va_end(ap);
if (r == -1)
fatal("addargs: argument too long");
nalloc = args->nalloc;
if (args->list == NULL) {
nalloc = 32;
args->num = 0;
} else if (args->num+2 >= nalloc)
nalloc *= 2;
args->list = xrealloc(args->list, nalloc, sizeof(char *));
args->nalloc = nalloc;
args->list[args->num++] = cp;
args->list[args->num] = NULL;
}
void
replacearg(arglist *args, u_int which, char *fmt, ...)
{
va_list ap;
char *cp;
int r;
va_start(ap, fmt);
r = vasprintf(&cp, fmt, ap);
va_end(ap);
if (r == -1)
fatal("replacearg: argument too long");
if (which >= args->num)
fatal("replacearg: tried to replace invalid arg %d >= %d",
which, args->num);
free(args->list[which]);
args->list[which] = cp;
}
void
freeargs(arglist *args)
{
u_int i;
if (args->list != NULL) {
for (i = 0; i < args->num; i++)
free(args->list[i]);
free(args->list);
args->nalloc = args->num = 0;
args->list = NULL;
}
}
/*
* Expands tildes in the file name. Returns data allocated by xmalloc.
* Warning: this calls getpw*.
*/
char *
tilde_expand_filename(const char *filename, uid_t uid)
{
const char *path, *sep;
char user[128], *ret;
struct passwd *pw;
u_int len, slash;
if (*filename != '~')
return (xstrdup(filename));
filename++;
path = strchr(filename, '/');
if (path != NULL && path > filename) { /* ~user/path */
slash = path - filename;
if (slash > sizeof(user) - 1)
fatal("tilde_expand_filename: ~username too long");
memcpy(user, filename, slash);
user[slash] = '\0';
if ((pw = getpwnam(user)) == NULL)
fatal("tilde_expand_filename: No such user %s", user);
} else if ((pw = getpwuid(uid)) == NULL) /* ~/path */
fatal("tilde_expand_filename: No such uid %ld", (long)uid);
/* Make sure directory has a trailing '/' */
len = strlen(pw->pw_dir);
if (len == 0 || pw->pw_dir[len - 1] != '/')
sep = "/";
else
sep = "";
/* Skip leading '/' from specified path */
if (path != NULL)
filename = path + 1;
if (xasprintf(&ret, "%s%s%s", pw->pw_dir, sep, filename) >= PATH_MAX)
fatal("tilde_expand_filename: Path too long");
return (ret);
}
/*
* Expand a string with a set of %[char] escapes. A number of escapes may be
* specified as (char *escape_chars, char *replacement) pairs. The list must
* be terminated by a NULL escape_char. Returns replaced string in memory
* allocated by xmalloc.
*/
char *
percent_expand(const char *string, ...)
{
#define EXPAND_MAX_KEYS 16
u_int num_keys, i, j;
struct {
const char *key;
const char *repl;
} keys[EXPAND_MAX_KEYS];
char buf[4096];
va_list ap;
/* Gather keys */
va_start(ap, string);
for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) {
keys[num_keys].key = va_arg(ap, char *);
if (keys[num_keys].key == NULL)
break;
keys[num_keys].repl = va_arg(ap, char *);
if (keys[num_keys].repl == NULL)
fatal("%s: NULL replacement", __func__);
}
if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL)
fatal("%s: too many keys", __func__);
va_end(ap);
/* Expand string */
*buf = '\0';
for (i = 0; *string != '\0'; string++) {
if (*string != '%') {
append:
buf[i++] = *string;
if (i >= sizeof(buf))
fatal("%s: string too long", __func__);
buf[i] = '\0';
continue;
}
string++;
/* %% case */
if (*string == '%')
goto append;
for (j = 0; j < num_keys; j++) {
if (strchr(keys[j].key, *string) != NULL) {
i = strlcat(buf, keys[j].repl, sizeof(buf));
if (i >= sizeof(buf))
fatal("%s: string too long", __func__);
break;
}
}
if (j >= num_keys)
fatal("%s: unknown key %%%c", __func__, *string);
}
return (xstrdup(buf));
#undef EXPAND_MAX_KEYS
}
/*
* Read an entire line from a public key file into a static buffer, discarding
* lines that exceed the buffer size. Returns 0 on success, -1 on failure.
*/
int
read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
u_long *lineno)
{
while (fgets(buf, bufsz, f) != NULL) {
if (buf[0] == '\0')
continue;
(*lineno)++;
if (buf[strlen(buf) - 1] == '\n' || feof(f)) {
return 0;
} else {
debug("%s: %s line %lu exceeds size limit", __func__,
filename, *lineno);
/* discard remainder of line */
while (fgetc(f) != '\n' && !feof(f))
; /* nothing */
}
}
return -1;
}
int
tun_open(int tun, int mode)
{
#if defined(CUSTOM_SYS_TUN_OPEN)
return (sys_tun_open(tun, mode));
#elif defined(SSH_TUN_OPENBSD)
struct ifreq ifr;
char name[100];
int fd = -1, sock;
/* Open the tunnel device */
if (tun <= SSH_TUNID_MAX) {
snprintf(name, sizeof(name), "/dev/tun%d", tun);
fd = open(name, O_RDWR);
} else if (tun == SSH_TUNID_ANY) {
for (tun = 100; tun >= 0; tun--) {
snprintf(name, sizeof(name), "/dev/tun%d", tun);
if ((fd = open(name, O_RDWR)) >= 0)
break;
}
} else {
debug("%s: invalid tunnel %u", __func__, tun);
return (-1);
}
if (fd < 0) {
debug("%s: %s open failed: %s", __func__, name, strerror(errno));
return (-1);
}
debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
/* Set the tunnel device operation mode */
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "tun%d", tun);
if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
goto failed;
if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
goto failed;
/* Set interface mode */
ifr.ifr_flags &= ~IFF_UP;
if (mode == SSH_TUNMODE_ETHERNET)
ifr.ifr_flags |= IFF_LINK0;
else
ifr.ifr_flags &= ~IFF_LINK0;
if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
goto failed;
/* Bring interface up */
ifr.ifr_flags |= IFF_UP;
if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
goto failed;
close(sock);
return (fd);
failed:
if (fd >= 0)
close(fd);
if (sock >= 0)
close(sock);
debug("%s: failed to set %s mode %d: %s", __func__, name,
mode, strerror(errno));
return (-1);
#else
error("Tunnel interfaces are not supported on this platform");
return (-1);
#endif
}
void
sanitise_stdfd(void)
{
int nullfd, dupfd;
if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
fprintf(stderr, "Couldn't open /dev/null: %s\n",
strerror(errno));
exit(1);
}
while (++dupfd <= 2) {
/* Only clobber closed fds */
if (fcntl(dupfd, F_GETFL, 0) >= 0)
continue;
if (dup2(nullfd, dupfd) == -1) {
fprintf(stderr, "dup2: %s\n", strerror(errno));
exit(1);
}
}
if (nullfd > 2)
close(nullfd);
}
char *
tohex(const void *vp, size_t l)
{
const u_char *p = (const u_char *)vp;
char b[3], *r;
size_t i, hl;
if (l > 65536)
return xstrdup("tohex: length > 65536");
hl = l * 2 + 1;
r = xcalloc(1, hl);
for (i = 0; i < l; i++) {
snprintf(b, sizeof(b), "%02x", p[i]);
strlcat(r, b, hl);
}
return (r);
}
u_int64_t
get_u64(const void *vp)
{
const u_char *p = (const u_char *)vp;
u_int64_t v;
v = (u_int64_t)p[0] << 56;
v |= (u_int64_t)p[1] << 48;
v |= (u_int64_t)p[2] << 40;
v |= (u_int64_t)p[3] << 32;
v |= (u_int64_t)p[4] << 24;
v |= (u_int64_t)p[5] << 16;
v |= (u_int64_t)p[6] << 8;
v |= (u_int64_t)p[7];
return (v);
}
u_int32_t
get_u32(const void *vp)
{
const u_char *p = (const u_char *)vp;
u_int32_t v;
v = (u_int32_t)p[0] << 24;
v |= (u_int32_t)p[1] << 16;
v |= (u_int32_t)p[2] << 8;
v |= (u_int32_t)p[3];
return (v);
}
u_int32_t
get_u32_le(const void *vp)
{
const u_char *p = (const u_char *)vp;
u_int32_t v;
v = (u_int32_t)p[0];
v |= (u_int32_t)p[1] << 8;
v |= (u_int32_t)p[2] << 16;
v |= (u_int32_t)p[3] << 24;
return (v);
}
u_int16_t
get_u16(const void *vp)
{
const u_char *p = (const u_char *)vp;
u_int16_t v;
v = (u_int16_t)p[0] << 8;
v |= (u_int16_t)p[1];
return (v);
}
void
put_u64(void *vp, u_int64_t v)
{
u_char *p = (u_char *)vp;
p[0] = (u_char)(v >> 56) & 0xff;
p[1] = (u_char)(v >> 48) & 0xff;
p[2] = (u_char)(v >> 40) & 0xff;
p[3] = (u_char)(v >> 32) & 0xff;
p[4] = (u_char)(v >> 24) & 0xff;
p[5] = (u_char)(v >> 16) & 0xff;
p[6] = (u_char)(v >> 8) & 0xff;
p[7] = (u_char)v & 0xff;
}
void
put_u32(void *vp, u_int32_t v)
{
u_char *p = (u_char *)vp;
p[0] = (u_char)(v >> 24) & 0xff;
p[1] = (u_char)(v >> 16) & 0xff;
p[2] = (u_char)(v >> 8) & 0xff;
p[3] = (u_char)v & 0xff;
}
void
put_u32_le(void *vp, u_int32_t v)
{
u_char *p = (u_char *)vp;
p[0] = (u_char)v & 0xff;
p[1] = (u_char)(v >> 8) & 0xff;
p[2] = (u_char)(v >> 16) & 0xff;
p[3] = (u_char)(v >> 24) & 0xff;
}
void
put_u16(void *vp, u_int16_t v)
{
u_char *p = (u_char *)vp;
p[0] = (u_char)(v >> 8) & 0xff;
p[1] = (u_char)v & 0xff;
}
void
ms_subtract_diff(struct timeval *start, int *ms)
{
struct timeval diff, finish;
gettimeofday(&finish, NULL);
timersub(&finish, start, &diff);
*ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000);
}
void
ms_to_timeval(struct timeval *tv, int ms)
{
if (ms < 0)
ms = 0;
tv->tv_sec = ms / 1000;
tv->tv_usec = (ms % 1000) * 1000;
}
time_t
monotime(void)
{
#if defined(HAVE_CLOCK_GETTIME) && \
(defined(CLOCK_MONOTONIC) || defined(CLOCK_BOOTTIME))
struct timespec ts;
static int gettime_failed = 0;
if (!gettime_failed) {
#if defined(CLOCK_BOOTTIME)
if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0)
return (ts.tv_sec);
#endif
#if defined(CLOCK_MONOTONIC)
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
return (ts.tv_sec);
#endif
debug3("clock_gettime: %s", strerror(errno));
gettime_failed = 1;
}
#endif /* HAVE_CLOCK_GETTIME && (CLOCK_MONOTONIC || CLOCK_BOOTTIME */
return time(NULL);
}
void
bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen)
{
bw->buflen = buflen;
bw->rate = kbps;
bw->thresh = bw->rate;
bw->lamt = 0;
timerclear(&bw->bwstart);
timerclear(&bw->bwend);
}
/* Callback from read/write loop to insert bandwidth-limiting delays */
void
bandwidth_limit(struct bwlimit *bw, size_t read_len)
{
u_int64_t waitlen;
struct timespec ts, rm;
if (!timerisset(&bw->bwstart)) {
gettimeofday(&bw->bwstart, NULL);
return;
}
bw->lamt += read_len;
if (bw->lamt < bw->thresh)
return;
gettimeofday(&bw->bwend, NULL);
timersub(&bw->bwend, &bw->bwstart, &bw->bwend);
if (!timerisset(&bw->bwend))
return;
bw->lamt *= 8;
waitlen = (double)1000000L * bw->lamt / bw->rate;
bw->bwstart.tv_sec = waitlen / 1000000L;
bw->bwstart.tv_usec = waitlen % 1000000L;
if (timercmp(&bw->bwstart, &bw->bwend, >)) {
timersub(&bw->bwstart, &bw->bwend, &bw->bwend);
/* Adjust the wait time */
if (bw->bwend.tv_sec) {
bw->thresh /= 2;
if (bw->thresh < bw->buflen / 4)
bw->thresh = bw->buflen / 4;
} else if (bw->bwend.tv_usec < 10000) {
bw->thresh *= 2;
if (bw->thresh > bw->buflen * 8)
bw->thresh = bw->buflen * 8;
}
TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts);
while (nanosleep(&ts, &rm) == -1) {
if (errno != EINTR)
break;
ts = rm;
}
}
bw->lamt = 0;
gettimeofday(&bw->bwstart, NULL);
}
/* Make a template filename for mk[sd]temp() */
void
mktemp_proto(char *s, size_t len)
{
const char *tmpdir;
int r;
if ((tmpdir = getenv("TMPDIR")) != NULL) {
r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir);
if (r > 0 && (size_t)r < len)
return;
}
r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX");
if (r < 0 || (size_t)r >= len)
fatal("%s: template string too short", __func__);
}
static const struct {
const char *name;
int value;
} ipqos[] = {
{ "af11", IPTOS_DSCP_AF11 },
{ "af12", IPTOS_DSCP_AF12 },
{ "af13", IPTOS_DSCP_AF13 },
{ "af21", IPTOS_DSCP_AF21 },
{ "af22", IPTOS_DSCP_AF22 },
{ "af23", IPTOS_DSCP_AF23 },
{ "af31", IPTOS_DSCP_AF31 },
{ "af32", IPTOS_DSCP_AF32 },
{ "af33", IPTOS_DSCP_AF33 },
{ "af41", IPTOS_DSCP_AF41 },
{ "af42", IPTOS_DSCP_AF42 },
{ "af43", IPTOS_DSCP_AF43 },
{ "cs0", IPTOS_DSCP_CS0 },
{ "cs1", IPTOS_DSCP_CS1 },
{ "cs2", IPTOS_DSCP_CS2 },
{ "cs3", IPTOS_DSCP_CS3 },
{ "cs4", IPTOS_DSCP_CS4 },
{ "cs5", IPTOS_DSCP_CS5 },
{ "cs6", IPTOS_DSCP_CS6 },
{ "cs7", IPTOS_DSCP_CS7 },
{ "ef", IPTOS_DSCP_EF },
{ "lowdelay", IPTOS_LOWDELAY },
{ "throughput", IPTOS_THROUGHPUT },
{ "reliability", IPTOS_RELIABILITY },
{ NULL, -1 }
};
int
parse_ipqos(const char *cp)
{
u_int i;
char *ep;
long val;
if (cp == NULL)
return -1;
for (i = 0; ipqos[i].name != NULL; i++) {
if (strcasecmp(cp, ipqos[i].name) == 0)
return ipqos[i].value;
}
/* Try parsing as an integer */
val = strtol(cp, &ep, 0);
if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255)
return -1;
return val;
}
const char *
iptos2str(int iptos)
{
int i;
static char iptos_str[sizeof "0xff"];
for (i = 0; ipqos[i].name != NULL; i++) {
if (ipqos[i].value == iptos)
return ipqos[i].name;
}
snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos);
return iptos_str;
}
void
lowercase(char *s)
{
for (; *s; s++)
*s = tolower((u_char)*s);
}
int
unix_listener(const char *path, int backlog, int unlink_first)
{
struct sockaddr_un sunaddr;
int saved_errno, sock;
memset(&sunaddr, 0, sizeof(sunaddr));
sunaddr.sun_family = AF_UNIX;
if (strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) {
error("%s: \"%s\" too long for Unix domain socket", __func__,
path);
errno = ENAMETOOLONG;
return -1;
}
sock = socket(PF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
saved_errno = errno;
error("socket: %.100s", strerror(errno));
errno = saved_errno;
return -1;
}
if (unlink_first == 1) {
if (unlink(path) != 0 && errno != ENOENT)
error("unlink(%s): %.100s", path, strerror(errno));
}
if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
saved_errno = errno;
error("bind: %.100s", strerror(errno));
close(sock);
error("%s: cannot bind to path: %s", __func__, path);
errno = saved_errno;
return -1;
}
if (listen(sock, backlog) < 0) {
saved_errno = errno;
error("listen: %.100s", strerror(errno));
close(sock);
unlink(path);
error("%s: cannot listen on path: %s", __func__, path);
errno = saved_errno;
return -1;
}
return sock;
}
void
sock_set_v6only(int s)
{
#ifdef IPV6_V6ONLY
int on = 1;
debug3("%s: set socket %d IPV6_V6ONLY", __func__, s);
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1)
error("setsockopt IPV6_V6ONLY: %s", strerror(errno));
#endif
}