1 /* helpers.c - Various helper functions
2 
3    Copyright (C) 2000, 2001 Thomas Moestl
4    Copyright (C) 2002, 2003, 2005, 2006, 2008, 2011 Paul A. Rombouts
5 
6   This file is part of the pdnsd package.
7 
8   pdnsd is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12 
13   pdnsd is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17 
18   You should have received a copy of the GNU General Public License
19   along with pdnsd; see the file COPYING. If not, see
20   <http://www.gnu.org/licenses/>.
21 */
22 
23 #include <config.h>
24 #include <sys/types.h>
25 #include <sys/time.h>
26 #include <signal.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <ctype.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <pwd.h>
34 #include <grp.h>
35 #include <errno.h>
36 #include "ipvers.h"
37 #include "thread.h"
38 #include "error.h"
39 #include "helpers.h"
40 #include "cache.h"
41 #include "conff.h"
42 
43 
44 /*
45  * This is to exit pdnsd from any thread.
46  */
pdnsd_exit()47 void pdnsd_exit()
48 {
49 	pthread_kill(main_thrid,SIGTERM);
50 	pthread_exit(NULL);
51 }
52 
53 /*
54  * Try to grab a mutex. If we can't, fail. This will loop until we get the
55  * mutex or fail. This is only used in debugging code or at exit, otherwise
56  * we might run into lock contention problems.
57  */
softlock_mutex(pthread_mutex_t * mutex)58 int softlock_mutex(pthread_mutex_t *mutex)
59 {
60 	unsigned int tr=0;
61 	while(pthread_mutex_trylock(mutex)) {
62 		if (++tr>=SOFTLOCK_MAXTRIES)
63 			return 0;
64 		usleep_r(10000);
65 	}
66 	return 1;
67 }
68 
69 /*
70  * setuid() and setgid() for a specified user.
71  */
run_as(const char * user)72 int run_as(const char *user)
73 {
74 	if (user[0]) {
75 #ifdef HAVE_GETPWNAM_R
76 		struct passwd pwdbuf, *pwd;
77 		size_t buflen;
78 		int err;
79 
80 		for(buflen=128;; buflen*=2) {
81 			char buf[buflen];  /* variable length array */
82 
83 			/* Note that we use getpwnam_r() instead of getpwnam(),
84 			   which returns its result in a statically allocated buffer and
85 			   cannot be considered thread safe.
86 			   Doesn't use NSS! */
87 			err=getpwnam_r(user, &pwdbuf, buf, buflen, &pwd);
88 			if(err==0 && pwd) {
89 				/* setgid first, because we may not be allowed to do it anymore after setuid */
90 				if (setgid(pwd->pw_gid)!=0) {
91 					log_error("Could not change group id to that of run_as user '%s': %s",
92 						  user,strerror(errno));
93 					return 0;
94 				}
95 
96 				/* initgroups uses NSS, so we can disable it,
97 				   i.e. we might need DNS for LDAP lookups, which times out */
98 				if (global.use_nss && (initgroups(user, pwd->pw_gid)!=0)) {
99 					log_error("Could not initialize the group access list of run_as user '%s': %s",
100 						  user,strerror(errno));
101 					return 0;
102 				}
103 				if (setuid(pwd->pw_uid)!=0) {
104 					log_error("Could not change user id to that of run_as user '%s': %s",
105 						  user,strerror(errno));
106 					return 0;
107 				}
108 				break;
109 			}
110 			else if(err!=ERANGE) {
111 				if(err)
112 					log_error("run_as user '%s' could not be found: %s",user,strerror(err));
113 				else
114 					log_error("run_as user '%s' could not be found.",user);
115 				return 0;
116 			}
117 			else if(buflen>=16*1024) {
118 				/* If getpwnam_r() seems defective, call it quits rather than
119 				   keep on allocating ever larger buffers until we crash. */
120 				log_error("getpwnam_r() requires more than %u bytes of buffer space.",(unsigned)buflen);
121 				return 0;
122 			}
123 			/* Else try again with larger buffer. */
124 		}
125 #else
126 		/* No getpwnam_r() :-(  We'll use getpwnam() and hope for the best. */
127 		struct passwd *pwd;
128 
129 		if (!(pwd=getpwnam(user))) {
130 			log_error("run_as user %s could not be found.",user);
131 			return 0;
132 		}
133 		/* setgid first, because we may not allowed to do it anymore after setuid */
134 		if (setgid(pwd->pw_gid)!=0) {
135 			log_error("Could not change group id to that of run_as user '%s': %s",
136 				  user,strerror(errno));
137 			return 0;
138 		}
139 		/* initgroups uses NSS, so we can disable it,
140 		   i.e. we might need DNS for LDAP lookups, which times out */
141 		if (global.use_nss && (initgroups(user, pwd->pw_gid)!=0)) {
142 			log_error("Could not initialize the group access list of run_as user '%s': %s",
143 				  user,strerror(errno));
144 			return 0;
145 		}
146 		if (setuid(pwd->pw_uid)!=0) {
147 			log_error("Could not change user id to that of run_as user '%s': %s",
148 				  user,strerror(errno));
149 			return 0;
150 		}
151 #endif
152 	}
153 
154 	return 1;
155 }
156 
157 /*
158  * returns whether c is allowed in IN domain names
159  */
160 #if 0
161 int isdchar (unsigned char c)
162 {
163 	if ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='-'
164 #ifdef UNDERSCORE
165 	    || c=='_'
166 #endif
167 	   )
168 		return 1;
169 	return 0;
170 }
171 #endif
172 
173 /*
174  * Convert a string given in dotted notation to the transport format (length byte prepended
175  * domain name parts, ended by a null length sequence)
176  * The memory areas referenced by str and rhn may not overlap.
177  * The buffer rhn points to is assumed to be at least DNSNAMEBUFSIZE bytes in size.
178  *
179  * Returns 1 if successful, otherwise 0.
180  */
str2rhn(const unsigned char * str,unsigned char * rhn)181 int str2rhn(const unsigned char *str, unsigned char *rhn)
182 {
183 	unsigned int i,j;
184 
185 	if(*str=='.' && !*(str+1)) {
186 		/* Special case: root domain */
187 		rhn[0]=0;
188 		return 1;
189 	}
190 
191 	for(i=0;;) {
192 		unsigned int jlim= i+63;
193 		if(jlim>DNSNAMEBUFSIZE-2) jlim=DNSNAMEBUFSIZE-2; /* DNSNAMEBUFSIZE-2 because the termination 0 has to follow */
194 		for(j=i; str[j] && str[j]!='.'; ++j) {
195 			if(j>=jlim) return 0;
196 			rhn[j+1]=str[j];
197 		}
198 		if(!str[j])
199 			break;
200 		if(j<=i)
201 			return 0;
202 		rhn[i]=(unsigned char)(j-i);
203 		i = j+1;
204 	}
205 
206 	rhn[i]=0;
207 	if (j>i || i==0)
208 		return 0;
209 	return 1;
210 }
211 
212 /*
213   parsestr2rhn is essentially the same as str2rhn, except that it doesn't look beyond
214   the first len chars in the input string. It also tolerates strings
215   not ending in a dot and returns a message in case of an error.
216  */
parsestr2rhn(const unsigned char * str,unsigned int len,unsigned char * rhn)217 const char *parsestr2rhn(const unsigned char *str, unsigned int len, unsigned char *rhn)
218 {
219 	unsigned int i,j;
220 
221 	if(len>0 && *str=='.' && (len==1 || !*(str+1))) {
222 		/* Special case: root domain */
223 		rhn[0]=0;
224 		return NULL;
225 	}
226 
227 	i=0;
228 	do {
229 		unsigned int jlim= i+63;
230 		if(jlim>DNSNAMEBUFSIZE-2) jlim=DNSNAMEBUFSIZE-2;
231 		for(j=i; j<len && str[j] && str[j]!='.'; ++j) {
232 			/* if(!isdchar(str[j]))
233 				return "Illegal character in domain name"; */
234 			if(j>=jlim)
235 				return "Domain name element too long";
236 			rhn[j+1]=str[j];
237 		}
238 
239 		if(j<=i) {
240 			if(j<len && str[j])
241 				return "Empty name element in domain name";
242 			else
243 				break;
244 		}
245 
246 		rhn[i]=(unsigned char)(j-i);
247 		i = j+1;
248 	} while(j<len && str[j]);
249 
250 	rhn[i]=0;
251 	if(i==0)
252 		return "Empty domain name not allowed";
253 	return NULL;
254 }
255 
256 /*
257  * Take a host name as used in the dns transfer protocol (a length byte,
258  * followed by the first part of the name, ..., followed by a 0 length byte),
259  * and return a string in the usual dotted notation. Length checking is done
260  * elsewhere (in decompress_name), this takes names from the cache which are
261  * validated. No more than "size" bytes will be written to the buffer str points to
262  * (but size must be positive).
263  * If the labels in rhn contains non-printable characters, these are represented
264  * in the result by a backslash followed three octal digits. Additionally some
265  * special characters are preceded by a backslash. Normally the result should
266  * fit within DNSNAMEBUFSIZE bytes, but if there are many non-printable characters, the
267  * receiving buffer may not be able to contain the full result. In that case the
268  * truncation in the result is indicated by series of trailing dots. This
269  * function is only used for diagnostic purposes, thus a truncated result should
270  * not be a very serious problem (and should only occur under pathological
271  * circumstances).
272  */
rhn2str(const unsigned char * rhn,unsigned char * str,unsigned int size)273 const unsigned char *rhn2str(const unsigned char *rhn, unsigned char *str, unsigned int size)
274 {
275 	unsigned int i,j,lb;
276 
277 	if(size==0) return NULL;
278 
279 	i=0; j=0;
280 	lb=rhn[i++];
281 	if (!lb) {
282 		if(size>=2)
283 			str[j++]='.';
284 	}
285 	else {
286 		do {
287 			for (;lb;--lb) {
288 				unsigned char c;
289 				if(j+2>=size)
290 					goto overflow;
291 				c=rhn[i++];
292 				if(isgraph(c)) {
293 					if(c=='.' || c=='\\' || c=='"') {
294 						str[j++]='\\';
295 						if(j+2>=size)
296 							goto overflow;
297 					}
298 					str[j++]=c;
299 				}
300 				else {
301 					unsigned int rem=size-1-j;
302 					int n=snprintf(charp &str[j],rem,"\\%03o",c);
303 					if(n<0 || n>=rem) {
304 						str[j++]='.';
305 						goto overflow;
306 					}
307 					j+=n;
308 				}
309 			}
310 			str[j++]='.';
311 			lb=rhn[i++];
312 		} while(lb);
313 	}
314 	str[j]=0;
315 	return str;
316 
317  overflow:
318 	j=size;
319 	str[--j]=0;
320 	if(j>0) {
321 		str[--j]='.';
322 		if(j>0) {
323 			str[--j]='.';
324 			if(j>0)
325 				str[--j]='.';
326 		}
327 	}
328 	return str;
329 }
330 
331 /* Return the length of a domain name in transport format.
332    The definition has in fact been moved to helpers.h as an inline function.
333    Note added by Paul Rombouts:
334    Compared to the definition used by Thomas Moestl (strlen(rhn)+1), the following definition of rhnlen
335    may yield a different result in certain error situations (when a domain name segment contains null byte).
336 */
337 #if 0
338 unsigned int rhnlen(const unsigned char *rhn)
339 {
340 	unsigned int i=0,lb;
341 
342 	while((lb=rhn[i++]))
343 		i+=lb;
344 	return i;
345 }
346 #endif
347 
348 /*
349  * Non-validating rhn copy (use with checked or generated data only).
350  * Returns number of characters copied. The buffer dst points to is assumed to be DNSNAMEBUFSIZE (or
351  * at any rate large enough) bytes in size.
352  * The answer assembly code uses this; it is guaranteed to not clobber anything
353  * after the name.
354  */
rhncpy(unsigned char * dst,const unsigned char * src)355 unsigned int rhncpy(unsigned char *dst, const unsigned char *src)
356 {
357 	unsigned int len = rhnlen(src);
358 
359 	PDNSD_ASSERT(len<=DNSNAMEBUFSIZE,"rhncpy: src too long!");
360 	memcpy(dst,src,len>DNSNAMEBUFSIZE?DNSNAMEBUFSIZE:len);
361 	return len;
362 }
363 
364 
365 /* Check whether a name is a normal wire-encoded domain name,
366    i.e. is not compressed, doesn't use extended labels and is not
367    too long.
368 */
isnormalencdomname(const unsigned char * rhn,unsigned maxlen)369 int isnormalencdomname(const unsigned char *rhn, unsigned maxlen)
370 {
371 	unsigned int i,lb;
372 
373 	if(maxlen>DNSNAMEBUFSIZE)
374 		maxlen=DNSNAMEBUFSIZE;
375 	for(i=0;;) {
376 		if(i>=maxlen) return 0;
377 		lb=rhn[i++];
378 		if(lb==0)    break;
379 		if(lb>0x3f)  return 0;
380 		i += lb;
381 	}
382 
383 	return 1;
384 }
385 
str2pdnsd_a(const char * addr,pdnsd_a * a)386 int str2pdnsd_a(const char *addr, pdnsd_a *a)
387 {
388 #ifdef ENABLE_IPV4
389 	if (run_ipv4) {
390 		return inet_aton(addr,&a->ipv4);
391 	}
392 #endif
393 #ifdef ENABLE_IPV6
394 	ELSE_IPV6 {
395 		/* Try to map an IPv4 address to IPv6 */
396 		struct in_addr a4;
397 		if(inet_aton(addr,&a4)) {
398 			a->ipv6=global.ipv4_6_prefix;
399 			((uint32_t *)(&a->ipv6))[3]=a4.s_addr;
400 			return 1;
401 		}
402 		return inet_pton(AF_INET6,addr,&a->ipv6)>0;
403 	}
404 #endif
405 	/* return 0; */
406 }
407 
408 /* definition moved to helpers.h */
409 #if 0
410 int is_inaddr_any(pdnsd_a *a)
411 {
412   return SEL_IPVER( a->ipv4.s_addr==INADDR_ANY,
413 		    IN6_IS_ADDR_UNSPECIFIED(&a->ipv6) );
414 }
415 #endif
416 
417 /*
418  * This is used for user output only, so it does not matter when an error occurs.
419  */
pdnsd_a2str(pdnsd_a * a,char * buf,int maxlen)420 const char *pdnsd_a2str(pdnsd_a *a, char *buf, int maxlen)
421 {
422 	const char *res= SEL_IPVER( inet_ntop(AF_INET,&a->ipv4,buf,maxlen),
423 				    inet_ntop(AF_INET6,&a->ipv6,buf,maxlen) );
424 	if (!res) {
425 		log_error("inet_ntop: %s", strerror(errno));
426 		return "?.?.?.?";
427 	}
428 
429 	return res;
430 }
431 
432 
433 /* Appropriately set our random device */
434 #ifdef R_DEFAULT
435 # if (TARGET == TARGET_BSD) && !defined(__NetBSD__)
436 #  define R_ARC4RANDOM 1
437 # else
438 #  define R_RANDOM 1
439 # endif
440 #endif
441 
442 #ifdef RANDOM_DEVICE
443 FILE *rand_file;
444 #endif
445 
446 #ifdef R_RANDOM
init_crandom()447 void init_crandom()
448 {
449 	struct timeval tv;
450 	struct timezone tz;
451 
452 	gettimeofday(&tv,&tz);
453 	srandom(tv.tv_sec^tv.tv_usec); /* not as guessable as time() */
454 }
455 #endif
456 
457 /* initialize the PRNG */
init_rng()458 int init_rng()
459 {
460 #ifdef RANDOM_DEVICE
461 	if (!(rand_file=fopen(RANDOM_DEVICE,"r"))) {
462 		log_error("Could not open %s.",RANDOM_DEVICE);
463 		return 0;
464 	}
465 #endif
466 #ifdef R_RANDOM
467 	init_crandom();
468 #endif
469 	return 1;
470 }
471 
472 /* The following function is now actually defined as a macro in helpers.h */
473 #if 0
474 void free_rng()
475 {
476 #ifdef RANDOM_DEVICE
477 	if (rand_file)
478 		fclose(rand_file);
479 #endif
480 }
481 #endif
482 
483 /* generate a (more or less) random number 16 bits long. */
get_rand16()484 unsigned short get_rand16()
485 {
486 #ifdef RANDOM_DEVICE
487 	unsigned short rv;
488 
489 	if (rand_file) {
490 		if (fread(&rv,sizeof(rv),1, rand_file)!=1) {
491 			log_error("Error while reading from random device: %s", strerror(errno));
492 			pdnsd_exit();
493 		}
494 		return rv&0xffff;
495 	} else
496 		return random()&0xffff;
497 #endif
498 #ifdef R_RANDOM
499 	return random()&0xffff;
500 #endif
501 #ifdef R_ARC4RANDOM
502 	return arc4random()&0xffff;
503 #endif
504 }
505 
506 /* fsprintf does formatted output to a file descriptor.
507    The functionality is similar to fprintf, but note that fd
508    is of type int instead of FILE*.
509    This function has been rewritten by Paul Rombouts */
fsprintf(int fd,const char * format,...)510 int fsprintf(int fd, const char *format, ...)
511 {
512 	int n;
513 	va_list va;
514 
515 	{
516 		char buf[256];
517 
518 		va_start(va,format);
519 		n=vsnprintf(buf,sizeof(buf),format,va);
520 		va_end(va);
521 
522 		if(n<(int)sizeof(buf)) {
523 			if(n>0) n=write_all(fd,buf,n);
524 			return n;
525 		}
526 	}
527 	/* retry with a right sized buffer, needs glibc 2.1 or higher to work */
528 	{
529 		unsigned bufsize=n+1;
530 		char buf[bufsize];
531 
532 		va_start(va,format);
533 		n=vsnprintf(buf,bufsize,format,va);
534 		va_end(va);
535 
536 		if(n>0) n=write_all(fd,buf,n);
537 	}
538 	return n;
539 }
540 
541 /* Convert data into a hexadecimal representation (for debugging purposes)..
542    The result is stored in the character array buf.
543    If buf is not large enough to hold the result, the
544    truncation is indicated by trailing dots.
545 */
hexdump(const void * data,int dlen,char * buf,int buflen)546 void hexdump(const void *data, int dlen, char *buf, int buflen)
547 {
548 	const unsigned char *p=data;
549 	int i,j=0;
550 	for(i=0;i<dlen;++i) {
551 		int rem=buflen-j;
552 		int n=snprintf(buf+j, rem, i==0?"%02x":" %02x", p[i]);
553 		if(n<0 || n>=rem) goto truncated;
554 		j += n;
555 	}
556 	goto done;
557 
558  truncated:
559 	if(j>=6) {
560 		j -= 3;
561 		if(j+4>=buflen)
562 			j -= 3;
563 		buf[j++]=' ';
564 		buf[j++]='.';
565 		buf[j++]='.';
566 		buf[j++]='.';
567 	}
568 	else {
569 		int ndots=buflen-1;
570 		if(ndots>3) ndots=3;
571 		j=0;
572 		while(j<ndots) buf[j++]='.';
573 	}
574  done:
575 	buf[j]=0;
576 }
577 
578 /* Copy string "in" to "str" buffer, converting non-printable characters
579    to escape sequences.
580    Returns the length of the output string, or -1 if the result does not
581    fit in the output buffer.
582 */
escapestr(const char * in,int ilen,char * str,int size)583 int escapestr(const char *in, int ilen, char *str, int size)
584 {
585 	int i,j=0;
586 	for(i=0;i<ilen;++i) {
587 		unsigned char c;
588 		if(j+1>=size)
589 			return -1;
590 		c=in[i];
591 		if(!isprint(c)) {
592 			int rem=size-j;
593 			int n=snprintf(&str[j],rem,"\\%03o",c);
594 			if(n<0 || n>=rem) {
595 				return -1;
596 			}
597 			j+=n;
598 		}
599 		else {
600 			if(c=='\\' || c=='"') {
601 				str[j++]='\\';
602 				if(j+1>=size)
603 					return -1;
604 			}
605 			str[j++]=c;
606 		}
607 	}
608 	str[j]=0;
609 	return j;
610 }
611 
612 /*
613  * This is not like strcmp, but will return 1 on match or 0 if the
614  * strings are different.
615  */
616 /* definition moved to helpers.h as an inline function. */
617 #if 0
618 int stricomp(char *a, char *b)
619 {
620 	int i;
621 	if (strlen(a) != strlen(b))
622 		return 0;
623 	for (i=0;i<strlen(a);i++) {
624 		if (tolower(a[i])!=tolower(b[i]))
625 			return 0;
626 	}
627 	return 1;
628 }
629 #endif
630 
631 /* Bah. I want strlcpy */
632 /* definition moved to helpers.h */
633 #if 0
634 int strncp(char *dst, char *src, int dstsz)
635 {
636 	char o;
637 
638 	strncpy(dst,src,dstsz);
639 	o=dst[dstsz-1];
640 	dst[dstsz-1]='\0';
641 	if (strlen(dst) >= dstsz-1 && o!='\0')
642 		return 0;
643 	return 1;
644 }
645 #endif
646 
647 #ifndef HAVE_GETLINE
648 /* Note by Paul Rombouts: I know that getline is a GNU extension and is not really portable,
649    but the alternative standard functions have some real problems.
650    The following substitute does not have exactly the same semantics as the GNU getline,
651    but it should be good enough, as long as the stream doesn't contain any null chars.
652    This version is actually based on fgets_realloc() that I found in the WWWOFFLE source.
653 */
654 
655 #define BUFSIZE 256
getline(char ** lineptr,size_t * n,FILE * stream)656 int getline(char **lineptr, size_t *n, FILE *stream)
657 {
658 	char *line=*lineptr;
659 	size_t sz=*n,i;
660 
661 	if(!line || sz<BUFSIZE) {
662 		sz=BUFSIZE;
663 		line = realloc(line,sz);
664 		if(!line) return -1;
665 		*lineptr=line;
666 		*n=sz;
667 	}
668 
669 	for (i=0;;) {
670 		char *lni;
671 
672 		if(!(lni=fgets(line+i,sz-i,stream))) {
673 			if(i && feof(stream))
674 				break;
675 			else
676 				return -1;
677 		}
678 
679 		i += strlen(lni);
680 
681 		if(i<sz-1 || line[i-1]=='\n')
682 			break;
683 
684 		sz += BUFSIZE;
685 		line = realloc(line,sz);
686 		if(!line) return -1;
687 		*lineptr=line;
688 		*n=sz;
689 	}
690 
691 	return i;
692 }
693 #undef BUFSIZE
694 #endif
695 
696 
697 #ifndef HAVE_VASPRINTF
698 /* On systems where the macro va_copy is not available, hopefully simple assignment will work.
699    Otherwise, I don't see any portable way of defining vasprintf() (without using a completely
700    different algorithm).
701 */
702 #ifndef va_copy
703 # ifdef __va_copy
704 #  define va_copy __va_copy
705 # else
706 #  warning "No va_copy or __va_copy macro found, trying simple assignment."
707 #  define va_copy(dst,src) ((dst)=(src))
708 # endif
709 #endif
710 
vasprintf(char ** lineptr,const char * format,va_list va)711 int vasprintf (char **lineptr, const char *format, va_list va)
712 {
713 	int sz=128,n;
714 	char *line=malloc(sz);
715 	va_list vasave;
716 
717 	if(!line) return -1;
718 
719 	va_copy(vasave,va);
720 	n=vsnprintf(line,sz,format,va);
721 
722 	if(n>=sz) {
723 		/* retry with a right sized buffer, needs glibc 2.1 or higher to work */
724 		sz=n+1;
725 		{
726 			char *tmp=realloc(line,sz);
727 			if(tmp) {
728 				line=tmp;
729 				n=vsnprintf(line,sz,format,vasave);
730 			}
731 			else
732 				n= -1;
733 		}
734 	}
735 	va_end(vasave);
736 
737 	if(n>=0)
738 		*lineptr=line;
739 	else
740 		free(line);
741 	return n;
742 }
743 #endif
744 
745 #ifndef HAVE_ASPRINTF
asprintf(char ** lineptr,const char * format,...)746 int asprintf (char **lineptr, const char *format, ...)
747 {
748 	int n;
749 	va_list va;
750 
751 	va_start(va,format);
752 	n=vasprintf(lineptr,format,va);
753 	va_end(va);
754 
755 	return n;
756 }
757 #endif
758 
759 #ifndef HAVE_INET_NTOP
inet_ntop(int af,const void * src,char * dst,size_t size)760 const char *inet_ntop(int af, const void *src, char *dst, size_t size)
761 {
762 	const char *rc = NULL;
763 
764 	if (src != NULL && dst != NULL && size > 0) {
765 		switch (af) {
766 		case AF_INET:
767 		{
768 			const unsigned char *p=src;
769 			int n = snprintf(dst, size, "%u.%u.%u.%u",
770 					 p[0],p[1],p[2],p[3]);
771 			if (n >= 0 && n < size) rc = dst;
772 		}
773 		break;
774 
775 #ifdef AF_INET6
776 		case AF_INET6:
777 		{
778 			const unsigned char *p=src;
779 			unsigned int i,offs=0;
780 			for (i=0;i<16;i+=2) {
781 				int n=snprintf(dst+offs, size-offs,i==0?"%x":":%x", ((unsigned)p[i]<<8)|p[i+1]);
782 				if(n<0) return NULL;
783 				offs+=n;
784 				if(offs>=size) return NULL;
785 			}
786 			rc = dst;
787 		}
788 		break;
789 #endif
790 		}
791 	}
792 
793 	return rc;
794 }
795 #endif
796