xref: /titanic_51/usr/src/lib/libnisdb/ldap_util.c (revision 45e662eb8429b38c18931ebeed30f2e5287ae51b)
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  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <pthread.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 
38 #include "ldap_util.h"
39 #include "ldap_glob.h"
40 
41 static time_t	msgtime[MSG_LASTMSG] = {0};
42 static time_t	msgtimeout = 3600;
43 
44 static pthread_key_t		tsdKey;
45 
46 /*
47  * Log a message to the appropriate place.
48  */
49 void
50 logmsg(int msgtype, int priority, const char *fmt, ...) {
51 	va_list		ap;
52 	struct timeval	tp;
53 
54 	/*
55 	 * Only log LOG_INFO priority if 'verbose' is on, or if
56 	 * msgtype is MSG_ALWAYS.
57 	 */
58 	if (priority == LOG_INFO && !verbose && msgtype != MSG_ALWAYS)
59 		return;
60 
61 	/* Make sure we don't log the same message too often */
62 	if (msgtype != MSG_NOTIMECHECK && msgtype != MSG_ALWAYS &&
63 			msgtype > 0 && msgtype < MSG_LASTMSG &&
64 			gettimeofday(&tp, 0) != -1) {
65 		if (tp.tv_sec - msgtime[msgtype] < msgtimeout)
66 			return;
67 		msgtime[msgtype] = tp.tv_sec;
68 	}
69 
70 	va_start(ap, fmt);
71 	if (cons == 0) {
72 		vsyslog(priority, fmt, ap);
73 	} else {
74 		int	flen = slen(fmt);
75 
76 		vfprintf(cons, fmt, ap);
77 		/*
78 		 * If the last character in 'fmt' wasn't a '\n', write one
79 		 * to the console.
80 		 */
81 		if (flen > 0 && fmt[flen-1] != '\n')
82 			fprintf(cons, "\n");
83 	}
84 	va_end(ap);
85 }
86 
87 void
88 __destroyTsdKey(void *arg) {
89 	__nis_deferred_error_t	*defErr = arg;
90 
91 	if (defErr != 0) {
92 		sfree(defErr->message);
93 		free(defErr);
94 	}
95 }
96 
97 static void
98 __initTsdKey(void)
99 {
100 	(void) pthread_key_create(&tsdKey, __destroyTsdKey);
101 }
102 #pragma init(__initTsdKey)
103 
104 void
105 reportError(int error, char *fmt, ...) {
106 	__nis_deferred_error_t	*defErr = pthread_getspecific(tsdKey);
107 	int			doStore = (defErr == 0);
108 	char			*myself = "reportError";
109 	va_list			ap;
110 	__nis_buffer_t		b = {0, 0};
111 
112 	if (defErr == 0 && (defErr = am(myself, sizeof (*defErr))) == 0)
113 		return;
114 
115 	va_start(ap, fmt);
116 	b.len = vp2buf(myself, &b.buf, b.len, fmt, ap);
117 	va_end(ap);
118 
119 	if (b.len > 0) {
120 		defErr->error = error;
121 		defErr->message = b.buf;
122 		if (doStore) {
123 			int	ret = pthread_setspecific(tsdKey, defErr);
124 			if (ret != 0) {
125 				logmsg(MSG_TSDERR, LOG_ERR,
126 					"%s: pthread_setspecific() => %d",
127 					myself, ret);
128 				sfree(b.buf);
129 				free(defErr);
130 			}
131 		}
132 	}
133 }
134 
135 int
136 getError(char **message) {
137 	__nis_deferred_error_t	*defErr = pthread_getspecific(tsdKey);
138 	char			*myself = "getError";
139 
140 	if (defErr == 0) {
141 		if (message != 0)
142 			*message = sdup(myself, T, "no TSD");
143 		return (NPL_TSDERR);
144 	}
145 
146 	if (message != 0)
147 		*message = sdup(myself, T, defErr->message);
148 
149 	return (defErr->error);
150 }
151 
152 void
153 clearError(void) {
154 	__nis_deferred_error_t	*defErr = pthread_getspecific(tsdKey);
155 
156 	if (defErr != 0) {
157 		sfree(defErr->message);
158 		defErr->message = 0;
159 		defErr->error = NPL_NOERROR;
160 	}
161 }
162 
163 void
164 logError(int priority) {
165 	__nis_deferred_error_t	*defErr = pthread_getspecific(tsdKey);
166 	int			msgtype;
167 
168 	if (defErr != 0) {
169 		switch (defErr->error) {
170 		case NPL_NOERROR:
171 			msgtype = MSG_LASTMSG;
172 			break;
173 		case NPL_NOMEM:
174 			msgtype = MSG_NOMEM;
175 			break;
176 		case NPL_TSDERR:
177 			msgtype = MSG_TSDERR;
178 			break;
179 		case NPL_BERENCODE:
180 		case NPL_BERDECODE:
181 			msgtype = MSG_BER;
182 			break;
183 		default:
184 			msgtype = MSG_LASTMSG;
185 			break;
186 		}
187 
188 		if (msgtype != MSG_LASTMSG) {
189 			logmsg(msgtype, priority, defErr->message);
190 		}
191 	}
192 }
193 
194 /*
195  * Allocate zero-initialized memory of the specified 'size'. If the
196  * allocation fails, log a message and return NULL. Allocation of
197  * zero bytes is legal, and returns a NULL pointer.
198  */
199 void *
200 am(const char *msg, int size) {
201 	void	*p;
202 
203 	if (size > 0) {
204 		p = calloc(1, size);
205 		if (p == 0) {
206 			if (msg == 0)
207 				msg = "<unknown>";
208 			logmsg(MSG_NOMEM, LOG_ERR, "%s: calloc(%d) => NULL\n",
209 				msg, size);
210 			return (0);
211 		}
212 	} else if (size == 0) {
213 		p = 0;
214 	} else {
215 		if (msg == 0)
216 			msg = "<unknown>";
217 		logmsg(MSG_MEMPARAM, LOG_INFO, "%s: size (%d) < 0\n", size);
218 		exit(-1);
219 	}
220 	return (p);
221 }
222 
223 /*
224  * Return the length of a string, just like strlen(), but don't croak
225  * on a NULL pointer.
226  */
227 int
228 slen(const char *str) {
229 	return ((str != 0) ? strlen(str) : 0);
230 }
231 
232 /*
233  * If allocate==0, return 'str'; othewise, duplicate the string just
234  * like strdup(), but don't die if 'str' is a NULL pointer.
235  */
236 char *
237 sdup(const char *msg, int allocate, char *str) {
238 	char	*s;
239 
240 	if (!allocate)
241 		return (str);
242 
243 	if (str == 0) {
244 		s = strdup("");
245 	} else {
246 		s = strdup(str);
247 	}
248 	if (s == 0) {
249 		logmsg(MSG_NOMEM, LOG_ERR, "%s: strdup(%d bytes) => NULL\n",
250 			(msg != 0) ? msg : "<unknown>", slen(str)+1);
251 	}
252 	return (s);
253 }
254 
255 /*
256  * Concatenate strings like strcat(), but don't expire if passed a
257  * NULL pointer or two. If deallocate!=0, free() the input strings.
258  */
259 char *
260 scat(const char *msg, int deallocate, char *s1, char *s2) {
261 	char	*n;
262 	int	l1 = 0, l2 = 0;
263 
264 	if (s1 == 0) {
265 		n = sdup(msg, T, s2);
266 		if (deallocate)
267 			sfree(s2);
268 		return (n);
269 	} else if (s2 == 0) {
270 		n = sdup(msg, T, s1);
271 		if (deallocate)
272 			free(s1);
273 		return (n);
274 	}
275 
276 	l1 = strlen(s1);
277 	l2 = strlen(s2);
278 
279 	n = malloc(l1+l2+1);
280 	if (n != 0) {
281 		memcpy(n, s1, l1);
282 		memcpy(&n[l1], s2, l2);
283 		n[l1+l2] = '\0';
284 	} else {
285 		logmsg(MSG_NOMEM, LOG_ERR, "%s: malloc(%d) => NULL\n",
286 			(msg != 0) ? msg : "<unknown>", l1+l2+1);
287 	}
288 
289 	if (deallocate) {
290 		free(s1);
291 		free(s2);
292 	}
293 
294 	return (n);
295 }
296 
297 /* For debugging */
298 static void		*PTR = 0;
299 
300 /*
301  * Counters for memory errors. Note that we don't protect access,
302  * so the values aren't entirely reliable in an MT application.
303  */
304 ulong_t	numMisaligned = 0;
305 ulong_t	numNotActive = 0;
306 
307 /* free() the input, but don't pass away if it's NULL */
308 void
309 sfree(void *ptr) {
310 
311 	/* NULL pointer OK */
312 	if (ptr == 0)
313 		return;
314 
315 	/*
316 	 * For use in the debugger, when we need to detect free of a
317 	 * certain address.
318 	 */
319 	if (ptr == PTR)
320 		abort();
321 
322 	/*
323 	 * All addresses returned by malloc() and friends are "suitably
324 	 * aligned for any use", so they should fall on eight-byte boundaries.
325 	 */
326 	if (((unsigned long)ptr % 8) != 0) {
327 		numMisaligned++;
328 		return;
329 	}
330 
331 #ifdef	NISDB_LDAP_DEBUG
332 	/*
333 	 * Malloc:ed memory should have the length (four bytes), starting
334 	 * eight bytes before the block, and with the least-significant
335 	 * bit set.
336 	 */
337 	if ((((uint_t *)ptr)[-2] & 0x1) == 0) {
338 		numNotActive++;
339 		return;
340 	}
341 #endif	/* NISDB_LDAP_DEBUG */
342 
343 	/* Finally, we believe it's OK to free() the pointer */
344 	free(ptr);
345 }
346 
347 /*
348  * If a __nis_single_value_t represents a string, the length count may or may
349  * not include a concluding NUL. Hence this function, which returns the last
350  * non-NUL character of the value.
351  */
352 char
353 lastChar(__nis_single_value_t *v) {
354 	char	*s;
355 
356 	if (v == 0 || v->value == 0 || v->length < 2)
357 		return ('\0');
358 
359 	s = v->value;
360 	if (s[v->length - 1] != '\0')
361 		return (s[v->length - 1]);
362 	else
363 		return (s[v->length - 2]);
364 }
365 
366 void *
367 appendString2SingleVal(char *str, __nis_single_value_t *v, int *newLen) {
368 	void	*s;
369 	int	l, nl;
370 	char	*myself = "appendString2SingleVal";
371 
372 	if (v == 0 || v->length < 0)
373 		return (0);
374 
375 	/*
376 	 * If 'str' is NULL or empty, just return NULL so that the caller
377 	 * does nothing.
378 	 */
379 	l = slen(str);
380 	if (l <= 0)
381 		return (0);
382 
383 	s = am(myself, (nl = l + v->length) + 1);
384 	if (s == 0) {
385 		/* Caller does nothing; let's hope for the best... */
386 		return (0);
387 	}
388 
389 	if (v->value != 0)
390 		memcpy(s, v->value, v->length);
391 
392 	memcpy(&(((char *)s)[v->length]), str, l);
393 
394 	if (newLen != 0)
395 		*newLen = nl;
396 
397 	return (s);
398 }
399 
400 
401 /*
402  * Do the equivalent of a strcmp() between a string and a string-valued
403  * __nis_single_value_t.
404  */
405 int
406 scmp(char *s, __nis_single_value_t *v) {
407 
408 	if (s == 0)
409 		return (1);
410 	else if (v == 0 || v->value == 0 || v->length <= 0)
411 		return (-1);
412 
413 	return (strncmp(s, v->value, v->length));
414 }
415 
416 /*
417  * Do the equivalent of a strcasecmp() between a string and a string-valued
418  * __nis_single_value_t.
419  */
420 int
421 scasecmp(char *s, __nis_single_value_t *v) {
422 
423 	if (s == 0)
424 		return (1);
425 	else if (v == 0 || v->value == 0 || v->length <= 0)
426 		return (-1);
427 
428 	return (strncasecmp(s, v->value, v->length));
429 }
430 
431 #define	STDBUFSIZE	81
432 
433 /*
434  * vsprintf the 'fmt' and 'ap' to a buffer, then concatenate the
435  * result to '*buf'.
436  */
437 int
438 vp2buf(const char *msg, char **buf, int buflen, const char *fmt, va_list ap) {
439 	char		*newbuf = am(msg, STDBUFSIZE);
440 	int		size = 0;
441 
442 	if (newbuf == 0)
443 		return (0);
444 
445 	if (buf == 0 || buflen < 0 || fmt == 0) {
446 		free(newbuf);
447 		return (0);
448 	}
449 
450 	/* Find out how large the new buffer needs to be */
451 	size = vsnprintf(newbuf, STDBUFSIZE, fmt, ap);
452 
453 	if (size > STDBUFSIZE) {
454 		free(newbuf);
455 		newbuf = am(msg, size+1);
456 		if (newbuf == 0)
457 			return (0);
458 		size = vsnprintf(newbuf, size+1, fmt, ap);
459 	}
460 
461 	*buf = scat(msg, T, *buf, newbuf);
462 	/* Don't count the NUL. This enables us to concatenate correctly */
463 	buflen += size;
464 
465 	return (buflen);
466 }
467 
468 /* Generic print buffer */
469 __nis_buffer_t	pb = {0, 0};
470 
471 /* sprintf to the generic __nis_buffer_t */
472 void
473 p2buf(char *msg, char *fmt, ...) {
474 	va_list	ap;
475 
476 	va_start(ap, fmt);
477 	pb.len = vp2buf(msg, &pb.buf, pb.len, fmt, ap);
478 	va_end(ap);
479 }
480 
481 /* sprintf to the specified __nis_buffer_t */
482 void
483 bp2buf(const char *msg, __nis_buffer_t *b, const char *fmt, ...) {
484 	va_list	ap;
485 
486 	va_start(ap, fmt);
487 	b->len = vp2buf(msg, &b->buf, b->len, fmt, ap);
488 	va_end(ap);
489 }
490 
491 /* Copy 'buf' to the specified __nis_buffer_t */
492 void
493 bc2buf(const char *msg, void *buf, int len, __nis_buffer_t *b) {
494 	void	*new;
495 
496 	/*
497 	 * Make buffer one byte larger than the lenghts indicate. This
498 	 * gives us room to append a NUL, so that we can mix string and
499 	 * non-string copies into the buffer, and still end up with
500 	 * something that can be sent to printf(), strcat(), etc.
501 	 */
502 	new = realloc(b->buf, b->len+len+1);
503 	if (new != 0) {
504 		b->buf = new;
505 		memcpy(&(b->buf[b->len]), buf, len);
506 		b->len += len;
507 		/* Put a NUL at the end, just in case we printf() */
508 		if (b->len > 0 && b->buf[b->len-1] != '\0')
509 			b->buf[b->len] = '\0';
510 	} else {
511 		logmsg(MSG_NOMEM, LOG_ERR, "%s: realloc(%d) => NULL\n",
512 			(msg != 0) ? msg : "<unknown", b->len+len);
513 	}
514 }
515 
516 /* Like bc2buf(), but remove any trailing NUL bytes */
517 void
518 sbc2buf(const char *msg, void *buf, int len, __nis_buffer_t *b) {
519 	if (buf == 0 || len <= 0 || b == 0)
520 		return;
521 	/* Snip off trailing NULs */
522 	while (len > 0 && ((char *)buf)[len-1] == '\0')
523 		len--;
524 	if (len <= 0)
525 		return;
526 	bc2buf(msg, buf, len, b);
527 }
528 
529 /* Copy 'buf' to the generic __nis_buffer_t */
530 void
531 c2buf(char *msg, void *buf, int len) {
532 	bc2buf(msg, buf, len, &pb);
533 }
534 
535 /* Like c2buf(), but remove trailing NUL bytes */
536 void
537 sc2buf(char *msg, void *buf, int len) {
538 	sbc2buf(msg, buf, len, &pb);
539 }
540 
541 /* How many times we try write(2) if it fails */
542 #define	MAXTRY	10
543 
544 /* Output the generic __nis_buffer_t to stdout */
545 void
546 printbuf(void) {
547 	int	maxtry = MAXTRY, len = pb.len;
548 
549 	if (pb.buf != 0) {
550 		int	tmp;
551 
552 		while (len > 0 && maxtry > 0) {
553 			tmp = write(1, pb.buf, len);
554 			if (tmp < 0)
555 				break;
556 			len -= tmp;
557 			if (tmp > 0)
558 				maxtry = MAXTRY;
559 			else
560 				maxtry--;
561 		}
562 		free(pb.buf);
563 		pb.buf = 0;
564 	}
565 	pb.len = 0;
566 }
567 
568 void *
569 extendArray(void *array, int newsize) {
570 	void	*new = realloc(array, newsize);
571 	if (new == 0)
572 		sfree(array);
573 	return (new);
574 }
575 
576 /*
577  * Determine if the given string is an IP address (IPv4 or IPv6).
578  * If so, it converts it to the format as required by rfc2307bis
579  * and *newaddr will point to the new Address.
580  *
581  * Returns	-2		: error
582  *		-1		: not an IP address
583  *		0		: IP address not supported by rfc2307bis
584  *		AF_INET		: IPv4
585  *		AF_INET6	: IPv6
586  */
587 int
588 checkIPaddress(char *addr, int len, char **newaddr) {
589 	ipaddr_t	addr_ipv4;
590 	in6_addr_t	addr_ipv6;
591 	char		*buffer;
592 	int		s, e;
593 	char		*myself = "checkIPaddress";
594 
595 	/* skip leading whitespaces */
596 	for (s = 0; (s < len) && (addr[s] == ' ' || addr[s] == '\t'); s++);
597 	if (s >= len)
598 		return (-1);
599 
600 	/* skip trailing whitespaces */
601 	for (e = len - 1; (e > s) && (addr[e] == ' ' || addr[e] == '\t'); e--);
602 	if (s == e)
603 		return (-1);
604 
605 	/* adjust len */
606 	len = e - s + 1;
607 
608 	if ((buffer = am(myself, len + 1)) == 0)
609 		return (-2);
610 	(void) memcpy(buffer, addr + s, len);
611 
612 	if (inet_pton(AF_INET6, buffer, &addr_ipv6) == 1) {
613 		sfree(buffer);
614 		/*
615 		 * IPv4-compatible IPv6 address and IPv4-mapped
616 		 * IPv6 addresses not allowed by rfc2307bis
617 		 */
618 		if (IN6_IS_ADDR_V4COMPAT(&addr_ipv6))
619 			return (0);
620 		if (IN6_IS_ADDR_V4MAPPED(&addr_ipv6))
621 			return (0);
622 		if (newaddr == 0)
623 			return (AF_INET6);
624 		if ((*newaddr = am(myself, INET6_ADDRSTRLEN)) == 0)
625 			return (-2);
626 		if (inet_ntop(AF_INET6, &addr_ipv6, *newaddr, INET6_ADDRSTRLEN))
627 			return (AF_INET6);
628 		sfree(*newaddr);
629 		return (-2);
630 	}
631 
632 	if (inet_pton(AF_INET, buffer, &addr_ipv4) == 1) {
633 		sfree(buffer);
634 		if (newaddr == 0)
635 			return (AF_INET);
636 		if ((*newaddr = am(myself, INET_ADDRSTRLEN)) == 0)
637 			return (-2);
638 		if (inet_ntop(AF_INET, &addr_ipv4, *newaddr, INET_ADDRSTRLEN))
639 			return (AF_INET);
640 		sfree(*newaddr);
641 		return (-2);
642 	}
643 
644 	sfree(buffer);
645 	return (-1);
646 }
647 
648 int
649 sstrncmp(const char *s1, const char *s2, int n) {
650 	if (s1 == 0 && s2 == 0)
651 		return (0);
652 
653 	if (s1 == 0)
654 		return (1);
655 
656 	if (s2 == 0)
657 		return (-1);
658 
659 	return (strncmp(s1, s2, n));
660 }
661 
662 /*
663  * Does the following:
664  * - Trims leading and trailing whitespaces
665  * - Collapses two or more whitespaces into one space
666  * - Converts all whitespaces into spaces
667  * - At entrance, *len contains length of str
668  * - At exit, *len will contain length of the return string
669  * - In case of mem alloc failure, *len should be ignored
670  */
671 char *
672 trimWhiteSpaces(char *str, int *len, int deallocate) {
673 	char	*ostr;
674 	int	olen = 0;
675 	int	first = 1, i;
676 	char	*myself = "trimWhiteSpaces";
677 
678 	if ((ostr = am(myself, *len + 1)) == 0) {
679 		if (deallocate)
680 			sfree(str);
681 		*len = 0;
682 		return (0);
683 	}
684 
685 	/* Skip leading whitespaces */
686 	for (i = 0; i < *len && (str[i] == ' ' || str[i] == '\t'); i++);
687 
688 	/* Collapse multiple whitespaces into one */
689 	for (; i < *len; i++) {
690 		if (str[i] == ' ' || str[i] == '\t') {
691 			if (first) {
692 				first = 0;
693 				ostr[olen++] = ' ';
694 			}
695 			continue;
696 		}
697 		first = 1;
698 		ostr[olen++] = str[i];
699 	}
700 
701 	/* Handle the trailing whitespace if any */
702 	if (olen && ostr[olen - 1] == ' ') {
703 			olen--;
704 			ostr[olen] = 0;
705 	}
706 
707 	if (deallocate)
708 			sfree(str);
709 
710 	*len = olen;
711 	return (ostr);
712 }
713 
714 /*
715  * Escapes special characters in DN using the list from RFC 2253
716  */
717 int
718 escapeSpecialChars(__nis_value_t *val) {
719 	int	i, j, k, count;
720 	char	*newval, *s;
721 	char	*myself = "escapeSpecialChars";
722 
723 	/* Assume val is always non NULL */
724 
725 	for (i = 0; i < val->numVals; i++) {
726 		/*
727 		 * Count the special characters in value to determine
728 		 * the length for the new value
729 		 */
730 		s = val->val[i].value;
731 		for (j = 0, count = 0; j < val->val[i].length; j++, s++) {
732 			if (*s == '#' || *s == ',' || *s == '+' || *s == '"' ||
733 			*s == '\\' || *s == '<' || *s == '>' || *s == ';')
734 				count++;
735 		}
736 		if (count == 0)
737 			continue;
738 
739 		if ((newval = am(myself, val->val[i].length + count + 1)) == 0)
740 			return (-1);
741 
742 		/* Escape the special characters using '\\' */
743 		s = val->val[i].value;
744 		for (j = 0, k = 0; j < val->val[i].length; j++, k++, s++) {
745 			if (*s == '#' || *s == ',' || *s == '+' || *s == '"' ||
746 			*s == '\\' || *s == '<' || *s == '>' || *s == ';')
747 				newval[k++] = '\\';
748 			newval[k] = *s;
749 		}
750 
751 		sfree(val->val[i].value);
752 		val->val[i].value = newval;
753 		val->val[i].length += count;
754 	}
755 
756 	return (1);
757 }
758 
759 /*
760  * Remove escape characters from DN returned by LDAP server
761  */
762 void
763 removeEscapeChars(__nis_value_t *val) {
764 	int	i;
765 	char	*s, *d, *end;
766 
767 
768 	for (i = 0; i < val->numVals; i++) {
769 		s = val->val[i].value;
770 		end = s + val->val[i].length;
771 
772 		/*
773 		 * This function is called frequently and for most entries
774 		 * there will be no escapes. Process rapidly up to first escape.
775 		 */
776 		for (d = s; s < end;  s++, d++) {
777 			if (*s == '\\')
778 				break;
779 		}
780 
781 		/*
782 		 * Reached the end, in which case will not go into loop,
783 		 * or found an escape and now have to start moving data.
784 		 */
785 		for (; s < end;  s++) {
786 			if (*s == '\\') {
787 				val->val[i].length--;
788 				/*
789 				 * Next character gets coppied without being
790 				 * checked
791 				 */
792 				s++;
793 				if (s >= end)
794 					break;
795 			}
796 
797 			*d = *s;
798 			d++;
799 		}
800 	}
801 }
802