xref: /illumos-gate/usr/src/cmd/listen/nstoa.c (revision 55fea89d)
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 
73 char	scanbuf[SBUFSIZE];
74 int	sbp = 0;
75 int	rec = 0;	/* Recursion level */
76 
77 char	sbuf[SBUFSIZE];
78 
79 extern void free();
80 
81 struct netbuf *
stoa(str,addr)82 stoa(str, addr)			/* Return 0 for success, -1 for error */
83 char	*str;
84 struct 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 
163 int
dostring(s,buf,quote)164 dostring(s, buf, quote)		/* read in a raw address */
165 char	*s, *buf;
166 int	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 
249 int
dobase(s,buf,type)250 dobase(s, buf, type)	/* read in an address */
251 char	*s, *buf;	/* source ASCII, result binary string */
252 int	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 
314 char *
atos(str,addr,type)315 atos(str, addr, type)
316 char	*str;
317 struct netbuf	*addr;
318 int	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 
376 unsigned
x_atos(s,a,l)377 x_atos(s, a, l)
378 char	*s, *a;
379 unsigned	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 
399 unsigned
o_atos(s,a,l)400 o_atos(s, a, l)
401 char	*s, *a;
402 unsigned	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 
441 void
memcp(d,s,n)442 memcp(d, s, n)	/* safe memcpy for overlapping regions */
443 char	*d, *s;
444 int	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 
456 char *
xfer(dest,src,len,max)457 xfer(dest, src, len, max)
458 char	*dest, *src;
459 unsigned	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 
481 char *
prescan(s)482 prescan(s)
483 char	*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 
501 int
scan(s)502 scan(s)
503 char	*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 \
514 level 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 
543 char *
xcmd(s,ec,ps,len)544 xcmd(s, ec, ps, len)
545 char	*s;		/* input string */
546 char	ec;		/* escape char ( $ or ! ) */
547 char	**ps;		/* address of input string pointer */
548 int	*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