misc.c revision 146998
176259Sgreen/* 276259Sgreen * Copyright (c) 2000 Markus Friedl. All rights reserved. 376259Sgreen * 476259Sgreen * Redistribution and use in source and binary forms, with or without 576259Sgreen * modification, are permitted provided that the following conditions 676259Sgreen * are met: 776259Sgreen * 1. Redistributions of source code must retain the above copyright 876259Sgreen * notice, this list of conditions and the following disclaimer. 976259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1076259Sgreen * notice, this list of conditions and the following disclaimer in the 1176259Sgreen * documentation and/or other materials provided with the distribution. 1276259Sgreen * 1376259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1476259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1576259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1676259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1776259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1876259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1976259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2076259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2176259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2276259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2376259Sgreen */ 2476259Sgreen 2576259Sgreen#include "includes.h" 26146998SdesRCSID("$OpenBSD: misc.c,v 1.28 2005/03/01 10:09:52 djm Exp $"); 2776259Sgreen 2876259Sgreen#include "misc.h" 2976259Sgreen#include "log.h" 3076259Sgreen#include "xmalloc.h" 3176259Sgreen 3292555Sdes/* remove newline at end of string */ 3376259Sgreenchar * 3476259Sgreenchop(char *s) 3576259Sgreen{ 3676259Sgreen char *t = s; 3776259Sgreen while (*t) { 3892555Sdes if (*t == '\n' || *t == '\r') { 3976259Sgreen *t = '\0'; 4076259Sgreen return s; 4176259Sgreen } 4276259Sgreen t++; 4376259Sgreen } 4476259Sgreen return s; 4576259Sgreen 4676259Sgreen} 4776259Sgreen 4892555Sdes/* set/unset filedescriptor to non-blocking */ 49137015Sdesint 5076259Sgreenset_nonblock(int fd) 5176259Sgreen{ 5276259Sgreen int val; 5392555Sdes 5476259Sgreen val = fcntl(fd, F_GETFL, 0); 5576259Sgreen if (val < 0) { 5676259Sgreen error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 57137015Sdes return (-1); 5876259Sgreen } 5976259Sgreen if (val & O_NONBLOCK) { 60137015Sdes debug3("fd %d is O_NONBLOCK", fd); 61137015Sdes return (0); 6276259Sgreen } 63124208Sdes debug2("fd %d setting O_NONBLOCK", fd); 6476259Sgreen val |= O_NONBLOCK; 65137015Sdes if (fcntl(fd, F_SETFL, val) == -1) { 66137015Sdes debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, 67137015Sdes strerror(errno)); 68137015Sdes return (-1); 69137015Sdes } 70137015Sdes return (0); 7176259Sgreen} 7276259Sgreen 73137015Sdesint 7492555Sdesunset_nonblock(int fd) 7592555Sdes{ 7692555Sdes int val; 7792555Sdes 7892555Sdes val = fcntl(fd, F_GETFL, 0); 7992555Sdes if (val < 0) { 8092555Sdes error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno)); 81137015Sdes return (-1); 8292555Sdes } 8392555Sdes if (!(val & O_NONBLOCK)) { 84137015Sdes debug3("fd %d is not O_NONBLOCK", fd); 85137015Sdes return (0); 8692555Sdes } 8792555Sdes debug("fd %d clearing O_NONBLOCK", fd); 8892555Sdes val &= ~O_NONBLOCK; 89137015Sdes if (fcntl(fd, F_SETFL, val) == -1) { 90137015Sdes debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", 9192555Sdes fd, strerror(errno)); 92137015Sdes return (-1); 93137015Sdes } 94137015Sdes return (0); 9592555Sdes} 9692555Sdes 9792555Sdes/* disable nagle on socket */ 9892555Sdesvoid 9992555Sdesset_nodelay(int fd) 10092555Sdes{ 10192555Sdes int opt; 10292555Sdes socklen_t optlen; 10392555Sdes 10492555Sdes optlen = sizeof opt; 10592555Sdes if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { 106126274Sdes debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); 10792555Sdes return; 10892555Sdes } 10992555Sdes if (opt == 1) { 11092555Sdes debug2("fd %d is TCP_NODELAY", fd); 11192555Sdes return; 11292555Sdes } 11392555Sdes opt = 1; 114113908Sdes debug2("fd %d setting TCP_NODELAY", fd); 11592555Sdes if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) 11692555Sdes error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 11792555Sdes} 11892555Sdes 11976259Sgreen/* Characters considered whitespace in strsep calls. */ 12076259Sgreen#define WHITESPACE " \t\r\n" 12176259Sgreen 12292555Sdes/* return next token in configuration line */ 12376259Sgreenchar * 12476259Sgreenstrdelim(char **s) 12576259Sgreen{ 12676259Sgreen char *old; 12776259Sgreen int wspace = 0; 12876259Sgreen 12976259Sgreen if (*s == NULL) 13076259Sgreen return NULL; 13176259Sgreen 13276259Sgreen old = *s; 13376259Sgreen 13476259Sgreen *s = strpbrk(*s, WHITESPACE "="); 13576259Sgreen if (*s == NULL) 13676259Sgreen return (old); 13776259Sgreen 13876259Sgreen /* Allow only one '=' to be skipped */ 13976259Sgreen if (*s[0] == '=') 14076259Sgreen wspace = 1; 14176259Sgreen *s[0] = '\0'; 14276259Sgreen 14376259Sgreen *s += strspn(*s + 1, WHITESPACE) + 1; 14476259Sgreen if (*s[0] == '=' && !wspace) 14576259Sgreen *s += strspn(*s + 1, WHITESPACE) + 1; 14676259Sgreen 14776259Sgreen return (old); 14876259Sgreen} 14976259Sgreen 15076259Sgreenstruct passwd * 15176259Sgreenpwcopy(struct passwd *pw) 15276259Sgreen{ 15376259Sgreen struct passwd *copy = xmalloc(sizeof(*copy)); 15476259Sgreen 15576259Sgreen memset(copy, 0, sizeof(*copy)); 15676259Sgreen copy->pw_name = xstrdup(pw->pw_name); 15776259Sgreen copy->pw_passwd = xstrdup(pw->pw_passwd); 15876259Sgreen copy->pw_gecos = xstrdup(pw->pw_gecos); 15976259Sgreen copy->pw_uid = pw->pw_uid; 16076259Sgreen copy->pw_gid = pw->pw_gid; 16198937Sdes#ifdef HAVE_PW_EXPIRE_IN_PASSWD 16292555Sdes copy->pw_expire = pw->pw_expire; 16398937Sdes#endif 16498937Sdes#ifdef HAVE_PW_CHANGE_IN_PASSWD 16592555Sdes copy->pw_change = pw->pw_change; 16698937Sdes#endif 16798937Sdes#ifdef HAVE_PW_CLASS_IN_PASSWD 16876259Sgreen copy->pw_class = xstrdup(pw->pw_class); 16998937Sdes#endif 17076259Sgreen copy->pw_dir = xstrdup(pw->pw_dir); 17176259Sgreen copy->pw_shell = xstrdup(pw->pw_shell); 17276259Sgreen return copy; 17376259Sgreen} 17476259Sgreen 17592555Sdes/* 17692555Sdes * Convert ASCII string to TCP/IP port number. 17792555Sdes * Port must be >0 and <=65535. 17892555Sdes * Return 0 if invalid. 17992555Sdes */ 18092555Sdesint 18192555Sdesa2port(const char *s) 18276259Sgreen{ 18376259Sgreen long port; 18476259Sgreen char *endp; 18576259Sgreen 18676259Sgreen errno = 0; 18776259Sgreen port = strtol(s, &endp, 0); 18876259Sgreen if (s == endp || *endp != '\0' || 18976259Sgreen (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) || 19076259Sgreen port <= 0 || port > 65535) 19176259Sgreen return 0; 19276259Sgreen 19376259Sgreen return port; 19476259Sgreen} 19592555Sdes 19692555Sdes#define SECONDS 1 19792555Sdes#define MINUTES (SECONDS * 60) 19892555Sdes#define HOURS (MINUTES * 60) 19992555Sdes#define DAYS (HOURS * 24) 20092555Sdes#define WEEKS (DAYS * 7) 20192555Sdes 20292555Sdes/* 20392555Sdes * Convert a time string into seconds; format is 20492555Sdes * a sequence of: 20592555Sdes * time[qualifier] 20692555Sdes * 20792555Sdes * Valid time qualifiers are: 20892555Sdes * <none> seconds 20992555Sdes * s|S seconds 21092555Sdes * m|M minutes 21192555Sdes * h|H hours 21292555Sdes * d|D days 21392555Sdes * w|W weeks 21492555Sdes * 21592555Sdes * Examples: 21692555Sdes * 90m 90 minutes 21792555Sdes * 1h30m 90 minutes 21892555Sdes * 2d 2 days 21992555Sdes * 1w 1 week 22092555Sdes * 22192555Sdes * Return -1 if time string is invalid. 22292555Sdes */ 22392555Sdeslong 22492555Sdesconvtime(const char *s) 22592555Sdes{ 22692555Sdes long total, secs; 22792555Sdes const char *p; 22892555Sdes char *endp; 22992555Sdes 23092555Sdes errno = 0; 23192555Sdes total = 0; 23292555Sdes p = s; 23392555Sdes 23492555Sdes if (p == NULL || *p == '\0') 23592555Sdes return -1; 23692555Sdes 23792555Sdes while (*p) { 23892555Sdes secs = strtol(p, &endp, 10); 23992555Sdes if (p == endp || 24092555Sdes (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 24192555Sdes secs < 0) 24292555Sdes return -1; 24392555Sdes 24492555Sdes switch (*endp++) { 24592555Sdes case '\0': 24692555Sdes endp--; 24792555Sdes case 's': 24892555Sdes case 'S': 24992555Sdes break; 25092555Sdes case 'm': 25192555Sdes case 'M': 25292555Sdes secs *= MINUTES; 25392555Sdes break; 25492555Sdes case 'h': 25592555Sdes case 'H': 25692555Sdes secs *= HOURS; 25792555Sdes break; 25892555Sdes case 'd': 25992555Sdes case 'D': 26092555Sdes secs *= DAYS; 26192555Sdes break; 26292555Sdes case 'w': 26392555Sdes case 'W': 26492555Sdes secs *= WEEKS; 26592555Sdes break; 26692555Sdes default: 26792555Sdes return -1; 26892555Sdes } 26992555Sdes total += secs; 27092555Sdes if (total < 0) 27192555Sdes return -1; 27292555Sdes p = endp; 27392555Sdes } 27492555Sdes 27592555Sdes return total; 27692555Sdes} 27792555Sdes 278146998Sdes/* 279146998Sdes * Search for next delimiter between hostnames/addresses and ports. 280146998Sdes * Argument may be modified (for termination). 281146998Sdes * Returns *cp if parsing succeeds. 282146998Sdes * *cp is set to the start of the next delimiter, if one was found. 283146998Sdes * If this is the last field, *cp is set to NULL. 284146998Sdes */ 28592555Sdeschar * 286146998Sdeshpdelim(char **cp) 287146998Sdes{ 288146998Sdes char *s, *old; 289146998Sdes 290146998Sdes if (cp == NULL || *cp == NULL) 291146998Sdes return NULL; 292146998Sdes 293146998Sdes old = s = *cp; 294146998Sdes if (*s == '[') { 295146998Sdes if ((s = strchr(s, ']')) == NULL) 296146998Sdes return NULL; 297146998Sdes else 298146998Sdes s++; 299146998Sdes } else if ((s = strpbrk(s, ":/")) == NULL) 300146998Sdes s = *cp + strlen(*cp); /* skip to end (see first case below) */ 301146998Sdes 302146998Sdes switch (*s) { 303146998Sdes case '\0': 304146998Sdes *cp = NULL; /* no more fields*/ 305146998Sdes break; 306146998Sdes 307146998Sdes case ':': 308146998Sdes case '/': 309146998Sdes *s = '\0'; /* terminate */ 310146998Sdes *cp = s + 1; 311146998Sdes break; 312146998Sdes 313146998Sdes default: 314146998Sdes return NULL; 315146998Sdes } 316146998Sdes 317146998Sdes return old; 318146998Sdes} 319146998Sdes 320146998Sdeschar * 32192555Sdescleanhostname(char *host) 32292555Sdes{ 32392555Sdes if (*host == '[' && host[strlen(host) - 1] == ']') { 32492555Sdes host[strlen(host) - 1] = '\0'; 32592555Sdes return (host + 1); 32692555Sdes } else 32792555Sdes return host; 32892555Sdes} 32992555Sdes 33092555Sdeschar * 33192555Sdescolon(char *cp) 33292555Sdes{ 33392555Sdes int flag = 0; 33492555Sdes 33592555Sdes if (*cp == ':') /* Leading colon is part of file name. */ 33692555Sdes return (0); 33792555Sdes if (*cp == '[') 33892555Sdes flag = 1; 33992555Sdes 34092555Sdes for (; *cp; ++cp) { 34192555Sdes if (*cp == '@' && *(cp+1) == '[') 34292555Sdes flag = 1; 34392555Sdes if (*cp == ']' && *(cp+1) == ':' && flag) 34492555Sdes return (cp+1); 34592555Sdes if (*cp == ':' && !flag) 34692555Sdes return (cp); 34792555Sdes if (*cp == '/') 34892555Sdes return (0); 34992555Sdes } 35092555Sdes return (0); 35192555Sdes} 35292555Sdes 35392555Sdes/* function to assist building execv() arguments */ 35492555Sdesvoid 35592555Sdesaddargs(arglist *args, char *fmt, ...) 35692555Sdes{ 35792555Sdes va_list ap; 35892555Sdes char buf[1024]; 359137015Sdes u_int nalloc; 36092555Sdes 36192555Sdes va_start(ap, fmt); 36292555Sdes vsnprintf(buf, sizeof(buf), fmt, ap); 36392555Sdes va_end(ap); 36492555Sdes 365120161Snectar nalloc = args->nalloc; 36692555Sdes if (args->list == NULL) { 367120161Snectar nalloc = 32; 36892555Sdes args->num = 0; 369120161Snectar } else if (args->num+2 >= nalloc) 370120161Snectar nalloc *= 2; 37192555Sdes 372120161Snectar args->list = xrealloc(args->list, nalloc * sizeof(char *)); 373120161Snectar args->nalloc = nalloc; 37492555Sdes args->list[args->num++] = xstrdup(buf); 37592555Sdes args->list[args->num] = NULL; 37692555Sdes} 377146998Sdes 378146998Sdes/* 379146998Sdes * Read an entire line from a public key file into a static buffer, discarding 380146998Sdes * lines that exceed the buffer size. Returns 0 on success, -1 on failure. 381146998Sdes */ 382146998Sdesint 383146998Sdesread_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, 384146998Sdes u_long *lineno) 385146998Sdes{ 386146998Sdes while (fgets(buf, bufsz, f) != NULL) { 387146998Sdes (*lineno)++; 388146998Sdes if (buf[strlen(buf) - 1] == '\n' || feof(f)) { 389146998Sdes return 0; 390146998Sdes } else { 391146998Sdes debug("%s: %s line %lu exceeds size limit", __func__, 392146998Sdes filename, *lineno); 393146998Sdes /* discard remainder of line */ 394146998Sdes while(fgetc(f) != '\n' && !feof(f)) 395146998Sdes ; /* nothing */ 396146998Sdes } 397146998Sdes } 398146998Sdes return -1; 399146998Sdes} 400