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