1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22
23/*
24 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29/*	  All Rights Reserved  	*/
30
31/*
32	stoa - convert string to address
33
34	If a string begins in \o or \O, the following address is octal
35	"  "   "       "    " \x or \X, the following address is hex
36	Otherwise, a string is considered text. Text may be quoted
37	with double quotes and the C-like escapes \n, \b, \t, \v, \r, and \nnn
38	(nnn = octal char) are recognized.
39	A \ followed by a newline causes the newline
40	to vanish. A \ followed by any other char causes any "magic" of
41	any other char to disappear.
42
43	Other escape sequences recognized are:
44		\!cmd args [ \! || EOL ]
45	which is replaced by the raw output of the execution of cmd.
46	This may only be used in a string.
47
48		\$cmd args [ \$ || EOL ]
49	which is replaced by the output of the execution of cmd and
50	is then reprocessed.
51
52	A  NULL is returned on any error(s).
53*/
54
55#include <stdio.h>
56#include <memory.h>
57#include <ctype.h>
58#include "nsaddr.h"
59
60
61#define	toupper(c)	(islower(c) ? _toupper(c) : (c))
62#define	todigit(c)	((int)((c) - '0'))	/* char to digit */
63#define	toxdigit(c)	((isdigit(c))?todigit(c):(toupper(c)-(int)'A'+10))
64#define	isodigit(c)	(isdigit(c) && ((c) != '9') && ((c) != '8'))
65#define	itoac(i)	(((i) > 9) ? ((char)((i)-10) + 'A'):((char)(i) + '0'))
66#define	MASK(n)		((1 << (n)) - 1)
67
68#define	MAXRLEVEL	10	/* maximum recursion level */
69
70#define	TRUE	1;
71#define	FALSE	0;
72
73char	scanbuf[SBUFSIZE];
74int	sbp = 0;
75int	rec = 0;	/* Recursion level */
76
77char	sbuf[SBUFSIZE];
78
79extern void free();
80
81struct netbuf *
82stoa(str, addr)			/* Return 0 for success, -1 for error */
83char	*str;
84struct netbuf	*addr;
85{
86	char	*xfer(), *prescan();
87
88	int	myadr;		/* was netbuf struct allocated here ? */
89	int	quote;		/* quoted string ? */
90
91	myadr = FALSE;
92
93	if (!str)
94		return NULL;
95	while (*str && isspace(*str))	/* leading whites are OK */
96		++str;
97
98	str = prescan(str);		/* Do all \$ ... \$ */
99
100	if (!str || !*str) return NULL;		/* Nothing to convert */
101
102	if (!addr) {
103		if ((addr = (struct netbuf *)malloc(sizeof(struct netbuf))) == NULL)
104			return NULL;
105		myadr = TRUE;
106		addr->buf = NULL;
107		addr->maxlen = 0;
108		addr->len = 0;
109	}
110
111	/* Now process the address */
112	quote = 0;
113
114	if (*str == '\\') {
115		++str;
116		switch (*str) {
117
118		case 'X':	/* hex */
119		case 'x':
120			addr->len = dobase(++str, sbuf, HEX);
121			break;
122
123		case 'o':	/* octal */
124		case 'O':
125			addr->len = dobase(++str, sbuf, OCT);
126			break;
127
128		case '\0':	/* No address given!, length is 0 */
129			addr->len = dostring(str, sbuf, 0);
130			break;
131
132		default:	/* \ is handled by dostring */
133			addr->len = dostring(--str, sbuf, quote);
134			break;
135		}
136	}
137	else {
138		if (*str == '"') {	/* quoted string */
139			quote = 1;
140			++str;
141		}
142		addr->len = dostring(str, sbuf, quote);
143	}
144
145	if (addr->len == 0) {	/* Error in conversion */
146		if (myadr)
147			free(addr);
148		return NULL;
149	}
150	if ((addr->buf = xfer(addr->buf, sbuf, addr->len, addr->maxlen)) == NULL)
151		return NULL;
152	else
153		return addr;
154}
155
156
157/*
158	dostring:	Copy string at s to buf translating
159		escaped characters and shell escapes.
160	return length of string.
161*/
162
163int
164dostring(s, buf, quote)		/* read in a raw address */
165char	*s, *buf;
166int	quote;
167{
168	char	*xcmd();
169
170	int	oc, ch, len = 0;
171	int	l = 0;
172	char	*rout;
173
174	while (*s) {
175		if (len >= SBUFSIZE) {
176			fprintf(stderr, "dostring: string too long\n");
177			break;
178		}
179		else if (*s == '\\')
180			switch(*++s) {
181
182			case '!':	/* raw shell escape */
183				if (rout = xcmd(s+1, '!', &s, &l)) {
184					if (len + l < SBUFSIZE)
185						memcpy(buf+len, rout, l);
186					len += l;
187					free(rout);
188				}
189				break;
190
191			case '\n':	/* ignore newline */
192				++s;
193				break;
194
195			case 'b':	/* backspace */
196				buf[len++] = '\b'; s++;
197				break;
198
199			case 'n':	/* newline */
200				buf[len++] = '\n'; s++;
201				break;
202
203			case 'r':	/* return */
204				buf[len++] = '\r'; s++;
205				break;
206
207			case 't':	/* horiz. tab */
208				buf[len++] = '\t'; s++;
209				break;
210
211			case 'v':	/* vert. tab */
212				buf[len++] = '\v'; s++;
213				/* FALLTHROUGH */
214
215			case '0':
216			case '1':
217			case '2':
218			case '3':
219				for(oc=ch=0; (*s >= '0' && *s <= '7') && oc++ < 3; ++s)
220					ch = (ch << 3) | (*s - '0');
221				buf[len++] = ch;
222				break;
223
224			case 0:		/* end of string -- terminate */
225				break;
226
227			default:	/* take the character blindly */
228				buf[len++] = *s++;
229				break;
230			}
231		else if ((quote && (*s == '"')) || (!quote && isspace(*s)))
232			break;
233
234		else
235			buf[len++] = *s++;
236	}
237	return (len >= SBUFSIZE) ? 0 : len;
238}
239
240
241/*
242	dobase :	converts a hex or octal ASCII string
243		to a binary address. Only HEX or OCT may be used
244		for type.
245	return length of binary string (in bytes), 0 if error.
246	The binary result is placed at buf.
247*/
248
249int
250dobase(s, buf, type)	/* read in an address */
251char	*s, *buf;	/* source ASCII, result binary string */
252int	type;
253{
254	void	memcp();
255	int	bp = SBUFSIZE - 1;
256	int	shift = 0;
257	char	*end;
258
259	for (end = s; *end && ((type == OCT) ? isodigit(*end) :
260		isxdigit(*end)); ++end) ;
261
262	/* any non-white, non-digits cause address to be rejected,
263	   other fields are ignored */
264
265	if ((*s == 0) || (end == s) || (!isspace(*end) && *end)) {
266		fprintf(stderr, "dobase: Illegal trailer on address string\n");
267		buf[0] = '\0';
268		return 0;
269	}
270	--end;
271
272	buf[bp] = '\0';
273
274	while (bp > 0 && end >= s) {
275		buf[bp] |= toxdigit(*end) << shift;
276		if (type == OCT) {
277			if (shift > 5) {
278				buf[--bp] = (todigit(*end) >> (8 - shift))
279					& MASK(shift-5);
280			}
281			if ((shift = (shift + 3) % 8) == 0)
282				buf[--bp] = 0;
283		}
284		else	/* hex */
285			if ((shift = (shift) ? 0 : 4) == 0)
286				buf[--bp] = 0;;
287		--end;
288	}
289	if (bp == 0) {
290		fprintf(stderr, "stoa: dobase: number to long\n");
291		return 0;
292	}
293
294	/* need to catch end case to avoid extra 0's in front	*/
295	if (!shift)
296		bp++;
297	memcp(buf, &buf[bp], (SBUFSIZE - bp));
298	return (SBUFSIZE - bp);
299}
300
301#ifdef NOTUSED
302
303
304/*
305
306	atos(str, addr, type)
307
308	convert address to ASCII form with address in hex, octal,
309	or character form.
310	return pointer to buffer (NULL on failure).
311*/
312
313
314char *
315atos(str, addr, type)
316char	*str;
317struct netbuf	*addr;
318int	type;
319{
320	char	*xfer();
321	int	mystr = 0;	/* was str allocated here ? */
322	unsigned	x_atos(), o_atos();
323	void	memcp();
324
325	char	*base;
326
327	if (addr == NULL)
328		return NULL;
329
330	if (str == NULL)
331		if ((str = malloc(SBUFSIZE)) == NULL)
332			return NULL;
333		else
334			mystr = 1;
335
336	switch (type) {
337
338	case OCT:
339		/* first add \o */
340		sbuf[0] = '\\';
341		sbuf[1] = 'o';
342
343		return xfer(str, sbuf, o_atos(sbuf+2, addr->buf, addr->len) + 2,
344			mystr ? SBUFSIZE : 0);
345
346	case HEX:
347		/* first add \x */
348		sbuf[0] = '\\';
349		sbuf[1] = 'x';
350
351		return xfer(str, sbuf, x_atos(sbuf+2, addr->buf, addr->len) + 2,
352			mystr ? SBUFSIZE : 0);
353
354	case RAW:
355		base = xfer(str, addr->buf,
356			 addr->len + 1, mystr ? SBUFSIZE : 0);
357		if (base)
358			base[addr->len] = '\0';	/* terminate*/
359		return base;
360
361	default:
362		return NULL;
363	}
364}
365
366
367/*
368	x_atos, o_atos
369	return the number of bytes occupied by string + NULL*/
370
371/*
372	x_atos :	convert an address string a, length s
373		to hex ASCII in s */
374
375
376unsigned
377x_atos(s, a, l)
378char	*s, *a;
379unsigned	l;
380{
381	char	*b;
382
383	b = s;
384	while (l--) {
385		*s++ = itoac(((*a >> 4) & MASK (4)));
386		*s++ = itoac((*a & MASK(4)));
387		++a;
388	}
389	*s = '\0';
390	return (s - b + 1);
391}
392
393
394/*
395	o_atos :	convert an address a, length l
396		to octal ASCII in s   */
397
398
399unsigned
400o_atos(s, a, l)
401char	*s, *a;
402unsigned	l;
403{
404	int	i, shift;
405	char	*b;
406
407	b = s;
408	if (l == 0) {
409		*s = '\0';
410		return 0;
411	}
412
413	/* take care of partial bits and set shift factor for next 3  */
414
415	i = l % 3;
416	*s++ = itoac((*a>>(i+5)) & MASK(3-i));
417	shift = 2 + i;
418
419	while (l)
420		if (shift <= 5) {
421			*s++ = itoac((*a >> shift) & MASK(3));
422			if (shift == 0) {
423				++a;
424				--l;
425			}
426			shift += (shift < 3) ? 5 : -3;
427		}
428		else {
429			i = (*a & MASK(shift-5)) << (8-shift);
430			i |= (*++a >> shift) & MASK(8-shift);
431			*s++ = itoac(i);
432			shift -= 3;
433			--l;
434		}
435	*s++ = '\0';
436	return (s - b + 1);
437}
438
439#endif /* NOTUSED */
440
441void
442memcp(d, s, n)	/* safe memcpy for overlapping regions */
443char	*d, *s;
444int	n;
445{
446	while (n--)
447		*d++ = *s++;
448}
449
450
451/* transfer block to a given destination or allocate one of the
452    right size
453    if max = 0 : ignore max
454*/
455
456char *
457xfer(dest, src, len, max)
458char	*dest, *src;
459unsigned	len, max;
460{
461	if (max && dest && max < len) {		/* No room */
462		fprintf(stderr, "xfer: destination not long enough\n");
463		return NULL;
464	}
465	if (!dest)
466		if ((dest = (char *)malloc(len)) == NULL) {
467			fprintf(stderr, "xfer: malloc failed\n");
468			return NULL;
469		}
470
471	memcpy(dest, src, (int)len);
472	return dest;
473}
474
475/*
476	prescan:	scan through string s, expanding all \$...\$
477		as shell escapes.
478	Return pointer to string of expanded text.
479*/
480
481char *
482prescan(s)
483char	*s;
484{
485	int	scan();
486
487	rec = sbp = 0;
488	if (!s || !*s || !scan(s))
489		return NULL;
490	scanbuf[sbp] = '\0';
491	return scanbuf;
492}
493
494
495/*
496	scan:	scan through string s, expanding all \$...\$.
497	(Part II of prescan)
498	Return 0 if anything failed, else 1.
499*/
500
501int
502scan(s)
503char	*s;
504{
505	char	*xcmd();
506	char	*cmd;
507	int	len;
508	int	esc = 0;		/* Keep lookout for \\$ */
509
510	while (*s) {
511		if (!esc && (*s == '\\' && *(s+1) == '$')) {
512			if (rec++ == MAXRLEVEL) {
513				fprintf(stderr, "scan: Recursion \
514level past %d on shell escape\n", rec);
515				return 0;
516			}
517			if ((cmd = xcmd(s+2, '$', &s, &len)) != NULL) {
518				cmd[len] = '\0';
519 				if (*cmd != '\0')
520					scan(cmd);
521				free(cmd);
522			}
523			else
524				return 0;
525		}
526
527		else if (sbp == SBUFSIZE) {
528			fprintf(stderr, "Overflow on shell esc expansion\n");
529			return 0;
530		}
531		else if (sbp < SBUFSIZE)
532			esc = ((scanbuf[sbp++] = *s++) == '\\');
533	}
534	return 1;
535}
536
537
538/*
539	xcmd :	extract command line for shell escape and execute it
540		return pointer to output of command
541*/
542
543char *
544xcmd(s, ec, ps, len)
545char	*s;		/* input string */
546char	ec;		/* escape char ( $ or ! ) */
547char	**ps;		/* address of input string pointer */
548int	*len;		/* Number of bytes of output from command */
549{
550	FILE	*popen();
551	int	pclose();
552
553	FILE	*pfp;		/* pipe for process */
554	char	*cmd;		/* command buffer */
555	char	*cmdp;		/* pointer along cmd */
556	char	*ocmd;		/* output of command buffer */
557	int	esc = 0;	/* escaped escape shell */
558
559	*len = 0;
560
561	if ((cmd = cmdp = (char *)malloc(SBUFSIZE)) == NULL) {
562		fprintf(stderr, "xcmd: malloc failed\n");
563		return NULL;
564	}
565
566	if ((ocmd = (char *)malloc(SBUFSIZE)) == NULL) {
567		fprintf(stderr, "xcmd: malloc failed\n");
568		free(cmd);
569		return NULL;
570	}
571	while (*s) {
572		if (!esc && *s == '\\' && *(s+1) == ec) {
573			s += 2;
574			break;
575		}
576		else
577			esc = (*cmdp++ = *s++) == '\\';
578	}
579	*cmdp = '\0';
580	*ps = s;
581
582	if ((pfp = popen(cmd, "r")) == NULL)
583		fprintf(stderr, "xcmd: popen failed\n");
584	while (fread(&ocmd[*len], 1, 1, pfp))
585		if ((*len += 1) >= SBUFSIZE) {
586			fprintf(stderr, "xcmd: command output too long\n");
587			break;
588		}
589	pclose(pfp);
590	free(cmd);
591
592	return ocmd;
593}
594