1323136Sdes/* $OpenBSD: misc.c,v 1.109 2017/03/14 00:55:37 dtucker Exp $ */ 276259Sgreen/* 376259Sgreen * Copyright (c) 2000 Markus Friedl. All rights reserved. 4162852Sdes * Copyright (c) 2005,2006 Damien Miller. All rights reserved. 576259Sgreen * 676259Sgreen * Redistribution and use in source and binary forms, with or without 776259Sgreen * modification, are permitted provided that the following conditions 876259Sgreen * are met: 976259Sgreen * 1. Redistributions of source code must retain the above copyright 1076259Sgreen * notice, this list of conditions and the following disclaimer. 1176259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1276259Sgreen * notice, this list of conditions and the following disclaimer in the 1376259Sgreen * documentation and/or other materials provided with the distribution. 1476259Sgreen * 1576259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1676259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1776259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1876259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1976259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2076259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2176259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2276259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2376259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2476259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2576259Sgreen */ 2676259Sgreen 2776259Sgreen#include "includes.h" 2876259Sgreen 29162852Sdes#include <sys/types.h> 30162852Sdes#include <sys/ioctl.h> 31162852Sdes#include <sys/socket.h> 32323134Sdes#include <sys/sysctl.h> 33296633Sdes#include <sys/time.h> 34294328Sdes#include <sys/un.h> 35162852Sdes 36294332Sdes#include <limits.h> 37162852Sdes#include <stdarg.h> 38162852Sdes#include <stdio.h> 39162852Sdes#include <stdlib.h> 40162852Sdes#include <string.h> 41221420Sdes#include <time.h> 42162852Sdes#include <unistd.h> 43162852Sdes 44162852Sdes#include <netinet/in.h> 45221420Sdes#include <netinet/in_systm.h> 46221420Sdes#include <netinet/ip.h> 47162852Sdes#include <netinet/tcp.h> 48162852Sdes 49261320Sdes#include <ctype.h> 50162852Sdes#include <errno.h> 51162852Sdes#include <fcntl.h> 52181111Sdes#include <netdb.h> 53162852Sdes#ifdef HAVE_PATHS_H 54162852Sdes# include <paths.h> 55162852Sdes#include <pwd.h> 56162852Sdes#endif 57157016Sdes#ifdef SSH_TUN_OPENBSD 58157016Sdes#include <net/if.h> 59157016Sdes#endif 60157016Sdes 61162852Sdes#include "xmalloc.h" 6276259Sgreen#include "misc.h" 6376259Sgreen#include "log.h" 64162852Sdes#include "ssh.h" 6576259Sgreen 6692555Sdes/* remove newline at end of string */ 6776259Sgreenchar * 6876259Sgreenchop(char *s) 6976259Sgreen{ 7076259Sgreen char *t = s; 7176259Sgreen while (*t) { 7292555Sdes if (*t == '\n' || *t == '\r') { 7376259Sgreen *t = '\0'; 7476259Sgreen return s; 7576259Sgreen } 7676259Sgreen t++; 7776259Sgreen } 7876259Sgreen return s; 7976259Sgreen 8076259Sgreen} 8176259Sgreen 8292555Sdes/* set/unset filedescriptor to non-blocking */ 83137015Sdesint 8476259Sgreenset_nonblock(int fd) 8576259Sgreen{ 8676259Sgreen int val; 8792555Sdes 88323129Sdes val = fcntl(fd, F_GETFL); 8976259Sgreen if (val < 0) { 90323129Sdes error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); 91137015Sdes return (-1); 9276259Sgreen } 9376259Sgreen if (val & O_NONBLOCK) { 94137015Sdes debug3("fd %d is O_NONBLOCK", fd); 95137015Sdes return (0); 9676259Sgreen } 97124208Sdes debug2("fd %d setting O_NONBLOCK", fd); 9876259Sgreen val |= O_NONBLOCK; 99137015Sdes if (fcntl(fd, F_SETFL, val) == -1) { 100137015Sdes debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, 101137015Sdes strerror(errno)); 102137015Sdes return (-1); 103137015Sdes } 104137015Sdes return (0); 10576259Sgreen} 10676259Sgreen 107137015Sdesint 10892555Sdesunset_nonblock(int fd) 10992555Sdes{ 11092555Sdes int val; 11192555Sdes 112323129Sdes val = fcntl(fd, F_GETFL); 11392555Sdes if (val < 0) { 114323129Sdes error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); 115137015Sdes return (-1); 11692555Sdes } 11792555Sdes if (!(val & O_NONBLOCK)) { 118137015Sdes debug3("fd %d is not O_NONBLOCK", fd); 119137015Sdes return (0); 12092555Sdes } 12192555Sdes debug("fd %d clearing O_NONBLOCK", fd); 12292555Sdes val &= ~O_NONBLOCK; 123137015Sdes if (fcntl(fd, F_SETFL, val) == -1) { 124137015Sdes debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", 12592555Sdes fd, strerror(errno)); 126137015Sdes return (-1); 127137015Sdes } 128137015Sdes return (0); 12992555Sdes} 13092555Sdes 131181111Sdesconst char * 132181111Sdesssh_gai_strerror(int gaierr) 133181111Sdes{ 134255767Sdes if (gaierr == EAI_SYSTEM && errno != 0) 135181111Sdes return strerror(errno); 136181111Sdes return gai_strerror(gaierr); 137181111Sdes} 138181111Sdes 13992555Sdes/* disable nagle on socket */ 14092555Sdesvoid 14192555Sdesset_nodelay(int fd) 14292555Sdes{ 14392555Sdes int opt; 14492555Sdes socklen_t optlen; 14592555Sdes 14692555Sdes optlen = sizeof opt; 14792555Sdes if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { 148126274Sdes debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); 14992555Sdes return; 15092555Sdes } 15192555Sdes if (opt == 1) { 15292555Sdes debug2("fd %d is TCP_NODELAY", fd); 15392555Sdes return; 15492555Sdes } 15592555Sdes opt = 1; 156113908Sdes debug2("fd %d setting TCP_NODELAY", fd); 15792555Sdes if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) 15892555Sdes error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); 15992555Sdes} 16092555Sdes 16176259Sgreen/* Characters considered whitespace in strsep calls. */ 16276259Sgreen#define WHITESPACE " \t\r\n" 163162852Sdes#define QUOTE "\"" 16476259Sgreen 16592555Sdes/* return next token in configuration line */ 16676259Sgreenchar * 16776259Sgreenstrdelim(char **s) 16876259Sgreen{ 16976259Sgreen char *old; 17076259Sgreen int wspace = 0; 17176259Sgreen 17276259Sgreen if (*s == NULL) 17376259Sgreen return NULL; 17476259Sgreen 17576259Sgreen old = *s; 17676259Sgreen 177162852Sdes *s = strpbrk(*s, WHITESPACE QUOTE "="); 17876259Sgreen if (*s == NULL) 17976259Sgreen return (old); 18076259Sgreen 181162852Sdes if (*s[0] == '\"') { 182162852Sdes memmove(*s, *s + 1, strlen(*s)); /* move nul too */ 183162852Sdes /* Find matching quote */ 184162852Sdes if ((*s = strpbrk(*s, QUOTE)) == NULL) { 185162852Sdes return (NULL); /* no matching quote */ 186162852Sdes } else { 187162852Sdes *s[0] = '\0'; 188215116Sdes *s += strspn(*s + 1, WHITESPACE) + 1; 189162852Sdes return (old); 190162852Sdes } 191162852Sdes } 192162852Sdes 19376259Sgreen /* Allow only one '=' to be skipped */ 19476259Sgreen if (*s[0] == '=') 19576259Sgreen wspace = 1; 19676259Sgreen *s[0] = '\0'; 19776259Sgreen 198162852Sdes /* Skip any extra whitespace after first token */ 19976259Sgreen *s += strspn(*s + 1, WHITESPACE) + 1; 20076259Sgreen if (*s[0] == '=' && !wspace) 20176259Sgreen *s += strspn(*s + 1, WHITESPACE) + 1; 20276259Sgreen 20376259Sgreen return (old); 20476259Sgreen} 20576259Sgreen 20676259Sgreenstruct passwd * 20776259Sgreenpwcopy(struct passwd *pw) 20876259Sgreen{ 209162852Sdes struct passwd *copy = xcalloc(1, sizeof(*copy)); 21076259Sgreen 21176259Sgreen copy->pw_name = xstrdup(pw->pw_name); 21276259Sgreen copy->pw_passwd = xstrdup(pw->pw_passwd); 213255767Sdes#ifdef HAVE_STRUCT_PASSWD_PW_GECOS 21476259Sgreen copy->pw_gecos = xstrdup(pw->pw_gecos); 215255767Sdes#endif 21676259Sgreen copy->pw_uid = pw->pw_uid; 21776259Sgreen copy->pw_gid = pw->pw_gid; 218255767Sdes#ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE 21992555Sdes copy->pw_expire = pw->pw_expire; 22098937Sdes#endif 221255767Sdes#ifdef HAVE_STRUCT_PASSWD_PW_CHANGE 22292555Sdes copy->pw_change = pw->pw_change; 22398937Sdes#endif 224255767Sdes#ifdef HAVE_STRUCT_PASSWD_PW_CLASS 22576259Sgreen copy->pw_class = xstrdup(pw->pw_class); 22698937Sdes#endif 22776259Sgreen copy->pw_dir = xstrdup(pw->pw_dir); 22876259Sgreen copy->pw_shell = xstrdup(pw->pw_shell); 22976259Sgreen return copy; 23076259Sgreen} 23176259Sgreen 23292555Sdes/* 23392555Sdes * Convert ASCII string to TCP/IP port number. 234192595Sdes * Port must be >=0 and <=65535. 235192595Sdes * Return -1 if invalid. 23692555Sdes */ 23792555Sdesint 23892555Sdesa2port(const char *s) 23976259Sgreen{ 240192595Sdes long long port; 241192595Sdes const char *errstr; 24276259Sgreen 243192595Sdes port = strtonum(s, 0, 65535, &errstr); 244192595Sdes if (errstr != NULL) 245192595Sdes return -1; 246192595Sdes return (int)port; 24776259Sgreen} 24892555Sdes 249157016Sdesint 250157016Sdesa2tun(const char *s, int *remote) 251157016Sdes{ 252157016Sdes const char *errstr = NULL; 253157016Sdes char *sp, *ep; 254157016Sdes int tun; 255157016Sdes 256157016Sdes if (remote != NULL) { 257157016Sdes *remote = SSH_TUNID_ANY; 258157016Sdes sp = xstrdup(s); 259157016Sdes if ((ep = strchr(sp, ':')) == NULL) { 260255767Sdes free(sp); 261157016Sdes return (a2tun(s, NULL)); 262157016Sdes } 263157016Sdes ep[0] = '\0'; ep++; 264157016Sdes *remote = a2tun(ep, NULL); 265157016Sdes tun = a2tun(sp, NULL); 266255767Sdes free(sp); 267157016Sdes return (*remote == SSH_TUNID_ERR ? *remote : tun); 268157016Sdes } 269157016Sdes 270157016Sdes if (strcasecmp(s, "any") == 0) 271157016Sdes return (SSH_TUNID_ANY); 272157016Sdes 273157016Sdes tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); 274157016Sdes if (errstr != NULL) 275157016Sdes return (SSH_TUNID_ERR); 276157016Sdes 277157016Sdes return (tun); 278157016Sdes} 279157016Sdes 28092555Sdes#define SECONDS 1 28192555Sdes#define MINUTES (SECONDS * 60) 28292555Sdes#define HOURS (MINUTES * 60) 28392555Sdes#define DAYS (HOURS * 24) 28492555Sdes#define WEEKS (DAYS * 7) 28592555Sdes 28692555Sdes/* 28792555Sdes * Convert a time string into seconds; format is 28892555Sdes * a sequence of: 28992555Sdes * time[qualifier] 29092555Sdes * 29192555Sdes * Valid time qualifiers are: 29292555Sdes * <none> seconds 29392555Sdes * s|S seconds 29492555Sdes * m|M minutes 29592555Sdes * h|H hours 29692555Sdes * d|D days 29792555Sdes * w|W weeks 29892555Sdes * 29992555Sdes * Examples: 30092555Sdes * 90m 90 minutes 30192555Sdes * 1h30m 90 minutes 30292555Sdes * 2d 2 days 30392555Sdes * 1w 1 week 30492555Sdes * 30592555Sdes * Return -1 if time string is invalid. 30692555Sdes */ 30792555Sdeslong 30892555Sdesconvtime(const char *s) 30992555Sdes{ 310323136Sdes long total, secs, multiplier = 1; 31192555Sdes const char *p; 31292555Sdes char *endp; 31392555Sdes 31492555Sdes errno = 0; 31592555Sdes total = 0; 31692555Sdes p = s; 31792555Sdes 31892555Sdes if (p == NULL || *p == '\0') 31992555Sdes return -1; 32092555Sdes 32192555Sdes while (*p) { 32292555Sdes secs = strtol(p, &endp, 10); 32392555Sdes if (p == endp || 32492555Sdes (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || 32592555Sdes secs < 0) 32692555Sdes return -1; 32792555Sdes 32892555Sdes switch (*endp++) { 32992555Sdes case '\0': 33092555Sdes endp--; 331162852Sdes break; 33292555Sdes case 's': 33392555Sdes case 'S': 33492555Sdes break; 33592555Sdes case 'm': 33692555Sdes case 'M': 337323136Sdes multiplier = MINUTES; 33892555Sdes break; 33992555Sdes case 'h': 34092555Sdes case 'H': 341323136Sdes multiplier = HOURS; 34292555Sdes break; 34392555Sdes case 'd': 34492555Sdes case 'D': 345323136Sdes multiplier = DAYS; 34692555Sdes break; 34792555Sdes case 'w': 34892555Sdes case 'W': 349323136Sdes multiplier = WEEKS; 35092555Sdes break; 35192555Sdes default: 35292555Sdes return -1; 35392555Sdes } 354323136Sdes if (secs >= LONG_MAX / multiplier) 355323136Sdes return -1; 356323136Sdes secs *= multiplier; 357323136Sdes if (total >= LONG_MAX - secs) 358323136Sdes return -1; 35992555Sdes total += secs; 36092555Sdes if (total < 0) 36192555Sdes return -1; 36292555Sdes p = endp; 36392555Sdes } 36492555Sdes 36592555Sdes return total; 36692555Sdes} 36792555Sdes 368146998Sdes/* 369162852Sdes * Returns a standardized host+port identifier string. 370162852Sdes * Caller must free returned string. 371162852Sdes */ 372162852Sdeschar * 373162852Sdesput_host_port(const char *host, u_short port) 374162852Sdes{ 375162852Sdes char *hoststr; 376162852Sdes 377162852Sdes if (port == 0 || port == SSH_DEFAULT_PORT) 378162852Sdes return(xstrdup(host)); 379162852Sdes if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) 380162852Sdes fatal("put_host_port: asprintf: %s", strerror(errno)); 381162852Sdes debug3("put_host_port: %s", hoststr); 382162852Sdes return hoststr; 383162852Sdes} 384162852Sdes 385162852Sdes/* 386146998Sdes * Search for next delimiter between hostnames/addresses and ports. 387146998Sdes * Argument may be modified (for termination). 388146998Sdes * Returns *cp if parsing succeeds. 389146998Sdes * *cp is set to the start of the next delimiter, if one was found. 390146998Sdes * If this is the last field, *cp is set to NULL. 391146998Sdes */ 39292555Sdeschar * 393146998Sdeshpdelim(char **cp) 394146998Sdes{ 395146998Sdes char *s, *old; 396146998Sdes 397146998Sdes if (cp == NULL || *cp == NULL) 398146998Sdes return NULL; 399146998Sdes 400146998Sdes old = s = *cp; 401146998Sdes if (*s == '[') { 402146998Sdes if ((s = strchr(s, ']')) == NULL) 403146998Sdes return NULL; 404146998Sdes else 405146998Sdes s++; 406146998Sdes } else if ((s = strpbrk(s, ":/")) == NULL) 407146998Sdes s = *cp + strlen(*cp); /* skip to end (see first case below) */ 408146998Sdes 409146998Sdes switch (*s) { 410146998Sdes case '\0': 411146998Sdes *cp = NULL; /* no more fields*/ 412146998Sdes break; 413147001Sdes 414146998Sdes case ':': 415146998Sdes case '/': 416146998Sdes *s = '\0'; /* terminate */ 417146998Sdes *cp = s + 1; 418146998Sdes break; 419147001Sdes 420146998Sdes default: 421146998Sdes return NULL; 422146998Sdes } 423146998Sdes 424146998Sdes return old; 425146998Sdes} 426146998Sdes 427146998Sdeschar * 42892555Sdescleanhostname(char *host) 42992555Sdes{ 43092555Sdes if (*host == '[' && host[strlen(host) - 1] == ']') { 43192555Sdes host[strlen(host) - 1] = '\0'; 43292555Sdes return (host + 1); 43392555Sdes } else 43492555Sdes return host; 43592555Sdes} 43692555Sdes 43792555Sdeschar * 43892555Sdescolon(char *cp) 43992555Sdes{ 44092555Sdes int flag = 0; 44192555Sdes 44292555Sdes if (*cp == ':') /* Leading colon is part of file name. */ 443215116Sdes return NULL; 44492555Sdes if (*cp == '[') 44592555Sdes flag = 1; 44692555Sdes 44792555Sdes for (; *cp; ++cp) { 44892555Sdes if (*cp == '@' && *(cp+1) == '[') 44992555Sdes flag = 1; 45092555Sdes if (*cp == ']' && *(cp+1) == ':' && flag) 45192555Sdes return (cp+1); 45292555Sdes if (*cp == ':' && !flag) 45392555Sdes return (cp); 45492555Sdes if (*cp == '/') 455215116Sdes return NULL; 45692555Sdes } 457215116Sdes return NULL; 45892555Sdes} 45992555Sdes 460323129Sdes/* 461323129Sdes * Parse a [user@]host[:port] string. 462323129Sdes * Caller must free returned user and host. 463323129Sdes * Any of the pointer return arguments may be NULL (useful for syntax checking). 464323129Sdes * If user was not specified then *userp will be set to NULL. 465323129Sdes * If port was not specified then *portp will be -1. 466323129Sdes * Returns 0 on success, -1 on failure. 467323129Sdes */ 468323129Sdesint 469323129Sdesparse_user_host_port(const char *s, char **userp, char **hostp, int *portp) 470323129Sdes{ 471323129Sdes char *sdup, *cp, *tmp; 472323129Sdes char *user = NULL, *host = NULL; 473323129Sdes int port = -1, ret = -1; 474323129Sdes 475323129Sdes if (userp != NULL) 476323129Sdes *userp = NULL; 477323129Sdes if (hostp != NULL) 478323129Sdes *hostp = NULL; 479323129Sdes if (portp != NULL) 480323129Sdes *portp = -1; 481323129Sdes 482323129Sdes if ((sdup = tmp = strdup(s)) == NULL) 483323129Sdes return -1; 484323129Sdes /* Extract optional username */ 485323129Sdes if ((cp = strchr(tmp, '@')) != NULL) { 486323129Sdes *cp = '\0'; 487323129Sdes if (*tmp == '\0') 488323129Sdes goto out; 489323129Sdes if ((user = strdup(tmp)) == NULL) 490323129Sdes goto out; 491323129Sdes tmp = cp + 1; 492323129Sdes } 493323129Sdes /* Extract mandatory hostname */ 494323129Sdes if ((cp = hpdelim(&tmp)) == NULL || *cp == '\0') 495323129Sdes goto out; 496323129Sdes host = xstrdup(cleanhostname(cp)); 497323129Sdes /* Convert and verify optional port */ 498323129Sdes if (tmp != NULL && *tmp != '\0') { 499323129Sdes if ((port = a2port(tmp)) <= 0) 500323129Sdes goto out; 501323129Sdes } 502323129Sdes /* Success */ 503323129Sdes if (userp != NULL) { 504323129Sdes *userp = user; 505323129Sdes user = NULL; 506323129Sdes } 507323129Sdes if (hostp != NULL) { 508323129Sdes *hostp = host; 509323129Sdes host = NULL; 510323129Sdes } 511323129Sdes if (portp != NULL) 512323129Sdes *portp = port; 513323129Sdes ret = 0; 514323129Sdes out: 515323129Sdes free(sdup); 516323129Sdes free(user); 517323129Sdes free(host); 518323129Sdes return ret; 519323129Sdes} 520323129Sdes 52192555Sdes/* function to assist building execv() arguments */ 52292555Sdesvoid 52392555Sdesaddargs(arglist *args, char *fmt, ...) 52492555Sdes{ 52592555Sdes va_list ap; 526157016Sdes char *cp; 527137015Sdes u_int nalloc; 528157016Sdes int r; 52992555Sdes 53092555Sdes va_start(ap, fmt); 531157016Sdes r = vasprintf(&cp, fmt, ap); 53292555Sdes va_end(ap); 533157016Sdes if (r == -1) 534157016Sdes fatal("addargs: argument too long"); 53592555Sdes 536120161Snectar nalloc = args->nalloc; 53792555Sdes if (args->list == NULL) { 538120161Snectar nalloc = 32; 53992555Sdes args->num = 0; 540120161Snectar } else if (args->num+2 >= nalloc) 541120161Snectar nalloc *= 2; 54292555Sdes 543294336Sdes args->list = xreallocarray(args->list, nalloc, sizeof(char *)); 544120161Snectar args->nalloc = nalloc; 545157016Sdes args->list[args->num++] = cp; 54692555Sdes args->list[args->num] = NULL; 54792555Sdes} 548146998Sdes 549157016Sdesvoid 550157016Sdesreplacearg(arglist *args, u_int which, char *fmt, ...) 551157016Sdes{ 552157016Sdes va_list ap; 553157016Sdes char *cp; 554157016Sdes int r; 555157016Sdes 556157016Sdes va_start(ap, fmt); 557157016Sdes r = vasprintf(&cp, fmt, ap); 558157016Sdes va_end(ap); 559157016Sdes if (r == -1) 560157016Sdes fatal("replacearg: argument too long"); 561157016Sdes 562157016Sdes if (which >= args->num) 563157016Sdes fatal("replacearg: tried to replace invalid arg %d >= %d", 564157016Sdes which, args->num); 565255767Sdes free(args->list[which]); 566157016Sdes args->list[which] = cp; 567157016Sdes} 568157016Sdes 569157016Sdesvoid 570157016Sdesfreeargs(arglist *args) 571157016Sdes{ 572157016Sdes u_int i; 573157016Sdes 574157016Sdes if (args->list != NULL) { 575157016Sdes for (i = 0; i < args->num; i++) 576255767Sdes free(args->list[i]); 577255767Sdes free(args->list); 578157016Sdes args->nalloc = args->num = 0; 579157016Sdes args->list = NULL; 580157016Sdes } 581157016Sdes} 582157016Sdes 583146998Sdes/* 584149749Sdes * Expands tildes in the file name. Returns data allocated by xmalloc. 585149749Sdes * Warning: this calls getpw*. 586149749Sdes */ 587149749Sdeschar * 588149749Sdestilde_expand_filename(const char *filename, uid_t uid) 589149749Sdes{ 590255767Sdes const char *path, *sep; 591255767Sdes char user[128], *ret; 592149749Sdes struct passwd *pw; 593149749Sdes u_int len, slash; 594149749Sdes 595149749Sdes if (*filename != '~') 596149749Sdes return (xstrdup(filename)); 597149749Sdes filename++; 598149749Sdes 599149749Sdes path = strchr(filename, '/'); 600149749Sdes if (path != NULL && path > filename) { /* ~user/path */ 601149749Sdes slash = path - filename; 602149749Sdes if (slash > sizeof(user) - 1) 603149749Sdes fatal("tilde_expand_filename: ~username too long"); 604149749Sdes memcpy(user, filename, slash); 605149749Sdes user[slash] = '\0'; 606149749Sdes if ((pw = getpwnam(user)) == NULL) 607149749Sdes fatal("tilde_expand_filename: No such user %s", user); 608149749Sdes } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ 609181111Sdes fatal("tilde_expand_filename: No such uid %ld", (long)uid); 610149749Sdes 611149749Sdes /* Make sure directory has a trailing '/' */ 612149749Sdes len = strlen(pw->pw_dir); 613255767Sdes if (len == 0 || pw->pw_dir[len - 1] != '/') 614255767Sdes sep = "/"; 615255767Sdes else 616255767Sdes sep = ""; 617149749Sdes 618149749Sdes /* Skip leading '/' from specified path */ 619149749Sdes if (path != NULL) 620149749Sdes filename = path + 1; 621255767Sdes 622294332Sdes if (xasprintf(&ret, "%s%s%s", pw->pw_dir, sep, filename) >= PATH_MAX) 623149749Sdes fatal("tilde_expand_filename: Path too long"); 624149749Sdes 625255767Sdes return (ret); 626149749Sdes} 627149749Sdes 628149749Sdes/* 629149749Sdes * Expand a string with a set of %[char] escapes. A number of escapes may be 630149749Sdes * specified as (char *escape_chars, char *replacement) pairs. The list must 631149749Sdes * be terminated by a NULL escape_char. Returns replaced string in memory 632149749Sdes * allocated by xmalloc. 633149749Sdes */ 634149749Sdeschar * 635149749Sdespercent_expand(const char *string, ...) 636149749Sdes{ 637149749Sdes#define EXPAND_MAX_KEYS 16 638204917Sdes u_int num_keys, i, j; 639149749Sdes struct { 640149749Sdes const char *key; 641149749Sdes const char *repl; 642149749Sdes } keys[EXPAND_MAX_KEYS]; 643149749Sdes char buf[4096]; 644149749Sdes va_list ap; 645149749Sdes 646149749Sdes /* Gather keys */ 647149749Sdes va_start(ap, string); 648149749Sdes for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { 649149749Sdes keys[num_keys].key = va_arg(ap, char *); 650149749Sdes if (keys[num_keys].key == NULL) 651149749Sdes break; 652149749Sdes keys[num_keys].repl = va_arg(ap, char *); 653149749Sdes if (keys[num_keys].repl == NULL) 654204917Sdes fatal("%s: NULL replacement", __func__); 655149749Sdes } 656204917Sdes if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) 657204917Sdes fatal("%s: too many keys", __func__); 658149749Sdes va_end(ap); 659149749Sdes 660149749Sdes /* Expand string */ 661149749Sdes *buf = '\0'; 662149749Sdes for (i = 0; *string != '\0'; string++) { 663149749Sdes if (*string != '%') { 664149749Sdes append: 665149749Sdes buf[i++] = *string; 666149749Sdes if (i >= sizeof(buf)) 667204917Sdes fatal("%s: string too long", __func__); 668149749Sdes buf[i] = '\0'; 669149749Sdes continue; 670149749Sdes } 671149749Sdes string++; 672204917Sdes /* %% case */ 673149749Sdes if (*string == '%') 674149749Sdes goto append; 675296633Sdes if (*string == '\0') 676296633Sdes fatal("%s: invalid format", __func__); 677149749Sdes for (j = 0; j < num_keys; j++) { 678149749Sdes if (strchr(keys[j].key, *string) != NULL) { 679149749Sdes i = strlcat(buf, keys[j].repl, sizeof(buf)); 680149749Sdes if (i >= sizeof(buf)) 681204917Sdes fatal("%s: string too long", __func__); 682149749Sdes break; 683149749Sdes } 684149749Sdes } 685149749Sdes if (j >= num_keys) 686204917Sdes fatal("%s: unknown key %%%c", __func__, *string); 687149749Sdes } 688149749Sdes return (xstrdup(buf)); 689149749Sdes#undef EXPAND_MAX_KEYS 690149749Sdes} 691149749Sdes 692149749Sdes/* 693146998Sdes * Read an entire line from a public key file into a static buffer, discarding 694146998Sdes * lines that exceed the buffer size. Returns 0 on success, -1 on failure. 695146998Sdes */ 696146998Sdesint 697146998Sdesread_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, 698146998Sdes u_long *lineno) 699146998Sdes{ 700146998Sdes while (fgets(buf, bufsz, f) != NULL) { 701181111Sdes if (buf[0] == '\0') 702181111Sdes continue; 703146998Sdes (*lineno)++; 704146998Sdes if (buf[strlen(buf) - 1] == '\n' || feof(f)) { 705146998Sdes return 0; 706146998Sdes } else { 707146998Sdes debug("%s: %s line %lu exceeds size limit", __func__, 708146998Sdes filename, *lineno); 709146998Sdes /* discard remainder of line */ 710147001Sdes while (fgetc(f) != '\n' && !feof(f)) 711146998Sdes ; /* nothing */ 712146998Sdes } 713146998Sdes } 714146998Sdes return -1; 715146998Sdes} 716149749Sdes 717157016Sdesint 718157016Sdestun_open(int tun, int mode) 719157016Sdes{ 720157016Sdes#if defined(CUSTOM_SYS_TUN_OPEN) 721157016Sdes return (sys_tun_open(tun, mode)); 722157016Sdes#elif defined(SSH_TUN_OPENBSD) 723157016Sdes struct ifreq ifr; 724157016Sdes char name[100]; 725157016Sdes int fd = -1, sock; 726296633Sdes const char *tunbase = "tun"; 727157016Sdes 728296633Sdes if (mode == SSH_TUNMODE_ETHERNET) 729296633Sdes tunbase = "tap"; 730296633Sdes 731157016Sdes /* Open the tunnel device */ 732157016Sdes if (tun <= SSH_TUNID_MAX) { 733296633Sdes snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); 734157016Sdes fd = open(name, O_RDWR); 735157016Sdes } else if (tun == SSH_TUNID_ANY) { 736157016Sdes for (tun = 100; tun >= 0; tun--) { 737296633Sdes snprintf(name, sizeof(name), "/dev/%s%d", 738296633Sdes tunbase, tun); 739157016Sdes if ((fd = open(name, O_RDWR)) >= 0) 740157016Sdes break; 741157016Sdes } 742157016Sdes } else { 743157016Sdes debug("%s: invalid tunnel %u", __func__, tun); 744296633Sdes return -1; 745157016Sdes } 746157016Sdes 747157016Sdes if (fd < 0) { 748296633Sdes debug("%s: %s open: %s", __func__, name, strerror(errno)); 749296633Sdes return -1; 750157016Sdes } 751157016Sdes 752157016Sdes debug("%s: %s mode %d fd %d", __func__, name, mode, fd); 753157016Sdes 754296633Sdes /* Bring interface up if it is not already */ 755296633Sdes snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); 756157016Sdes if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 757157016Sdes goto failed; 758157016Sdes 759296633Sdes if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) { 760296633Sdes debug("%s: get interface %s flags: %s", __func__, 761296633Sdes ifr.ifr_name, strerror(errno)); 762157016Sdes goto failed; 763296633Sdes } 764157016Sdes 765296633Sdes if (!(ifr.ifr_flags & IFF_UP)) { 766296633Sdes ifr.ifr_flags |= IFF_UP; 767296633Sdes if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) { 768296633Sdes debug("%s: activate interface %s: %s", __func__, 769296633Sdes ifr.ifr_name, strerror(errno)); 770296633Sdes goto failed; 771296633Sdes } 772296633Sdes } 773157016Sdes 774157016Sdes close(sock); 775296633Sdes return fd; 776157016Sdes 777157016Sdes failed: 778157016Sdes if (fd >= 0) 779157016Sdes close(fd); 780157016Sdes if (sock >= 0) 781157016Sdes close(sock); 782296633Sdes return -1; 783157016Sdes#else 784157016Sdes error("Tunnel interfaces are not supported on this platform"); 785157016Sdes return (-1); 786157016Sdes#endif 787157016Sdes} 788157016Sdes 789157016Sdesvoid 790157016Sdessanitise_stdfd(void) 791157016Sdes{ 792157016Sdes int nullfd, dupfd; 793157016Sdes 794157016Sdes if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { 795192595Sdes fprintf(stderr, "Couldn't open /dev/null: %s\n", 796192595Sdes strerror(errno)); 797157016Sdes exit(1); 798157016Sdes } 799323129Sdes while (++dupfd <= STDERR_FILENO) { 800323129Sdes /* Only populate closed fds. */ 801323129Sdes if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) { 802323129Sdes if (dup2(nullfd, dupfd) == -1) { 803323129Sdes fprintf(stderr, "dup2: %s\n", strerror(errno)); 804323129Sdes exit(1); 805323129Sdes } 806157016Sdes } 807157016Sdes } 808323129Sdes if (nullfd > STDERR_FILENO) 809157016Sdes close(nullfd); 810157016Sdes} 811157016Sdes 812149749Sdeschar * 813162852Sdestohex(const void *vp, size_t l) 814149749Sdes{ 815162852Sdes const u_char *p = (const u_char *)vp; 816149749Sdes char b[3], *r; 817162852Sdes size_t i, hl; 818149749Sdes 819162852Sdes if (l > 65536) 820162852Sdes return xstrdup("tohex: length > 65536"); 821162852Sdes 822149749Sdes hl = l * 2 + 1; 823162852Sdes r = xcalloc(1, hl); 824149749Sdes for (i = 0; i < l; i++) { 825162852Sdes snprintf(b, sizeof(b), "%02x", p[i]); 826149749Sdes strlcat(r, b, hl); 827149749Sdes } 828149749Sdes return (r); 829149749Sdes} 830149749Sdes 831162852Sdesu_int64_t 832162852Sdesget_u64(const void *vp) 833162852Sdes{ 834162852Sdes const u_char *p = (const u_char *)vp; 835162852Sdes u_int64_t v; 836162852Sdes 837162852Sdes v = (u_int64_t)p[0] << 56; 838162852Sdes v |= (u_int64_t)p[1] << 48; 839162852Sdes v |= (u_int64_t)p[2] << 40; 840162852Sdes v |= (u_int64_t)p[3] << 32; 841162852Sdes v |= (u_int64_t)p[4] << 24; 842162852Sdes v |= (u_int64_t)p[5] << 16; 843162852Sdes v |= (u_int64_t)p[6] << 8; 844162852Sdes v |= (u_int64_t)p[7]; 845162852Sdes 846162852Sdes return (v); 847162852Sdes} 848162852Sdes 849162852Sdesu_int32_t 850162852Sdesget_u32(const void *vp) 851162852Sdes{ 852162852Sdes const u_char *p = (const u_char *)vp; 853162852Sdes u_int32_t v; 854162852Sdes 855162852Sdes v = (u_int32_t)p[0] << 24; 856162852Sdes v |= (u_int32_t)p[1] << 16; 857162852Sdes v |= (u_int32_t)p[2] << 8; 858162852Sdes v |= (u_int32_t)p[3]; 859162852Sdes 860162852Sdes return (v); 861162852Sdes} 862162852Sdes 863294328Sdesu_int32_t 864294328Sdesget_u32_le(const void *vp) 865294328Sdes{ 866294328Sdes const u_char *p = (const u_char *)vp; 867294328Sdes u_int32_t v; 868294328Sdes 869294328Sdes v = (u_int32_t)p[0]; 870294328Sdes v |= (u_int32_t)p[1] << 8; 871294328Sdes v |= (u_int32_t)p[2] << 16; 872294328Sdes v |= (u_int32_t)p[3] << 24; 873294328Sdes 874294328Sdes return (v); 875294328Sdes} 876294328Sdes 877162852Sdesu_int16_t 878162852Sdesget_u16(const void *vp) 879162852Sdes{ 880162852Sdes const u_char *p = (const u_char *)vp; 881162852Sdes u_int16_t v; 882162852Sdes 883162852Sdes v = (u_int16_t)p[0] << 8; 884162852Sdes v |= (u_int16_t)p[1]; 885162852Sdes 886162852Sdes return (v); 887162852Sdes} 888162852Sdes 889162852Sdesvoid 890162852Sdesput_u64(void *vp, u_int64_t v) 891162852Sdes{ 892162852Sdes u_char *p = (u_char *)vp; 893162852Sdes 894162852Sdes p[0] = (u_char)(v >> 56) & 0xff; 895162852Sdes p[1] = (u_char)(v >> 48) & 0xff; 896162852Sdes p[2] = (u_char)(v >> 40) & 0xff; 897162852Sdes p[3] = (u_char)(v >> 32) & 0xff; 898162852Sdes p[4] = (u_char)(v >> 24) & 0xff; 899162852Sdes p[5] = (u_char)(v >> 16) & 0xff; 900162852Sdes p[6] = (u_char)(v >> 8) & 0xff; 901162852Sdes p[7] = (u_char)v & 0xff; 902162852Sdes} 903162852Sdes 904162852Sdesvoid 905162852Sdesput_u32(void *vp, u_int32_t v) 906162852Sdes{ 907162852Sdes u_char *p = (u_char *)vp; 908162852Sdes 909162852Sdes p[0] = (u_char)(v >> 24) & 0xff; 910162852Sdes p[1] = (u_char)(v >> 16) & 0xff; 911162852Sdes p[2] = (u_char)(v >> 8) & 0xff; 912162852Sdes p[3] = (u_char)v & 0xff; 913162852Sdes} 914162852Sdes 915294328Sdesvoid 916294328Sdesput_u32_le(void *vp, u_int32_t v) 917294328Sdes{ 918294328Sdes u_char *p = (u_char *)vp; 919162852Sdes 920294328Sdes p[0] = (u_char)v & 0xff; 921294328Sdes p[1] = (u_char)(v >> 8) & 0xff; 922294328Sdes p[2] = (u_char)(v >> 16) & 0xff; 923294328Sdes p[3] = (u_char)(v >> 24) & 0xff; 924294328Sdes} 925294328Sdes 926162852Sdesvoid 927162852Sdesput_u16(void *vp, u_int16_t v) 928162852Sdes{ 929162852Sdes u_char *p = (u_char *)vp; 930162852Sdes 931162852Sdes p[0] = (u_char)(v >> 8) & 0xff; 932162852Sdes p[1] = (u_char)v & 0xff; 933162852Sdes} 934181111Sdes 935181111Sdesvoid 936181111Sdesms_subtract_diff(struct timeval *start, int *ms) 937181111Sdes{ 938181111Sdes struct timeval diff, finish; 939181111Sdes 940181111Sdes gettimeofday(&finish, NULL); 941181111Sdes timersub(&finish, start, &diff); 942181111Sdes *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); 943181111Sdes} 944181111Sdes 945181111Sdesvoid 946181111Sdesms_to_timeval(struct timeval *tv, int ms) 947181111Sdes{ 948181111Sdes if (ms < 0) 949181111Sdes ms = 0; 950181111Sdes tv->tv_sec = ms / 1000; 951181111Sdes tv->tv_usec = (ms % 1000) * 1000; 952181111Sdes} 953181111Sdes 954255767Sdestime_t 955255767Sdesmonotime(void) 956255767Sdes{ 957294328Sdes#if defined(HAVE_CLOCK_GETTIME) && \ 958294328Sdes (defined(CLOCK_MONOTONIC) || defined(CLOCK_BOOTTIME)) 959255767Sdes struct timespec ts; 960255767Sdes static int gettime_failed = 0; 961255767Sdes 962255767Sdes if (!gettime_failed) { 963294328Sdes#if defined(CLOCK_BOOTTIME) 964294328Sdes if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) 965294328Sdes return (ts.tv_sec); 966294328Sdes#endif 967294328Sdes#if defined(CLOCK_MONOTONIC) 968255767Sdes if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) 969255767Sdes return (ts.tv_sec); 970294328Sdes#endif 971255767Sdes debug3("clock_gettime: %s", strerror(errno)); 972255767Sdes gettime_failed = 1; 973255767Sdes } 974294328Sdes#endif /* HAVE_CLOCK_GETTIME && (CLOCK_MONOTONIC || CLOCK_BOOTTIME */ 975255767Sdes 976255767Sdes return time(NULL); 977255767Sdes} 978255767Sdes 979323129Sdesdouble 980323129Sdesmonotime_double(void) 981323129Sdes{ 982323129Sdes#if defined(HAVE_CLOCK_GETTIME) && \ 983323129Sdes (defined(CLOCK_MONOTONIC) || defined(CLOCK_BOOTTIME)) 984323129Sdes struct timespec ts; 985323129Sdes static int gettime_failed = 0; 986323129Sdes 987323129Sdes if (!gettime_failed) { 988323129Sdes#if defined(CLOCK_BOOTTIME) 989323129Sdes if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) 990323129Sdes return (ts.tv_sec + (double)ts.tv_nsec / 1000000000); 991323129Sdes#endif 992323129Sdes#if defined(CLOCK_MONOTONIC) 993323129Sdes if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) 994323129Sdes return (ts.tv_sec + (double)ts.tv_nsec / 1000000000); 995323129Sdes#endif 996323129Sdes debug3("clock_gettime: %s", strerror(errno)); 997323129Sdes gettime_failed = 1; 998323129Sdes } 999323129Sdes#endif /* HAVE_CLOCK_GETTIME && (CLOCK_MONOTONIC || CLOCK_BOOTTIME */ 1000323129Sdes 1001323129Sdes return (double)time(NULL); 1002323129Sdes} 1003323129Sdes 1004221420Sdesvoid 1005221420Sdesbandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen) 1006221420Sdes{ 1007221420Sdes bw->buflen = buflen; 1008221420Sdes bw->rate = kbps; 1009221420Sdes bw->thresh = bw->rate; 1010221420Sdes bw->lamt = 0; 1011221420Sdes timerclear(&bw->bwstart); 1012221420Sdes timerclear(&bw->bwend); 1013221420Sdes} 1014221420Sdes 1015221420Sdes/* Callback from read/write loop to insert bandwidth-limiting delays */ 1016221420Sdesvoid 1017221420Sdesbandwidth_limit(struct bwlimit *bw, size_t read_len) 1018221420Sdes{ 1019221420Sdes u_int64_t waitlen; 1020221420Sdes struct timespec ts, rm; 1021221420Sdes 1022221420Sdes if (!timerisset(&bw->bwstart)) { 1023221420Sdes gettimeofday(&bw->bwstart, NULL); 1024221420Sdes return; 1025221420Sdes } 1026221420Sdes 1027221420Sdes bw->lamt += read_len; 1028221420Sdes if (bw->lamt < bw->thresh) 1029221420Sdes return; 1030221420Sdes 1031221420Sdes gettimeofday(&bw->bwend, NULL); 1032221420Sdes timersub(&bw->bwend, &bw->bwstart, &bw->bwend); 1033221420Sdes if (!timerisset(&bw->bwend)) 1034221420Sdes return; 1035221420Sdes 1036221420Sdes bw->lamt *= 8; 1037221420Sdes waitlen = (double)1000000L * bw->lamt / bw->rate; 1038221420Sdes 1039221420Sdes bw->bwstart.tv_sec = waitlen / 1000000L; 1040221420Sdes bw->bwstart.tv_usec = waitlen % 1000000L; 1041221420Sdes 1042221420Sdes if (timercmp(&bw->bwstart, &bw->bwend, >)) { 1043221420Sdes timersub(&bw->bwstart, &bw->bwend, &bw->bwend); 1044221420Sdes 1045221420Sdes /* Adjust the wait time */ 1046221420Sdes if (bw->bwend.tv_sec) { 1047221420Sdes bw->thresh /= 2; 1048221420Sdes if (bw->thresh < bw->buflen / 4) 1049221420Sdes bw->thresh = bw->buflen / 4; 1050221420Sdes } else if (bw->bwend.tv_usec < 10000) { 1051221420Sdes bw->thresh *= 2; 1052221420Sdes if (bw->thresh > bw->buflen * 8) 1053221420Sdes bw->thresh = bw->buflen * 8; 1054221420Sdes } 1055221420Sdes 1056221420Sdes TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts); 1057221420Sdes while (nanosleep(&ts, &rm) == -1) { 1058221420Sdes if (errno != EINTR) 1059221420Sdes break; 1060221420Sdes ts = rm; 1061221420Sdes } 1062221420Sdes } 1063221420Sdes 1064221420Sdes bw->lamt = 0; 1065221420Sdes gettimeofday(&bw->bwstart, NULL); 1066221420Sdes} 1067221420Sdes 1068221420Sdes/* Make a template filename for mk[sd]temp() */ 1069221420Sdesvoid 1070221420Sdesmktemp_proto(char *s, size_t len) 1071221420Sdes{ 1072221420Sdes const char *tmpdir; 1073221420Sdes int r; 1074221420Sdes 1075221420Sdes if ((tmpdir = getenv("TMPDIR")) != NULL) { 1076221420Sdes r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir); 1077221420Sdes if (r > 0 && (size_t)r < len) 1078221420Sdes return; 1079221420Sdes } 1080221420Sdes r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX"); 1081221420Sdes if (r < 0 || (size_t)r >= len) 1082221420Sdes fatal("%s: template string too short", __func__); 1083221420Sdes} 1084221420Sdes 1085221420Sdesstatic const struct { 1086221420Sdes const char *name; 1087221420Sdes int value; 1088221420Sdes} ipqos[] = { 1089221420Sdes { "af11", IPTOS_DSCP_AF11 }, 1090221420Sdes { "af12", IPTOS_DSCP_AF12 }, 1091221420Sdes { "af13", IPTOS_DSCP_AF13 }, 1092240075Sdes { "af21", IPTOS_DSCP_AF21 }, 1093221420Sdes { "af22", IPTOS_DSCP_AF22 }, 1094221420Sdes { "af23", IPTOS_DSCP_AF23 }, 1095221420Sdes { "af31", IPTOS_DSCP_AF31 }, 1096221420Sdes { "af32", IPTOS_DSCP_AF32 }, 1097221420Sdes { "af33", IPTOS_DSCP_AF33 }, 1098221420Sdes { "af41", IPTOS_DSCP_AF41 }, 1099221420Sdes { "af42", IPTOS_DSCP_AF42 }, 1100221420Sdes { "af43", IPTOS_DSCP_AF43 }, 1101221420Sdes { "cs0", IPTOS_DSCP_CS0 }, 1102221420Sdes { "cs1", IPTOS_DSCP_CS1 }, 1103221420Sdes { "cs2", IPTOS_DSCP_CS2 }, 1104221420Sdes { "cs3", IPTOS_DSCP_CS3 }, 1105221420Sdes { "cs4", IPTOS_DSCP_CS4 }, 1106221420Sdes { "cs5", IPTOS_DSCP_CS5 }, 1107221420Sdes { "cs6", IPTOS_DSCP_CS6 }, 1108221420Sdes { "cs7", IPTOS_DSCP_CS7 }, 1109221420Sdes { "ef", IPTOS_DSCP_EF }, 1110221420Sdes { "lowdelay", IPTOS_LOWDELAY }, 1111221420Sdes { "throughput", IPTOS_THROUGHPUT }, 1112221420Sdes { "reliability", IPTOS_RELIABILITY }, 1113221420Sdes { NULL, -1 } 1114221420Sdes}; 1115221420Sdes 1116215116Sdesint 1117221420Sdesparse_ipqos(const char *cp) 1118215116Sdes{ 1119221420Sdes u_int i; 1120221420Sdes char *ep; 1121221420Sdes long val; 1122215116Sdes 1123221420Sdes if (cp == NULL) 1124221420Sdes return -1; 1125221420Sdes for (i = 0; ipqos[i].name != NULL; i++) { 1126221420Sdes if (strcasecmp(cp, ipqos[i].name) == 0) 1127221420Sdes return ipqos[i].value; 1128221420Sdes } 1129221420Sdes /* Try parsing as an integer */ 1130221420Sdes val = strtol(cp, &ep, 0); 1131221420Sdes if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255) 1132221420Sdes return -1; 1133221420Sdes return val; 1134215116Sdes} 1135221420Sdes 1136226046Sdesconst char * 1137226046Sdesiptos2str(int iptos) 1138226046Sdes{ 1139226046Sdes int i; 1140226046Sdes static char iptos_str[sizeof "0xff"]; 1141226046Sdes 1142226046Sdes for (i = 0; ipqos[i].name != NULL; i++) { 1143226046Sdes if (ipqos[i].value == iptos) 1144226046Sdes return ipqos[i].name; 1145226046Sdes } 1146226046Sdes snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos); 1147226046Sdes return iptos_str; 1148226046Sdes} 1149261320Sdes 1150204917Sdesvoid 1151261320Sdeslowercase(char *s) 1152261320Sdes{ 1153261320Sdes for (; *s; s++) 1154261320Sdes *s = tolower((u_char)*s); 1155261320Sdes} 1156294328Sdes 1157294328Sdesint 1158294328Sdesunix_listener(const char *path, int backlog, int unlink_first) 1159294328Sdes{ 1160294328Sdes struct sockaddr_un sunaddr; 1161294328Sdes int saved_errno, sock; 1162294328Sdes 1163294328Sdes memset(&sunaddr, 0, sizeof(sunaddr)); 1164294328Sdes sunaddr.sun_family = AF_UNIX; 1165294328Sdes if (strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) { 1166294328Sdes error("%s: \"%s\" too long for Unix domain socket", __func__, 1167294328Sdes path); 1168294328Sdes errno = ENAMETOOLONG; 1169294328Sdes return -1; 1170294328Sdes } 1171294328Sdes 1172294328Sdes sock = socket(PF_UNIX, SOCK_STREAM, 0); 1173294328Sdes if (sock < 0) { 1174294328Sdes saved_errno = errno; 1175294328Sdes error("socket: %.100s", strerror(errno)); 1176294328Sdes errno = saved_errno; 1177294328Sdes return -1; 1178294328Sdes } 1179294328Sdes if (unlink_first == 1) { 1180294328Sdes if (unlink(path) != 0 && errno != ENOENT) 1181294328Sdes error("unlink(%s): %.100s", path, strerror(errno)); 1182294328Sdes } 1183294328Sdes if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { 1184294328Sdes saved_errno = errno; 1185294328Sdes error("bind: %.100s", strerror(errno)); 1186294328Sdes close(sock); 1187294328Sdes error("%s: cannot bind to path: %s", __func__, path); 1188294328Sdes errno = saved_errno; 1189294328Sdes return -1; 1190294328Sdes } 1191294328Sdes if (listen(sock, backlog) < 0) { 1192294328Sdes saved_errno = errno; 1193294328Sdes error("listen: %.100s", strerror(errno)); 1194294328Sdes close(sock); 1195294328Sdes unlink(path); 1196294328Sdes error("%s: cannot listen on path: %s", __func__, path); 1197294328Sdes errno = saved_errno; 1198294328Sdes return -1; 1199294328Sdes } 1200294328Sdes return sock; 1201294328Sdes} 1202294328Sdes 1203261320Sdesvoid 1204204917Sdessock_set_v6only(int s) 1205204917Sdes{ 1206296633Sdes#if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) 1207204917Sdes int on = 1; 1208204917Sdes 1209204917Sdes debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); 1210204917Sdes if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) 1211204917Sdes error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); 1212204917Sdes#endif 1213204917Sdes} 1214323129Sdes 1215323129Sdes/* 1216323129Sdes * Compares two strings that maybe be NULL. Returns non-zero if strings 1217323129Sdes * are both NULL or are identical, returns zero otherwise. 1218323129Sdes */ 1219323129Sdesstatic int 1220323129Sdesstrcmp_maybe_null(const char *a, const char *b) 1221323129Sdes{ 1222323129Sdes if ((a == NULL && b != NULL) || (a != NULL && b == NULL)) 1223323129Sdes return 0; 1224323129Sdes if (a != NULL && strcmp(a, b) != 0) 1225323129Sdes return 0; 1226323129Sdes return 1; 1227323129Sdes} 1228323129Sdes 1229323129Sdes/* 1230323129Sdes * Compare two forwards, returning non-zero if they are identical or 1231323129Sdes * zero otherwise. 1232323129Sdes */ 1233323129Sdesint 1234323129Sdesforward_equals(const struct Forward *a, const struct Forward *b) 1235323129Sdes{ 1236323129Sdes if (strcmp_maybe_null(a->listen_host, b->listen_host) == 0) 1237323129Sdes return 0; 1238323129Sdes if (a->listen_port != b->listen_port) 1239323129Sdes return 0; 1240323129Sdes if (strcmp_maybe_null(a->listen_path, b->listen_path) == 0) 1241323129Sdes return 0; 1242323129Sdes if (strcmp_maybe_null(a->connect_host, b->connect_host) == 0) 1243323129Sdes return 0; 1244323129Sdes if (a->connect_port != b->connect_port) 1245323129Sdes return 0; 1246323129Sdes if (strcmp_maybe_null(a->connect_path, b->connect_path) == 0) 1247323129Sdes return 0; 1248323129Sdes /* allocated_port and handle are not checked */ 1249323129Sdes return 1; 1250323129Sdes} 1251323129Sdes 1252323134Sdesstatic int 1253323134Sdesipport_reserved(void) 1254323134Sdes{ 1255323134Sdes#if __FreeBSD__ 1256323134Sdes int old, ret; 1257323134Sdes size_t len = sizeof(old); 1258323134Sdes 1259323134Sdes ret = sysctlbyname("net.inet.ip.portrange.reservedhigh", 1260323134Sdes &old, &len, NULL, 0); 1261323134Sdes if (ret == 0) 1262323134Sdes return (old + 1); 1263323134Sdes#endif 1264323134Sdes return (IPPORT_RESERVED); 1265323134Sdes} 1266323134Sdes 1267323134Sdes/* returns 1 if bind to specified port by specified user is permitted */ 1268323134Sdesint 1269323134Sdesbind_permitted(int port, uid_t uid) 1270323134Sdes{ 1271323134Sdes 1272323134Sdes if (port < ipport_reserved() && uid != 0) 1273323134Sdes return 0; 1274323134Sdes return 1; 1275323134Sdes} 1276323134Sdes 1277323134Sdes/* returns 1 if process is already daemonized, 0 otherwise */ 1278323134Sdesint 1279323134Sdesdaemonized(void) 1280323134Sdes{ 1281323134Sdes int fd; 1282323134Sdes 1283323134Sdes if ((fd = open(_PATH_TTY, O_RDONLY | O_NOCTTY)) >= 0) { 1284323134Sdes close(fd); 1285323134Sdes return 0; /* have controlling terminal */ 1286323134Sdes } 1287323134Sdes if (getppid() != 1) 1288323134Sdes return 0; /* parent is not init */ 1289323134Sdes if (getsid(0) != getpid()) 1290323134Sdes return 0; /* not session leader */ 1291323134Sdes debug3("already daemonized"); 1292323134Sdes return 1; 1293323134Sdes} 1294