Support retrieving client information for clients other than the one associated with...
[freeradius.git] / src / lib / misc.c
1 /*
2  * misc.c       Various miscellaneous functions.
3  *
4  * Version:     $Id$
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This library is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  */
22
23 RCSID("$Id$")
24
25 #include        <freeradius-devel/libradius.h>
26
27 #include        <ctype.h>
28 #include        <sys/file.h>
29 #include        <fcntl.h>
30 #include        <signal.h>
31
32 #define FR_PUT_LE16(a, val)\
33         do {\
34                 a[1] = ((uint16_t) (val)) >> 8;\
35                 a[0] = ((uint16_t) (val)) & 0xff;\
36         } while (0)
37
38 static int      fr_debugger_present = -1;
39
40 int     fr_dns_lookups = 0;
41 int     fr_debug_flag = 0;
42
43 /** Allocates a new talloc context from the root autofree context
44  *
45  * @param signum signal raised.
46  */
47 static void _sigtrap_handler(UNUSED int signum)
48 {
49     fr_debugger_present = 0;
50     signal(SIGTRAP, SIG_DFL);
51 }
52
53 /** Break in GDB (if were running under GDB)
54  *
55  * If the server is running under GDB this will raise a SIGTRAP which
56  * will pause the running process.
57  *
58  * If the server is not running under GDB then this will do nothing.
59  */
60 void fr_debug_break(void)
61 {
62     if (fr_debugger_present == -1) {
63         fr_debugger_present = 0;
64         signal(SIGTRAP, _sigtrap_handler);
65         raise(SIGTRAP);
66     } else if (fr_debugger_present == 1) {
67         raise(SIGTRAP);
68     }
69 }
70
71 /*
72  *      Return an IP address in standard dot notation
73  *
74  *      FIXME: DELETE THIS
75  */
76 char const *ip_ntoa(char *buffer, uint32_t ipaddr)
77 {
78         ipaddr = ntohl(ipaddr);
79
80         sprintf(buffer, "%d.%d.%d.%d",
81                 (ipaddr >> 24) & 0xff,
82                 (ipaddr >> 16) & 0xff,
83                 (ipaddr >>  8) & 0xff,
84                 (ipaddr      ) & 0xff);
85         return buffer;
86 }
87
88 /*
89  *      Internal wrapper for locking, to minimize the number of ifdef's
90  *
91  *      Use fcntl or error
92  */
93 int rad_lockfd(int fd, int lock_len)
94 {
95 #ifdef F_WRLCK
96         struct flock fl;
97
98         fl.l_start = 0;
99         fl.l_len = lock_len;
100         fl.l_pid = getpid();
101         fl.l_type = F_WRLCK;
102         fl.l_whence = SEEK_CUR;
103
104         return fcntl(fd, F_SETLKW, (void *)&fl);
105 #else
106 #error "missing definition for F_WRLCK, all file locks will fail"
107
108         return -1;
109 #endif
110 }
111
112 /*
113  *      Internal wrapper for locking, to minimize the number of ifdef's
114  *
115  *      Lock an fd, prefer lockf() over flock()
116  *      Nonblocking version.
117  */
118 int rad_lockfd_nonblock(int fd, int lock_len)
119 {
120 #ifdef F_WRLCK
121         struct flock fl;
122
123         fl.l_start = 0;
124         fl.l_len = lock_len;
125         fl.l_pid = getpid();
126         fl.l_type = F_WRLCK;
127         fl.l_whence = SEEK_CUR;
128
129         return fcntl(fd, F_SETLK, (void *)&fl);
130 #else
131 #error "missing definition for F_WRLCK, all file locks will fail"
132
133         return -1;
134 #endif
135 }
136
137 /*
138  *      Internal wrapper for unlocking, to minimize the number of ifdef's
139  *      in the source.
140  *
141  *      Unlock an fd, prefer lockf() over flock()
142  */
143 int rad_unlockfd(int fd, int lock_len)
144 {
145 #ifdef F_WRLCK
146         struct flock fl;
147
148         fl.l_start = 0;
149         fl.l_len = lock_len;
150         fl.l_pid = getpid();
151         fl.l_type = F_WRLCK;
152         fl.l_whence = SEEK_CUR;
153
154         return fcntl(fd, F_UNLCK, (void *)&fl);
155 #else
156 #error "missing definition for F_WRLCK, all file locks will fail"
157
158         return -1;
159 #endif
160 }
161
162 /*
163  *      Return an interface-id in standard colon notation
164  */
165 char *ifid_ntoa(char *buffer, size_t size, uint8_t const *ifid)
166 {
167         snprintf(buffer, size, "%x:%x:%x:%x",
168                  (ifid[0] << 8) + ifid[1], (ifid[2] << 8) + ifid[3],
169                  (ifid[4] << 8) + ifid[5], (ifid[6] << 8) + ifid[7]);
170         return buffer;
171 }
172
173
174 /*
175  *      Return an interface-id from
176  *      one supplied in standard colon notation.
177  */
178 uint8_t *ifid_aton(char const *ifid_str, uint8_t *ifid)
179 {
180         static char const xdigits[] = "0123456789abcdef";
181         char const *p, *pch;
182         int num_id = 0, val = 0, idx = 0;
183
184         for (p = ifid_str; ; ++p) {
185                 if (*p == ':' || *p == '\0') {
186                         if (num_id <= 0)
187                                 return NULL;
188
189                         /*
190                          *      Drop 'val' into the array.
191                          */
192                         ifid[idx] = (val >> 8) & 0xff;
193                         ifid[idx + 1] = val & 0xff;
194                         if (*p == '\0') {
195                                 /*
196                                  *      Must have all entries before
197                                  *      end of the string.
198                                  */
199                                 if (idx != 6)
200                                         return NULL;
201                                 break;
202                         }
203                         val = 0;
204                         num_id = 0;
205                         if ((idx += 2) > 6)
206                                 return NULL;
207                 } else if ((pch = strchr(xdigits, tolower(*p))) != NULL) {
208                         if (++num_id > 4)
209                                 return NULL;
210                         /*
211                          *      Dumb version of 'scanf'
212                          */
213                         val <<= 4;
214                         val |= (pch - xdigits);
215                 } else
216                         return NULL;
217         }
218         return ifid;
219 }
220
221
222 #ifndef HAVE_INET_PTON
223 static int inet_pton4(char const *src, struct in_addr *dst)
224 {
225         int octet;
226         unsigned int num;
227         char const *p, *off;
228         uint8_t tmp[4];
229         static char const digits[] = "0123456789";
230
231         octet = 0;
232         p = src;
233         while (1) {
234                 num = 0;
235                 while (*p && ((off = strchr(digits, *p)) != NULL)) {
236                         num *= 10;
237                         num += (off - digits);
238
239                         if (num > 255) return 0;
240
241                         p++;
242                 }
243                 if (!*p) break;
244
245                 /*
246                  *      Not a digit, MUST be a dot, else we
247                  *      die.
248                  */
249                 if (*p != '.') {
250                         return 0;
251                 }
252
253                 tmp[octet++] = num;
254                 p++;
255         }
256
257         /*
258          *      End of the string.  At the fourth
259          *      octet is OK, anything else is an
260          *      error.
261          */
262         if (octet != 3) {
263                 return 0;
264         }
265         tmp[3] = num;
266
267         memcpy(dst, &tmp, sizeof(tmp));
268         return 1;
269 }
270
271
272 #ifdef HAVE_STRUCT_SOCKADDR_IN6
273 /* int
274  * inet_pton6(src, dst)
275  *      convert presentation level address to network order binary form.
276  * return:
277  *      1 if `src' is a valid [RFC1884 2.2] address, else 0.
278  * notice:
279  *      (1) does not touch `dst' unless it's returning 1.
280  *      (2) :: in a full address is silently ignored.
281  * credit:
282  *      inspired by Mark Andrews.
283  * author:
284  *      Paul Vixie, 1996.
285  */
286 static int inet_pton6(char const *src, unsigned char *dst)
287 {
288         static char const xdigits_l[] = "0123456789abcdef",
289                           xdigits_u[] = "0123456789ABCDEF";
290         u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
291         char const *xdigits, *curtok;
292         int ch, saw_xdigit;
293         u_int val;
294
295         memset((tp = tmp), 0, IN6ADDRSZ);
296         endp = tp + IN6ADDRSZ;
297         colonp = NULL;
298         /* Leading :: requires some special handling. */
299         if (*src == ':')
300                 if (*++src != ':')
301                         return (0);
302         curtok = src;
303         saw_xdigit = 0;
304         val = 0;
305         while ((ch = *src++) != '\0') {
306                 char const *pch;
307
308                 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
309                         pch = strchr((xdigits = xdigits_u), ch);
310                 if (pch != NULL) {
311                         val <<= 4;
312                         val |= (pch - xdigits);
313                         if (val > 0xffff)
314                                 return (0);
315                         saw_xdigit = 1;
316                         continue;
317                 }
318                 if (ch == ':') {
319                         curtok = src;
320                         if (!saw_xdigit) {
321                                 if (colonp)
322                                         return (0);
323                                 colonp = tp;
324                                 continue;
325                         }
326                         if (tp + INT16SZ > endp)
327                                 return (0);
328                         *tp++ = (u_char) (val >> 8) & 0xff;
329                         *tp++ = (u_char) val & 0xff;
330                         saw_xdigit = 0;
331                         val = 0;
332                         continue;
333                 }
334                 if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
335                     inet_pton4(curtok, (struct in_addr *) tp) > 0) {
336                         tp += INADDRSZ;
337                         saw_xdigit = 0;
338                         break;  /* '\0' was seen by inet_pton4(). */
339                 }
340                 return (0);
341         }
342         if (saw_xdigit) {
343                 if (tp + INT16SZ > endp)
344                         return (0);
345                 *tp++ = (u_char) (val >> 8) & 0xff;
346                 *tp++ = (u_char) val & 0xff;
347         }
348         if (colonp != NULL) {
349                 /*
350                  * Since some memmove()'s erroneously fail to handle
351                  * overlapping regions, we'll do the shift by hand.
352                  */
353                 int const n = tp - colonp;
354                 int i;
355
356                 for (i = 1; i <= n; i++) {
357                         endp[- i] = colonp[n - i];
358                         colonp[n - i] = 0;
359                 }
360                 tp = endp;
361         }
362         if (tp != endp)
363                 return (0);
364         /* bcopy(tmp, dst, IN6ADDRSZ); */
365         memcpy(dst, tmp, IN6ADDRSZ);
366         return (1);
367 }
368 #endif
369
370 /*
371  *      Utility function, so that the rest of the server doesn't
372  *      have ifdef's around IPv6 support
373  */
374 int inet_pton(int af, char const *src, void *dst)
375 {
376         if (af == AF_INET) {
377                 return inet_pton4(src, dst);
378         }
379 #ifdef HAVE_STRUCT_SOCKADDR_IN6
380
381         if (af == AF_INET6) {
382                 return inet_pton6(src, dst);
383         }
384 #endif
385
386         return -1;
387 }
388 #endif
389
390 #ifndef HAVE_INET_NTOP
391 /*
392  *      Utility function, so that the rest of the server doesn't
393  *      have ifdef's around IPv6 support
394  */
395 char const *inet_ntop(int af, void const *src, char *dst, size_t cnt)
396 {
397         if (af == AF_INET) {
398                 uint8_t const *ipaddr = src;
399
400                 if (cnt <= INET_ADDRSTRLEN) return NULL;
401
402                 snprintf(dst, cnt, "%d.%d.%d.%d",
403                          ipaddr[0], ipaddr[1],
404                          ipaddr[2], ipaddr[3]);
405                 return dst;
406         }
407
408         /*
409          *      If the system doesn't define this, we define it
410          *      in missing.h
411          */
412         if (af == AF_INET6) {
413                 struct const in6_addr *ipaddr = src;
414
415                 if (cnt <= INET6_ADDRSTRLEN) return NULL;
416
417                 snprintf(dst, cnt, "%x:%x:%x:%x:%x:%x:%x:%x",
418                          (ipaddr->s6_addr[0] << 8) | ipaddr->s6_addr[1],
419                          (ipaddr->s6_addr[2] << 8) | ipaddr->s6_addr[3],
420                          (ipaddr->s6_addr[4] << 8) | ipaddr->s6_addr[5],
421                          (ipaddr->s6_addr[6] << 8) | ipaddr->s6_addr[7],
422                          (ipaddr->s6_addr[8] << 8) | ipaddr->s6_addr[9],
423                          (ipaddr->s6_addr[10] << 8) | ipaddr->s6_addr[11],
424                          (ipaddr->s6_addr[12] << 8) | ipaddr->s6_addr[13],
425                          (ipaddr->s6_addr[14] << 8) | ipaddr->s6_addr[15]);
426                 return dst;
427         }
428
429         return NULL;            /* don't support IPv6 */
430 }
431 #endif
432
433
434 /*
435  *      Try to convert the address to v4 then v6
436  */
437 int ip_ptonx(char const *src, fr_ipaddr_t *dst)
438 {
439         if (inet_pton(AF_INET, src, &dst->ipaddr.ip4addr) == 1) {
440                 dst->af = AF_INET;
441                 return 1;
442         }
443
444 #ifdef HAVE_STRUCT_SOCKADDR_IN6
445         if (inet_pton(AF_INET6, src, &dst->ipaddr.ip6addr) == 1) {
446                 dst->af = AF_INET6;
447                 return 1;
448         }
449 #endif
450
451         return 0;
452 }
453
454 /*
455  *      Wrappers for IPv4/IPv6 host to IP address lookup.
456  *      This API returns only one IP address, of the specified
457  *      address family, or the first address (of whatever family),
458  *      if AF_UNSPEC is used.
459  */
460 int ip_hton(char const *src, int af, fr_ipaddr_t *dst)
461 {
462         int rcode;
463         struct addrinfo hints, *ai = NULL, *res = NULL;
464
465         memset(&hints, 0, sizeof(hints));
466         hints.ai_family = af;
467
468 #ifdef TALLOC_DEBUG
469         /*
470          *      Avoid malloc for IP addresses.  This helps us debug
471          *      memory errors when using talloc.
472          */
473         if (af == AF_INET) {
474                 /*
475                  *      If it's all numeric, avoid getaddrinfo()
476                  */
477                 if (inet_pton(af, src, &dst->ipaddr.ip4addr) == 1) {
478                         return 0;
479                 }
480         }
481 #endif
482
483         if ((rcode = getaddrinfo(src, NULL, &hints, &res)) != 0) {
484                 fr_strerror_printf("ip_hton: %s", gai_strerror(rcode));
485                 return -1;
486         }
487
488         for (ai = res; ai; ai = ai->ai_next) {
489                 if ((af == ai->ai_family) || (af == AF_UNSPEC))
490                         break;
491         }
492
493         if (!ai) {
494                 fr_strerror_printf("ip_hton failed to find requested information for host %.100s", src);
495                 freeaddrinfo(ai);
496                 return -1;
497         }
498
499         rcode = fr_sockaddr2ipaddr((struct sockaddr_storage *)ai->ai_addr,
500                                    ai->ai_addrlen, dst, NULL);
501         freeaddrinfo(ai);
502         if (!rcode) return -1;
503
504         return 0;
505 }
506
507 /*
508  *      Look IP addresses up, and print names (depending on DNS config)
509  */
510 char const *ip_ntoh(fr_ipaddr_t const *src, char *dst, size_t cnt)
511 {
512         struct sockaddr_storage ss;
513         int error;
514         socklen_t salen;
515
516         /*
517          *      No DNS lookups
518          */
519         if (!fr_dns_lookups) {
520                 return inet_ntop(src->af, &(src->ipaddr), dst, cnt);
521         }
522
523         if (!fr_ipaddr2sockaddr(src, 0, &ss, &salen)) {
524                 return NULL;
525         }
526
527         if ((error = getnameinfo((struct sockaddr *)&ss, salen, dst, cnt, NULL, 0,
528                                  NI_NUMERICHOST | NI_NUMERICSERV)) != 0) {
529                 fr_strerror_printf("ip_ntoh: %s", gai_strerror(error));
530                 return NULL;
531         }
532         return dst;
533 }
534
535
536 static char const *hextab = "0123456789abcdef";
537
538 /** Convert hex strings to binary data
539  *
540  * @param bin Buffer to write output to.
541  * @param hex input string.
542  * @param outlen length of output buffer (or length of input string / 2).
543  * @return length of data written to buffer.
544  */
545 size_t fr_hex2bin(uint8_t *bin, char const *hex, size_t outlen)
546 {
547         size_t i;
548         char *c1, *c2;
549
550         for (i = 0; i < outlen; i++) {
551                 if(!(c1 = memchr(hextab, tolower((int) hex[i << 1]), 16)) ||
552                    !(c2 = memchr(hextab, tolower((int) hex[(i << 1) + 1]), 16)))
553                         break;
554                  bin[i] = ((c1-hextab)<<4) + (c2-hextab);
555         }
556
557         return i;
558 }
559
560
561 /** Convert binary data to a hex string
562  *
563  * Ascii encoded hex string will not be prefixed with '0x'
564  *
565  * @warning If the output buffer isn't long enough, we have a buffer overflow.
566  *
567  * @param[out] hex Buffer to write hex output.
568  * @param[in] bin input.
569  * @param[in] inlen of bin input.
570  * @return length of data written to buffer.
571  */
572 size_t fr_bin2hex(char *hex, uint8_t const *bin, size_t inlen)
573 {
574         size_t i;
575
576         for (i = 0; i < inlen; i++) {
577                 hex[0] = hextab[((*bin) >> 4) & 0x0f];
578                 hex[1] = hextab[*bin & 0x0f];
579                 hex += 2;
580                 bin++;
581         }
582
583         *hex = '\0';
584         return inlen * 2;
585 }
586
587
588 /*
589  *      So we don't have ifdef's in the rest of the code
590  */
591 #ifndef HAVE_CLOSEFROM
592 int closefrom(int fd)
593 {
594         int i;
595         int maxfd = 256;
596
597 #ifdef _SC_OPEN_MAX
598         maxfd = sysconf(_SC_OPEN_MAX);
599         if (maxfd < 0) {
600           maxfd = 256;
601         }
602 #endif
603
604         if (fd > maxfd) return 0;
605
606         /*
607          *      FIXME: return EINTR?
608          *
609          *      Use F_CLOSEM?
610          */
611         for (i = fd; i < maxfd; i++) {
612                 close(i);
613         }
614
615         return 0;
616 }
617 #endif
618
619 int fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
620 {
621         if (a->af < b->af) return -1;
622         if (a->af > b->af) return +1;
623
624         switch (a->af) {
625         case AF_INET:
626                 return memcmp(&a->ipaddr.ip4addr,
627                               &b->ipaddr.ip4addr,
628                               sizeof(a->ipaddr.ip4addr));
629                 break;
630
631 #ifdef HAVE_STRUCT_SOCKADDR_IN6
632         case AF_INET6:
633                 if (a->scope < b->scope) return -1;
634                 if (a->scope > b->scope) return +1;
635
636                 return memcmp(&a->ipaddr.ip6addr,
637                               &b->ipaddr.ip6addr,
638                               sizeof(a->ipaddr.ip6addr));
639                 break;
640 #endif
641
642         default:
643                 break;
644         }
645
646         return -1;
647 }
648
649 int fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, int port,
650                        struct sockaddr_storage *sa, socklen_t *salen)
651 {
652         if (ipaddr->af == AF_INET) {
653                 struct sockaddr_in s4;
654
655                 *salen = sizeof(s4);
656
657                 memset(&s4, 0, sizeof(s4));
658                 s4.sin_family = AF_INET;
659                 s4.sin_addr = ipaddr->ipaddr.ip4addr;
660                 s4.sin_port = htons(port);
661                 memset(sa, 0, sizeof(*sa));
662                 memcpy(sa, &s4, sizeof(s4));
663
664 #ifdef HAVE_STRUCT_SOCKADDR_IN6
665         } else if (ipaddr->af == AF_INET6) {
666                 struct sockaddr_in6 s6;
667
668                 *salen = sizeof(s6);
669
670                 memset(&s6, 0, sizeof(s6));
671                 s6.sin6_family = AF_INET6;
672                 s6.sin6_addr = ipaddr->ipaddr.ip6addr;
673                 s6.sin6_port = htons(port);
674                 s6.sin6_scope_id = ipaddr->scope;
675                 memset(sa, 0, sizeof(*sa));
676                 memcpy(sa, &s6, sizeof(s6));
677 #endif
678         } else {
679                 return 0;
680         }
681
682         return 1;
683 }
684
685
686 int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
687                        fr_ipaddr_t *ipaddr, int *port)
688 {
689         if (sa->ss_family == AF_INET) {
690                 struct sockaddr_in      s4;
691
692                 if (salen < sizeof(s4)) {
693                         fr_strerror_printf("IPv4 address is too small");
694                         return 0;
695                 }
696
697                 memcpy(&s4, sa, sizeof(s4));
698                 ipaddr->af = AF_INET;
699                 ipaddr->ipaddr.ip4addr = s4.sin_addr;
700                 if (port) *port = ntohs(s4.sin_port);
701
702 #ifdef HAVE_STRUCT_SOCKADDR_IN6
703         } else if (sa->ss_family == AF_INET6) {
704                 struct sockaddr_in6     s6;
705
706                 if (salen < sizeof(s6)) {
707                         fr_strerror_printf("IPv6 address is too small");
708                         return 0;
709                 }
710
711                 memcpy(&s6, sa, sizeof(s6));
712                 ipaddr->af = AF_INET6;
713                 ipaddr->ipaddr.ip6addr = s6.sin6_addr;
714                 if (port) *port = ntohs(s6.sin6_port);
715                 ipaddr->scope = s6.sin6_scope_id;
716 #endif
717
718         } else {
719                 fr_strerror_printf("Unsupported address famility %d",
720                                    sa->ss_family);
721                 return 0;
722         }
723
724         return 1;
725 }
726
727 /** Convert UTF8 string to UCS2 encoding
728  *
729  * @note Borrowed from src/crypto/ms_funcs.c of wpa_supplicant project (http://hostap.epitest.fi/wpa_supplicant/)
730  *
731  * @param[out] out Where to write the ucs2 string.
732  * @param[in] outlen Size of output buffer.
733  * @param[in] in UTF8 string to convert.
734  * @param[in] inlen length of UTF8 string.
735  * @return the size of the UCS2 string written to the output buffer (in bytes).
736  */
737 ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen)
738 {
739         size_t i;
740         uint8_t *start = out;
741
742         for (i = 0; i < inlen; i++) {
743                 uint8_t c, c2, c3;
744
745                 c = in[i];
746                 if ((size_t)(out - start) >= outlen) {
747                         /* input too long */
748                         return -1;
749                 }
750
751                 /* One-byte encoding */
752                 if (c <= 0x7f) {
753                         FR_PUT_LE16(out, c);
754                         out += 2;
755                         continue;
756                 } else if ((i == (inlen - 1)) || ((size_t)(out - start) >= (outlen - 1))) {
757                         /* Incomplete surrogate */
758                         return -1;
759                 }
760
761                 c2 = in[++i];
762                 /* Two-byte encoding */
763                 if ((c & 0xe0) == 0xc0) {
764                         FR_PUT_LE16(out, ((c & 0x1f) << 6) | (c2 & 0x3f));
765                         out += 2;
766                         continue;
767                 }
768                 if ((i == inlen) || ((size_t)(out - start) >= (outlen - 1))) {
769                         /* Incomplete surrogate */
770                         return -1;
771                 }
772
773                 /* Three-byte encoding */
774                 c3 = in[++i];
775                 FR_PUT_LE16(out, ((c & 0xf) << 12) | ((c2 & 0x3f) << 6) | (c3 & 0x3f));
776                 out += 2;
777         }
778
779         return out - start;
780 }
781
782 #ifdef TALLOC_DEBUG
783 void fr_talloc_verify_cb(UNUSED const void *ptr, UNUSED int depth,
784                          UNUSED int max_depth, UNUSED int is_ref,
785                          UNUSED void *private_data)
786 {
787         /* do nothing */
788 }
789 #endif