Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /**
2 util.c
5 Copyright (C) 2001-2003, Network Resonance, Inc.
6 Copyright (C) 2006, Network Resonance, Inc.
7 All Rights Reserved
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
13 1. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15 2. Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18 3. Neither the name of Network Resonance, Inc. nor the name of any
19 contributors to this software may be used to endorse or promote
20 products derived from this software without specific prior written
21 permission.
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 POSSIBILITY OF SUCH DAMAGE.
36 ekr@rtfm.com Wed Dec 26 17:19:36 2001
37 */
40 static char *RCSSTRING __UNUSED__ ="$Id: util.c,v 1.5 2007/11/21 00:09:13 adamcain Exp $";
42 #ifndef WIN32
43 #include <sys/uio.h>
44 #include <pwd.h>
45 #include <dirent.h>
46 #endif
47 #include <string.h>
48 #include <errno.h>
49 #include <ctype.h>
50 #include <sys/stat.h>
51 #ifdef OPENSSL
52 #include <openssl/evp.h>
53 #endif
54 #include "nr_common.h"
55 #include "r_common.h"
56 #include "registry.h"
57 #include "util.h"
58 #include "r_log.h"
60 int nr_util_default_log_facility=LOG_COMMON;
62 int nr_get_filename(base,name,namep)
63 char *base;
64 char *name;
65 char **namep;
66 {
67 int len=strlen(base)+strlen(name)+2;
68 char *ret=0;
69 int _status;
71 if(!(ret=(char *)RMALLOC(len)))
72 ABORT(R_NO_MEMORY);
73 if(base[strlen(base)-1]!='/'){
74 sprintf(ret,"%s/%s",base,name);
75 }
76 else{
77 sprintf(ret,"%s%s",base,name);
78 }
79 *namep=ret;
80 _status=0;
81 abort:
82 return(_status);
83 }
85 #if 0
86 int read_RSA_private_key(base,name,keyp)
87 char *base;
88 char *name;
89 RSA **keyp;
90 {
91 char *keyfile=0;
92 BIO *bio=0;
93 FILE *fp=0;
94 RSA *rsa=0;
95 int r,_status;
97 /* Load the keyfile */
98 if(r=get_filename(base,name,&keyfile))
99 ABORT(r);
100 if(!(fp=fopen(keyfile,"r")))
101 ABORT(R_NOT_FOUND);
102 if(!(bio=BIO_new(BIO_s_file())))
103 ABORT(R_NO_MEMORY);
104 BIO_set_fp(bio,fp,BIO_NOCLOSE);
106 if(!(rsa=PEM_read_bio_RSAPrivateKey(bio,0,0,0)))
107 ABORT(R_NOT_FOUND);
109 *keyp=rsa;
110 _status=0;
111 abort:
112 return(_status);
113 }
114 #endif
117 void nr_errprintf_log(const char *format,...)
118 {
119 va_list ap;
121 va_start(ap,format);
123 r_vlog(nr_util_default_log_facility,LOG_ERR,format,ap);
125 va_end(ap);
126 }
128 void nr_errprintf_log2(void *ignore, const char *format,...)
129 {
130 va_list ap;
132 va_start(ap,format);
134 r_vlog(nr_util_default_log_facility,LOG_ERR,format,ap);
136 va_end(ap);
137 }
140 int nr_fwrite_all(FILE *fp,UCHAR *buf,int len)
141 {
142 int r,_status;
144 while(len){
145 r=fwrite(buf,1,len,fp);
146 if(r==0)
147 ABORT(R_IO_ERROR);
149 len-=r;
150 buf+=r;
151 }
153 _status=0;
154 abort:
155 return(_status);
156 }
158 int nr_read_data(fd,buf,len)
159 int fd;
160 char *buf;
161 int len;
162 {
163 int r,_status;
165 while(len){
166 r=NR_SOCKET_READ(fd,buf,len);
167 if(r<=0)
168 ABORT(R_EOD);
170 buf+=r;
171 len-=r;
172 }
175 _status=0;
176 abort:
177 return(_status);
178 }
180 #ifdef WIN32
181 // TODO
182 #else
183 int nr_drop_privileges(char *username)
184 {
185 int _status;
187 /* Drop privileges */
188 if ((getuid() == 0) || geteuid()==0) {
189 struct passwd *passwd;
191 if ((passwd = getpwnam(CAPTURE_USER)) == 0){
192 r_log(LOG_GENERIC,LOG_EMERG,"Couldn't get user %s",CAPTURE_USER);
193 ABORT(R_INTERNAL);
194 }
196 if(setuid(passwd->pw_uid)!=0){
197 r_log(LOG_GENERIC,LOG_EMERG,"Couldn't drop privileges");
198 ABORT(R_INTERNAL);
199 }
200 }
202 _status=0;
203 abort:
204 return(_status);
205 }
206 #endif
208 int nr_bin2hex(UCHAR *in,int len,UCHAR *out)
209 {
210 while(len){
211 sprintf((char*)out,"%.2x",in[0] & 0xff);
213 in+=1;
214 out+=2;
216 len--;
217 }
219 return(0);
220 }
222 int nr_hex_ascii_dump(Data *data)
223 {
224 UCHAR *ptr=data->data;
225 int len=data->len;
227 while(len){
228 int i;
229 int bytes=MIN(len,16);
231 for(i=0;i<bytes;i++)
232 printf("%.2x ",ptr[i]&255);
233 /* Fill */
234 for(i=0;i<(16-bytes);i++)
235 printf(" ");
236 printf(" ");
238 for(i=0;i<bytes;i++){
239 if(isprint(ptr[i]))
240 printf("%c",ptr[i]);
241 else
242 printf(".");
243 }
244 printf("\n");
246 len-=bytes;
247 ptr+=bytes;
248 }
249 return(0);
250 }
252 #ifdef OPENSSL
253 int nr_sha1_file(char *filename,UCHAR *out)
254 {
255 EVP_MD_CTX md_ctx;
256 FILE *fp=0;
257 int r,_status;
258 UCHAR buf[1024];
259 int out_len;
261 EVP_MD_CTX_init(&md_ctx);
263 if(!(fp=fopen(filename,"r"))){
264 r_log(LOG_COMMON,LOG_ERR,"Couldn't open file %s",filename);
265 ABORT(R_NOT_FOUND);
266 }
268 EVP_DigestInit_ex(&md_ctx,EVP_sha1(),0);
270 while(1){
271 r=fread(buf,1,sizeof(buf),fp);
273 if(r<0){
274 r_log(LOG_COMMON,LOG_ERR,"Error reading from %s",filename);
275 ABORT(R_INTERNAL);
276 }
278 if(!r)
279 break;
281 EVP_DigestUpdate(&md_ctx,buf,r);
282 }
284 EVP_DigestFinal(&md_ctx,out,(unsigned int*)&out_len);
285 if(out_len!=20)
286 ABORT(R_INTERNAL);
288 _status=0;
289 abort:
290 EVP_MD_CTX_cleanup(&md_ctx);
291 if(fp) fclose(fp);
293 return(_status);
294 }
296 #endif
298 #ifdef WIN32
299 // TODO
300 #else
302 #if 0
304 #include <fts.h>
306 int nr_rm_tree(char *path)
307 {
308 FTS *fts=0;
309 FTSENT *p;
310 int failed=0;
311 int _status;
312 char *argv[2];
314 argv[0]=path;
315 argv[1]=0;
317 if(!(fts=fts_open(argv,0,NULL))){
318 r_log_e(LOG_COMMON,LOG_ERR,"Couldn't open directory %s",path);
319 ABORT(R_FAILED);
320 }
322 while(p=fts_read(fts)){
323 switch(p->fts_info){
324 case FTS_D:
325 break;
326 case FTS_DOT:
327 break;
328 case FTS_ERR:
329 r_log_e(LOG_COMMON,LOG_ERR,"Problem reading %s",p->fts_path);
330 break;
331 default:
332 r_log(LOG_COMMON,LOG_DEBUG,"Removing %s",p->fts_path);
333 errno=0;
334 if(remove(p->fts_path)){
335 r_log_e(LOG_COMMON,LOG_ERR,"Problem removing %s",p->fts_path);
336 failed=1;
337 }
338 }
339 }
341 if(failed)
342 ABORT(R_FAILED);
344 _status=0;
345 abort:
346 if(fts) fts_close(fts);
347 return(_status);
348 }
349 #endif
351 int nr_write_pid_file(char *pid_filename)
352 {
353 FILE *fp;
354 int _status;
356 if(!pid_filename)
357 ABORT(R_BAD_ARGS);
359 unlink(pid_filename);
361 if(!(fp=fopen(pid_filename,"w"))){
362 r_log(LOG_GENERIC,LOG_CRIT,"Couldn't open PID file: %s",strerror(errno));
363 ABORT(R_NOT_FOUND);
364 }
366 fprintf(fp,"%d\n",getpid());
368 fclose(fp);
370 chmod(pid_filename,S_IRUSR | S_IRGRP | S_IROTH);
372 _status=0;
373 abort:
374 return(_status);
375 }
376 #endif
378 int nr_reg_uint4_fetch_and_check(NR_registry key, UINT4 min, UINT4 max, int log_fac, int die, UINT4 *val)
379 {
380 int r,_status;
381 UINT4 my_val;
383 if(r=NR_reg_get_uint4(key,&my_val)){
384 r_log(log_fac,LOG_ERR,"Couldn't get key '%s', error %d",key,r);
385 ABORT(r);
386 }
388 if((min>0) && (my_val<min)){
389 r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%lu, (min = %lu)",key,(unsigned long)my_val,(unsigned long)min);
390 ABORT(R_BAD_DATA);
391 }
393 if(my_val>max){
394 r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%lu, (max = %lu)",key,(unsigned long)my_val,(unsigned long)max);
395 ABORT(R_BAD_DATA);
396 }
398 *val=my_val;
399 _status=0;
401 abort:
402 if(die && _status){
403 r_log(log_fac,LOG_CRIT,"Exiting due to invalid configuration (key '%s')",key);
404 exit(1);
405 }
406 return(_status);
407 }
409 int nr_reg_uint8_fetch_and_check(NR_registry key, UINT8 min, UINT8 max, int log_fac, int die, UINT8 *val)
410 {
411 int r,_status;
412 UINT8 my_val;
414 if(r=NR_reg_get_uint8(key,&my_val)){
415 r_log(log_fac,LOG_ERR,"Couldn't get key '%s', error %d",key,r);
416 ABORT(r);
417 }
419 if(my_val<min){
420 r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%llu, (min = %llu)",key,my_val,min);
421 ABORT(R_BAD_DATA);
422 }
424 if(my_val>max){
425 r_log(log_fac,LOG_ERR,"Invalid value for key '%s'=%llu, (max = %llu)",key,my_val,max);
426 ABORT(R_BAD_DATA);
427 }
429 *val=my_val;
430 _status=0;
432 abort:
433 if(die && _status){
434 r_log(log_fac,LOG_CRIT,"Exiting due to invalid configuration (key '%s')",key);
435 exit(1);
436 }
437 return(_status);
438 }
440 #if defined(LINUX) || defined(WIN32)
441 /*-
442 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
443 * All rights reserved.
444 *
445 * Redistribution and use in source and binary forms, with or without
446 * modification, are permitted provided that the following conditions
447 * are met:
448 * 1. Redistributions of source code must retain the above copyright
449 * notice, this list of conditions and the following disclaimer.
450 * 2. Redistributions in binary form must reproduce the above copyright
451 * notice, this list of conditions and the following disclaimer in the
452 * documentation and/or other materials provided with the distribution.
453 * 3. The name of the author may not be used to endorse or promote products
454 * derived from this software without specific prior written permission.
455 *
456 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
457 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
458 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
459 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
460 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
461 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
462 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
463 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
464 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
465 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
466 */
469 /*
470 * Appends src to string dst of size siz (unlike strncat, siz is the
471 * full size of dst, not space left). At most siz-1 characters
472 * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
473 * Returns strlen(src) + MIN(siz, strlen(initial dst)).
474 * If retval >= siz, truncation occurred.
475 */
476 size_t
477 strlcat(dst, src, siz)
478 char *dst;
479 const char *src;
480 size_t siz;
481 {
482 char *d = dst;
483 const char *s = src;
484 size_t n = siz;
485 size_t dlen;
487 /* Find the end of dst and adjust bytes left but don't go past end */
488 while (n-- != 0 && *d != '\0')
489 d++;
490 dlen = d - dst;
491 n = siz - dlen;
493 if (n == 0)
494 return(dlen + strlen(s));
495 while (*s != '\0') {
496 if (n != 1) {
497 *d++ = *s;
498 n--;
499 }
500 s++;
501 }
502 *d = '\0';
504 return(dlen + (s - src)); /* count does not include NUL */
505 }
507 #endif /* LINUX or WIN32 */
509 #if defined(USE_OWN_INET_NTOP) || defined(WIN32)
510 #include <errno.h>
511 #ifdef WIN32
512 #include <Ws2ipdef.h>
513 #ifndef EAFNOSUPPORT
514 #define EAFNOSUPPORT WSAEAFNOSUPPORT
515 #endif
516 #else
517 #include <sys/socket.h>
518 #endif
519 #define INET6
521 /* inet_ntop implementation from NetBSD */
523 /*
524 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
525 * Copyright (c) 1996-1999 by Internet Software Consortium.
526 *
527 * Permission to use, copy, modify, and distribute this software for any
528 * purpose with or without fee is hereby granted, provided that the above
529 * copyright notice and this permission notice appear in all copies.
530 *
531 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
532 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
533 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
534 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
535 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
536 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
537 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
538 */
540 #if !defined(NS_INADDRSZ)
541 # define NS_INADDRSZ 4
542 #endif
543 #if !defined(NS_IN6ADDRSZ)
544 # define NS_IN6ADDRSZ 16
545 #endif
546 #if !defined(NS_INT16SZ)
547 # define NS_INT16SZ 2
548 #endif
550 /*
551 * WARNING: Don't even consider trying to compile this on a system where
552 * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
553 */
555 static const char *inet_ntop4(const unsigned char *src, char *dst, size_t size);
556 #ifdef INET6
557 static const char *inet_ntop6(const unsigned char *src, char *dst, size_t size);
558 #endif /* INET6 */
560 /* char *
561 * inet_ntop(af, src, dst, size)
562 * convert a network format address to presentation format.
563 * return:
564 * pointer to presentation format address (`dst'), or NULL (see errno).
565 * author:
566 * Paul Vixie, 1996.
567 */
568 const char *
569 inet_ntop(int af, const void *src, char *dst, size_t size)
570 {
572 switch (af) {
573 case AF_INET:
574 return (inet_ntop4(src, dst, size));
575 #ifdef INET6
576 case AF_INET6:
577 return (inet_ntop6(src, dst, size));
578 #endif /* INET6 */
579 default:
580 errno = EAFNOSUPPORT;
581 return (NULL);
582 }
583 /* NOTREACHED */
584 }
586 /* const char *
587 * inet_ntop4(src, dst, size)
588 * format an IPv4 address, more or less like inet_ntoa()
589 * return:
590 * `dst' (as a const)
591 * notes:
592 * (1) uses no statics
593 * (2) takes a unsigned char* not an in_addr as input
594 * author:
595 * Paul Vixie, 1996.
596 */
597 static const char *
598 inet_ntop4(const unsigned char *src, char *dst, size_t size)
599 {
600 char tmp[sizeof "255.255.255.255"];
601 int l;
603 l = snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u",
604 src[0], src[1], src[2], src[3]);
605 if (l <= 0 || (size_t) l >= size) {
606 errno = ENOSPC;
607 return (NULL);
608 }
609 strlcpy(dst, tmp, size);
610 return (dst);
611 }
613 #ifdef INET6
614 /* const char *
615 * inet_ntop6(src, dst, size)
616 * convert IPv6 binary address into presentation (printable) format
617 * author:
618 * Paul Vixie, 1996.
619 */
620 static const char *
621 inet_ntop6(const unsigned char *src, char *dst, size_t size)
622 {
623 /*
624 * Note that int32_t and int16_t need only be "at least" large enough
625 * to contain a value of the specified size. On some systems, like
626 * Crays, there is no such thing as an integer variable with 16 bits.
627 * Keep this in mind if you think this function should have been coded
628 * to use pointer overlays. All the world's not a VAX.
629 */
630 char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
631 char *tp, *ep;
632 struct { int base, len; } best, cur;
633 unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ];
634 int i;
635 int advance;
637 /*
638 * Preprocess:
639 * Copy the input (bytewise) array into a wordwise array.
640 * Find the longest run of 0x00's in src[] for :: shorthanding.
641 */
642 memset(words, '\0', sizeof words);
643 for (i = 0; i < NS_IN6ADDRSZ; i++)
644 words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
645 best.base = -1;
646 cur.base = -1;
647 best.len = -1; /* XXX gcc */
648 cur.len = -1; /* XXX gcc */
649 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
650 if (words[i] == 0) {
651 if (cur.base == -1)
652 cur.base = i, cur.len = 1;
653 else
654 cur.len++;
655 } else {
656 if (cur.base != -1) {
657 if (best.base == -1 || cur.len > best.len)
658 best = cur;
659 cur.base = -1;
660 }
661 }
662 }
663 if (cur.base != -1) {
664 if (best.base == -1 || cur.len > best.len)
665 best = cur;
666 }
667 if (best.base != -1 && best.len < 2)
668 best.base = -1;
670 /*
671 * Format the result.
672 */
673 tp = tmp;
674 ep = tmp + sizeof(tmp);
675 for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
676 /* Are we inside the best run of 0x00's? */
677 if (best.base != -1 && i >= best.base &&
678 i < (best.base + best.len)) {
679 if (i == best.base)
680 *tp++ = ':';
681 continue;
682 }
683 /* Are we following an initial run of 0x00s or any real hex? */
684 if (i != 0) {
685 if (tp + 1 >= ep)
686 return (NULL);
687 *tp++ = ':';
688 }
689 /* Is this address an encapsulated IPv4? */
690 if (i == 6 && best.base == 0 &&
691 (best.len == 6 ||
692 (best.len == 7 && words[7] != 0x0001) ||
693 (best.len == 5 && words[5] == 0xffff))) {
694 if (!inet_ntop4(src+12, tp, (size_t)(ep - tp)))
695 return (NULL);
696 tp += strlen(tp);
697 break;
698 }
699 advance = snprintf(tp, (size_t)(ep - tp), "%x", words[i]);
700 if (advance <= 0 || advance >= ep - tp)
701 return (NULL);
702 tp += advance;
703 }
704 /* Was it a trailing run of 0x00's? */
705 if (best.base != -1 && (best.base + best.len) ==
706 (NS_IN6ADDRSZ / NS_INT16SZ)) {
707 if (tp + 1 >= ep)
708 return (NULL);
709 *tp++ = ':';
710 }
711 if (tp + 1 >= ep)
712 return (NULL);
713 *tp++ = '\0';
715 /*
716 * Check for overflow, copy, and we're done.
717 */
718 if ((size_t)(tp - tmp) > size) {
719 errno = ENOSPC;
720 return (NULL);
721 }
722 strlcpy(dst, tmp, size);
723 return (dst);
724 }
725 #endif /* INET6 */
727 #endif
729 #ifdef WIN32
730 #include <time.h>
731 /* this is only millisecond-accurate, but that should be OK */
733 int gettimeofday(struct timeval *tv, void *tz)
734 {
735 SYSTEMTIME st;
736 FILETIME ft;
737 ULARGE_INTEGER u;
739 GetLocalTime (&st);
741 /* strangely, the FILETIME is the number of 100 nanosecond (0.1 us) intervals
742 * since the Epoch */
743 SystemTimeToFileTime(&st, &ft);
744 u.HighPart = ft.dwHighDateTime;
745 u.LowPart = ft.dwLowDateTime;
747 tv->tv_sec = (long) (u.QuadPart / 10000000L);
748 tv->tv_usec = (long) (st.wMilliseconds * 1000);;
750 return 0;
751 }
753 int snprintf(char *buffer, size_t n, const char *format, ...)
754 {
755 va_list argp;
756 int ret;
757 va_start(argp, format);
758 ret = _vscprintf(format, argp);
759 vsnprintf_s(buffer, n, _TRUNCATE, format, argp);
760 va_end(argp);
761 return ret;
762 }
764 #endif