misc.c revision 146998
1/*
2 * Copyright (c) 2000 Markus Friedl.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26RCSID("$OpenBSD: misc.c,v 1.28 2005/03/01 10:09:52 djm Exp $");
27
28#include "misc.h"
29#include "log.h"
30#include "xmalloc.h"
31
32/* remove newline at end of string */
33char *
34chop(char *s)
35{
36	char *t = s;
37	while (*t) {
38		if (*t == '\n' || *t == '\r') {
39			*t = '\0';
40			return s;
41		}
42		t++;
43	}
44	return s;
45
46}
47
48/* set/unset filedescriptor to non-blocking */
49int
50set_nonblock(int fd)
51{
52	int val;
53
54	val = fcntl(fd, F_GETFL, 0);
55	if (val < 0) {
56		error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
57		return (-1);
58	}
59	if (val & O_NONBLOCK) {
60		debug3("fd %d is O_NONBLOCK", fd);
61		return (0);
62	}
63	debug2("fd %d setting O_NONBLOCK", fd);
64	val |= O_NONBLOCK;
65	if (fcntl(fd, F_SETFL, val) == -1) {
66		debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd,
67		    strerror(errno));
68		return (-1);
69	}
70	return (0);
71}
72
73int
74unset_nonblock(int fd)
75{
76	int val;
77
78	val = fcntl(fd, F_GETFL, 0);
79	if (val < 0) {
80		error("fcntl(%d, F_GETFL, 0): %s", fd, strerror(errno));
81		return (-1);
82	}
83	if (!(val & O_NONBLOCK)) {
84		debug3("fd %d is not O_NONBLOCK", fd);
85		return (0);
86	}
87	debug("fd %d clearing O_NONBLOCK", fd);
88	val &= ~O_NONBLOCK;
89	if (fcntl(fd, F_SETFL, val) == -1) {
90		debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s",
91		    fd, strerror(errno));
92		return (-1);
93	}
94	return (0);
95}
96
97/* disable nagle on socket */
98void
99set_nodelay(int fd)
100{
101	int opt;
102	socklen_t optlen;
103
104	optlen = sizeof opt;
105	if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) {
106		debug("getsockopt TCP_NODELAY: %.100s", strerror(errno));
107		return;
108	}
109	if (opt == 1) {
110		debug2("fd %d is TCP_NODELAY", fd);
111		return;
112	}
113	opt = 1;
114	debug2("fd %d setting TCP_NODELAY", fd);
115	if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)
116		error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
117}
118
119/* Characters considered whitespace in strsep calls. */
120#define WHITESPACE " \t\r\n"
121
122/* return next token in configuration line */
123char *
124strdelim(char **s)
125{
126	char *old;
127	int wspace = 0;
128
129	if (*s == NULL)
130		return NULL;
131
132	old = *s;
133
134	*s = strpbrk(*s, WHITESPACE "=");
135	if (*s == NULL)
136		return (old);
137
138	/* Allow only one '=' to be skipped */
139	if (*s[0] == '=')
140		wspace = 1;
141	*s[0] = '\0';
142
143	*s += strspn(*s + 1, WHITESPACE) + 1;
144	if (*s[0] == '=' && !wspace)
145		*s += strspn(*s + 1, WHITESPACE) + 1;
146
147	return (old);
148}
149
150struct passwd *
151pwcopy(struct passwd *pw)
152{
153	struct passwd *copy = xmalloc(sizeof(*copy));
154
155	memset(copy, 0, sizeof(*copy));
156	copy->pw_name = xstrdup(pw->pw_name);
157	copy->pw_passwd = xstrdup(pw->pw_passwd);
158	copy->pw_gecos = xstrdup(pw->pw_gecos);
159	copy->pw_uid = pw->pw_uid;
160	copy->pw_gid = pw->pw_gid;
161#ifdef HAVE_PW_EXPIRE_IN_PASSWD
162	copy->pw_expire = pw->pw_expire;
163#endif
164#ifdef HAVE_PW_CHANGE_IN_PASSWD
165	copy->pw_change = pw->pw_change;
166#endif
167#ifdef HAVE_PW_CLASS_IN_PASSWD
168	copy->pw_class = xstrdup(pw->pw_class);
169#endif
170	copy->pw_dir = xstrdup(pw->pw_dir);
171	copy->pw_shell = xstrdup(pw->pw_shell);
172	return copy;
173}
174
175/*
176 * Convert ASCII string to TCP/IP port number.
177 * Port must be >0 and <=65535.
178 * Return 0 if invalid.
179 */
180int
181a2port(const char *s)
182{
183	long port;
184	char *endp;
185
186	errno = 0;
187	port = strtol(s, &endp, 0);
188	if (s == endp || *endp != '\0' ||
189	    (errno == ERANGE && (port == LONG_MIN || port == LONG_MAX)) ||
190	    port <= 0 || port > 65535)
191		return 0;
192
193	return port;
194}
195
196#define SECONDS		1
197#define MINUTES		(SECONDS * 60)
198#define HOURS		(MINUTES * 60)
199#define DAYS		(HOURS * 24)
200#define WEEKS		(DAYS * 7)
201
202/*
203 * Convert a time string into seconds; format is
204 * a sequence of:
205 *      time[qualifier]
206 *
207 * Valid time qualifiers are:
208 *      <none>  seconds
209 *      s|S     seconds
210 *      m|M     minutes
211 *      h|H     hours
212 *      d|D     days
213 *      w|W     weeks
214 *
215 * Examples:
216 *      90m     90 minutes
217 *      1h30m   90 minutes
218 *      2d      2 days
219 *      1w      1 week
220 *
221 * Return -1 if time string is invalid.
222 */
223long
224convtime(const char *s)
225{
226	long total, secs;
227	const char *p;
228	char *endp;
229
230	errno = 0;
231	total = 0;
232	p = s;
233
234	if (p == NULL || *p == '\0')
235		return -1;
236
237	while (*p) {
238		secs = strtol(p, &endp, 10);
239		if (p == endp ||
240		    (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) ||
241		    secs < 0)
242			return -1;
243
244		switch (*endp++) {
245		case '\0':
246			endp--;
247		case 's':
248		case 'S':
249			break;
250		case 'm':
251		case 'M':
252			secs *= MINUTES;
253			break;
254		case 'h':
255		case 'H':
256			secs *= HOURS;
257			break;
258		case 'd':
259		case 'D':
260			secs *= DAYS;
261			break;
262		case 'w':
263		case 'W':
264			secs *= WEEKS;
265			break;
266		default:
267			return -1;
268		}
269		total += secs;
270		if (total < 0)
271			return -1;
272		p = endp;
273	}
274
275	return total;
276}
277
278/*
279 * Search for next delimiter between hostnames/addresses and ports.
280 * Argument may be modified (for termination).
281 * Returns *cp if parsing succeeds.
282 * *cp is set to the start of the next delimiter, if one was found.
283 * If this is the last field, *cp is set to NULL.
284 */
285char *
286hpdelim(char **cp)
287{
288	char *s, *old;
289
290	if (cp == NULL || *cp == NULL)
291		return NULL;
292
293	old = s = *cp;
294	if (*s == '[') {
295		if ((s = strchr(s, ']')) == NULL)
296			return NULL;
297		else
298			s++;
299	} else if ((s = strpbrk(s, ":/")) == NULL)
300		s = *cp + strlen(*cp); /* skip to end (see first case below) */
301
302	switch (*s) {
303	case '\0':
304		*cp = NULL;	/* no more fields*/
305		break;
306
307	case ':':
308	case '/':
309		*s = '\0';	/* terminate */
310		*cp = s + 1;
311		break;
312
313	default:
314		return NULL;
315	}
316
317	return old;
318}
319
320char *
321cleanhostname(char *host)
322{
323	if (*host == '[' && host[strlen(host) - 1] == ']') {
324		host[strlen(host) - 1] = '\0';
325		return (host + 1);
326	} else
327		return host;
328}
329
330char *
331colon(char *cp)
332{
333	int flag = 0;
334
335	if (*cp == ':')		/* Leading colon is part of file name. */
336		return (0);
337	if (*cp == '[')
338		flag = 1;
339
340	for (; *cp; ++cp) {
341		if (*cp == '@' && *(cp+1) == '[')
342			flag = 1;
343		if (*cp == ']' && *(cp+1) == ':' && flag)
344			return (cp+1);
345		if (*cp == ':' && !flag)
346			return (cp);
347		if (*cp == '/')
348			return (0);
349	}
350	return (0);
351}
352
353/* function to assist building execv() arguments */
354void
355addargs(arglist *args, char *fmt, ...)
356{
357	va_list ap;
358	char buf[1024];
359	u_int nalloc;
360
361	va_start(ap, fmt);
362	vsnprintf(buf, sizeof(buf), fmt, ap);
363	va_end(ap);
364
365	nalloc = args->nalloc;
366	if (args->list == NULL) {
367		nalloc = 32;
368		args->num = 0;
369	} else if (args->num+2 >= nalloc)
370		nalloc *= 2;
371
372	args->list = xrealloc(args->list, nalloc * sizeof(char *));
373	args->nalloc = nalloc;
374	args->list[args->num++] = xstrdup(buf);
375	args->list[args->num] = NULL;
376}
377
378/*
379 * Read an entire line from a public key file into a static buffer, discarding
380 * lines that exceed the buffer size.  Returns 0 on success, -1 on failure.
381 */
382int
383read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz,
384   u_long *lineno)
385{
386	while (fgets(buf, bufsz, f) != NULL) {
387		(*lineno)++;
388		if (buf[strlen(buf) - 1] == '\n' || feof(f)) {
389			return 0;
390		} else {
391			debug("%s: %s line %lu exceeds size limit", __func__,
392			    filename, *lineno);
393			/* discard remainder of line */
394			while(fgetc(f) != '\n' && !feof(f))
395				;	/* nothing */
396		}
397	}
398	return -1;
399}
400