-/* $OpenBSD: misc.c,v 1.2 2001/02/22 21:59:44 markus Exp $ */
-
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
*/
#include "includes.h"
-RCSID("$OpenBSD: misc.c,v 1.2 2001/02/22 21:59:44 markus Exp $");
+RCSID("$OpenBSD: misc.c,v 1.30 2005/04/09 04:32:54 djm Exp $");
#include "misc.h"
#include "log.h"
+#include "xmalloc.h"
+/* remove newline at end of string */
char *
chop(char *s)
{
char *t = s;
while (*t) {
- if(*t == '\n' || *t == '\r') {
+ if (*t == '\n' || *t == '\r') {
*t = '\0';
return s;
}
}
-void
+/* 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;
+ return (-1);
}
if (val & O_NONBLOCK) {
- debug("fd %d IS O_NONBLOCK", fd);
- return;
+ debug3("fd %d is O_NONBLOCK", fd);
+ return (0);
}
- debug("fd %d setting O_NONBLOCK", fd);
+ debug2("fd %d setting O_NONBLOCK", fd);
val |= O_NONBLOCK;
- if (fcntl(fd, F_SETFL, val) == -1)
- if (errno != ENODEV)
- error("fcntl(%d, F_SETFL, O_NONBLOCK): %s",
- fd, strerror(errno));
+ 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);
+}
+
+/* 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"
+/* return next token in configuration line */
char *
strdelim(char **s)
{
pwcopy(struct passwd *pw)
{
struct passwd *copy = xmalloc(sizeof(*copy));
+
memset(copy, 0, sizeof(*copy));
copy->pw_name = xstrdup(pw->pw_name);
copy->pw_passwd = xstrdup(pw->pw_passwd);
+ copy->pw_gecos = xstrdup(pw->pw_gecos);
copy->pw_uid = pw->pw_uid;
copy->pw_gid = pw->pw_gid;
+#ifdef HAVE_PW_EXPIRE_IN_PASSWD
+ copy->pw_expire = pw->pw_expire;
+#endif
+#ifdef HAVE_PW_CHANGE_IN_PASSWD
+ copy->pw_change = pw->pw_change;
+#endif
+#ifdef HAVE_PW_CLASS_IN_PASSWD
copy->pw_class = xstrdup(pw->pw_class);
+#endif
copy->pw_dir = xstrdup(pw->pw_dir);
copy->pw_shell = xstrdup(pw->pw_shell);
return copy;
}
-mysig_t
-mysignal(int sig, mysig_t act)
+/*
+ * Convert ASCII string to TCP/IP port number.
+ * Port must be >0 and <=65535.
+ * Return 0 if invalid.
+ */
+int
+a2port(const char *s)
{
-#ifdef HAVE_SIGACTION
- struct sigaction sa, osa;
-
- if (sigaction(sig, NULL, &osa) == -1)
- return (mysig_t) -1;
- if (osa.sa_handler != act) {
- memset(&sa, 0, sizeof(sa));
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = 0;
-#if defined(SA_RESTART)
- if (sig == SIGCHLD)
- sa.sa_flags |= SA_RESTART;
-#endif
-#if defined(SA_INTERRUPT)
- if (sig == SIGALRM)
- sa.sa_flags |= SA_INTERRUPT;
-#endif
- sa.sa_handler = act;
- if (sigaction(sig, &sa, NULL) == -1)
- return (mysig_t) -1;
+ long port;
+ char *endp;
+
+ errno = 0;
+ port = strtol(s, &endp, 0);
+ if (s == endp || *endp != '\0' ||
+ (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) ||
+ port <= 0 || port > 65535)
+ return 0;
+
+ return port;
+}
+
+#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--;
+ 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 (osa.sa_handler);
-#else
- return (signal(sig, act));
-#endif
+
+ return total;
+}
+
+/*
+ * 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 (0);
+ 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 (0);
+ }
+ return (0);
+}
+
+/* function to assist building execv() arguments */
+void
+addargs(arglist *args, char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+ u_int nalloc;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ 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++] = xstrdup(buf);
+ args->list[args->num] = 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;
+ char user[128], ret[MAXPATHLEN];
+ struct passwd *pw;
+ int len;
+
+ if (*filename != '~')
+ return (xstrdup(filename));
+ filename++;
+
+ path = strchr(filename, '/');
+ if (path != NULL && path > filename) { /* ~user/path */
+ if (path - filename > sizeof(user) - 1)
+ fatal("tilde_expand_filename: ~username too long");
+ memcpy(user, filename, path - filename);
+ user[path - filename] = '\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 %d", uid);
+
+ if (strlcpy(ret, pw->pw_dir, sizeof(ret)) >= sizeof(ret))
+ fatal("tilde_expand_filename: Path too long");
+
+ /* Make sure directory has a trailing '/' */
+ len = strlen(pw->pw_dir);
+ if ((len == 0 || pw->pw_dir[len - 1] != '/') &&
+ strlcat(ret, "/", sizeof(ret)) >= sizeof(ret))
+ fatal("tilde_expand_filename: Path too long");
+
+ /* Skip leading '/' from specified path */
+ if (path != NULL)
+ filename = path + 1;
+ if (strlcat(ret, filename, sizeof(ret)) >= sizeof(ret))
+ fatal("tilde_expand_filename: Path too long");
+
+ return (xstrdup(ret));
+}
+
+/*
+ * 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) {
+ (*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;
}