Sync with the new ipfw3 version.
[ipfw.git] / ipfw / glue.c
1 /*
2  * Copyright (C) 2009 Luigi Rizzo, Marta Carbone, Universita` di Pisa
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 /*
27  * $Id: glue.c 5881 2010-03-25 14:29:48Z svn_panicucci $
28  *
29  * Userland functions missing in linux/Windows
30  */
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35
36 #ifdef _WIN32
37 #include <netdb.h>
38 #include <windows.h>
39 #endif /* _WIN32 */
40
41 #ifndef HAVE_NAT
42 /* dummy nat functions */
43 void
44 ipfw_show_nat(int ac, char **av)
45 {
46         fprintf(stderr, "%s unsupported\n", __FUNCTION__);
47 }
48
49 void
50 ipfw_config_nat(int ac, char **av)
51 {
52         fprintf(stderr, "%s unsupported\n", __FUNCTION__);
53 }
54 #endif
55
56 #ifdef __linux__
57 int optreset;   /* missing in linux */
58 #endif
59
60 /*
61  * not implemented in linux.
62  * taken from /usr/src/lib/libc/string/strlcpy.c
63  */
64 size_t
65 strlcpy(char *dst, const char *src, size_t siz)
66 {
67         char *d = dst;
68         const char *s = src;
69         size_t n = siz;
70  
71         /* Copy as many bytes as will fit */
72         if (n != 0 && --n != 0) {
73                 do {
74                         if ((*d++ = *s++) == 0)
75                                 break;
76                 } while (--n != 0);
77         }
78
79         /* Not enough room in dst, add NUL and traverse rest of src */
80         if (n == 0) {
81                 if (siz != 0)
82                         *d = '\0';              /* NUL-terminate dst */
83                 while (*s++)
84                         ;
85         }
86
87         return(s - src - 1);    /* count does not include NUL */
88 }
89
90
91 /* missing in linux and windows */
92 long long int
93 strtonum(const char *nptr, long long minval, long long maxval,
94          const char **errstr)
95 {
96         long long ret;
97         int errno_c = errno;    /* save actual errno */
98
99         errno = 0;
100 #ifdef TCC
101         ret = strtol(nptr, (char **)errstr, 0);
102 #else
103         ret = strtoll(nptr, (char **)errstr, 0);
104 #endif
105         /* We accept only a string that represent exactly a number (ie. start
106          * and end with a digit).
107          * FreeBSD version wants errstr==NULL if no error occurs, otherwise
108          * errstr should point to an error string.
109          * For our purspose, we implement only the invalid error, ranges
110          * error aren't checked
111          */
112         if (errno != 0 || nptr == *errstr || **errstr != '\0')
113                 *errstr = "invalid";
114         else  {
115                 *errstr = NULL;
116                 errno = errno_c;
117         }
118         return ret;
119 }
120
121 #if defined (_WIN32) || defined (EMULATE_SYSCTL)
122 //XXX missing prerequisites
123 #include <net/if.h>             //openwrt
124 #include <netinet/ip.h>         //openwrt
125 #include <netinet/ip_fw.h>
126 #include <netinet/ip_dummynet.h>
127 #endif
128
129 /*
130  * set or get system information
131  * XXX lock acquisition/serialize calls 
132  *
133  * we export this as sys/module/ipfw_mod/parameters/___ 
134  * This function get or/and set the value of the sysctl passed by
135  * the name parameter. If the old value is not desired,
136  * oldp and oldlenp should be set to NULL.
137  *
138  * XXX
139  * I do not know how this works in FreeBSD in the case
140  * where there are no write permission on the sysctl var.
141  * We read the value and set return variables in any way
142  * but returns -1 on write failures, regardless the
143  * read success.
144  * 
145  * Since there is no information on types, in the following
146  * code we assume a lenght of 4 is a int.
147  * 
148  * Returns 0 on success, -1 on errors.
149  */
150 int
151 sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp,
152          size_t newlen)
153 {
154 #if defined (_WIN32) || defined (EMULATE_SYSCTL)
155         /*
156          * we embed the sysctl request in the usual sockopt mechanics.
157          * the sockopt buffer il filled with a dn_id with IP_DUMMYNET3
158          * command, and the special DN_SYSCTL_GET and DN_SYSCTL_SET
159          * subcommands.
160          * the syntax of this function is fully compatible with 
161          * POSIX sysctlby name: 
162          * if newp and newlen are != 0 => this is a set
163          * else if oldp and oldlen are != 0 => this is a get
164          *              to avoid too much overhead in the module, the whole
165          *              sysctltable is returned, and the parsing is done in userland,
166          *              a probe request is done to retrieve the size needed to
167          *              transfer the table, before the real request
168          * if both old and new params = 0 => this is a print
169          *              this is a special request, done only by main()
170          *              to implement the extension './ipfw sysctl',
171          *              a command that bypasses the normal getopt, and that 
172          *              is available on those platforms that use this
173          *              sysctl emulation.
174          *              in this case, a negative oldlen signals that *oldp
175          *              is actually a FILE* to print somewhere else than stdout
176          */
177         
178         int l;
179         int ret;
180         struct dn_id* oid;
181         struct sysctlhead* entry;
182         char* pstring;
183         char* pdata;
184         FILE* fp;
185         
186         if((oldlenp != NULL) && (*oldlenp < 0))
187                 fp = (FILE*)oldp;
188         else
189                 fp = stdout;
190         if(newp != NULL && newlen != 0)
191         {
192                 //this is a set
193                 l = sizeof(struct dn_id) + sizeof(struct sysctlhead) + strlen(name)+1 + newlen;
194                 oid = malloc(l);
195                 if (oid == NULL)
196                         return -1;
197                 oid->len = l;
198                 oid->type = DN_SYSCTL_SET;
199                 oid->id = DN_API_VERSION;
200
201                 entry = (struct sysctlhead*)(oid+1);
202                 pdata = (unsigned char*)(entry+1);
203                 pstring = pdata + newlen;
204
205                 entry->blocklen = ((sizeof(struct sysctlhead) + strlen(name)+1 + newlen) + 3) & ~3;
206                 entry->namelen = strlen(name)+1;
207                 entry->flags = 0;
208                 entry->datalen = newlen;
209
210                 bcopy(newp, pdata, newlen);
211                 bcopy(name, pstring, strlen(name)+1);
212
213                 ret = do_cmd(IP_DUMMYNET3, oid, (uintptr_t)l);
214                 if (ret != 0)
215                         return -1;
216         }
217         else
218         {
219                 //this is a get or a print
220                 l = sizeof(struct dn_id);
221                 oid = malloc(l);
222                 if (oid == NULL)
223                         return -1;
224                 oid->len = l;
225                 oid->type = DN_SYSCTL_GET;
226                 oid->id = DN_API_VERSION;
227
228                 ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l);
229                 if (ret != 0)
230                         return -1;
231                 
232                 l=oid->id;
233                 free(oid);
234                 oid = malloc(l);
235                 if (oid == NULL)
236                         return -1;
237                 oid->len = l;
238                 oid->type = DN_SYSCTL_GET;
239                 oid->id = DN_API_VERSION;
240                 
241                 ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l);
242                 if (ret != 0)
243                         return -1;
244                 
245                 entry = (struct sysctlhead*)(oid+1);
246                 while(entry->blocklen != 0)
247                 {
248                         pdata = (unsigned char*)(entry+1);
249                         pstring = pdata+entry->datalen;
250
251                         //time to check if this is a get or a print
252                         if(name != NULL && oldp != NULL && *oldlenp > 0) 
253                         {
254                                 //this is a get
255                                 if(strcmp(name,pstring) == 0)
256                                 {
257                                         //match found, sanity chech on len
258                                         if(*oldlenp < entry->datalen)
259                                         {
260                                                 printf("%s error: buffer too small\n",__FUNCTION__);
261                                                 return -1;
262                                         }
263                                         *oldlenp = entry->datalen;
264                                         bcopy(pdata, oldp, *oldlenp);
265                                         return 0;
266                                 }
267                         }
268                         else
269                         {
270                                 //this is a print
271                                 if( name == NULL )
272                                         goto print;     
273                                 if ( (strncmp(pstring,name,strlen(name)) == 0) && ( pstring[strlen(name)]=='\0' || pstring[strlen(name)]=='.' ) )
274                                                 goto print;
275                                 else
276                                                 goto skip;
277 print:                          
278                                 fprintf(fp, "%s: ",pstring);
279                                 switch( entry->flags >> 2 )
280                                 {
281                                         case SYSCTLTYPE_LONG:
282                                                 fprintf(fp, "%li ", *(long*)(pdata));
283                                                 break;
284                                         case SYSCTLTYPE_UINT:
285                                                 fprintf(fp, "%u ", *(unsigned int*)(pdata));
286                                                 break;
287                                         case SYSCTLTYPE_ULONG:
288                                                 fprintf(fp, "%lu ", *(unsigned long*)(pdata));
289                                                 break;
290                                         case SYSCTLTYPE_INT:
291                                         default:
292                                                 fprintf(fp, "%i ", *(int*)(pdata));
293                                 }
294                                 if( (entry->flags & 0x00000003) == CTLFLAG_RD )
295                                         fprintf(fp, "\t(read only)\n");
296                                 else
297                                         fprintf(fp, "\n");
298 skip:                   ;
299                         }
300                         entry = (struct sysctlhead*)((unsigned char*)entry + entry->blocklen);
301                 }
302                 free(oid);
303                 return 0;
304         }
305         //fallback for invalid options
306         return -1;
307
308 #else /* __linux__ */
309         FILE *fp;
310         char *basename = "/sys/module/ipfw_mod/parameters/";
311         char filename[256];     /* full filename */
312         char *varp;
313         int ret = 0;            /* return value */
314         int d;
315
316         if (name == NULL) /* XXX set errno */
317                 return -1;
318
319         /* locate the filename */
320         varp = strrchr(name, '.');
321         if (varp == NULL) /* XXX set errno */
322                 return -1;
323
324         snprintf(filename, sizeof(filename), "%s%s", basename, varp+1);
325
326         /*
327          * XXX we could open the file here, in rw mode
328          * but need to check if a file have write
329          * permissions.
330          */
331
332         /* check parameters */
333         if (oldp && oldlenp) { /* read mode */
334                 fp = fopen(filename, "r");
335                 if (fp == NULL) {
336                         fprintf(stderr, "%s fopen error reading filename %s\n", __FUNCTION__, filename);
337                         return -1;
338                 }
339                 if (*oldlenp == 4) {
340                         if (fscanf(fp, "%d", &d) == 1)
341                                 memcpy(oldp, &d, *oldlenp);
342                         else
343                                 ret = -1;
344                 }
345                 fclose(fp);
346         }
347
348         if (newp && newlen) { /* write */
349                 fp = fopen(filename, "w");
350                 if (fp == NULL) {
351                         fprintf(stderr, "%s fopen error writing filename %s\n", __FUNCTION__, filename);
352                         return -1;
353                 }
354                 if (newlen == 4) {
355                         if (fprintf(fp, "%d", *(int*)newp) < 1)
356                                 ret = -1;
357                 }
358                         
359                 fclose(fp);
360         }
361
362         return ret;
363 #endif /* __linux__ */
364 }
365
366 #ifdef _WIN32
367 /*
368  * On windows, set/getsockopt are mapped to DeviceIoControl()
369  */
370 int
371 wnd_setsockopt(int s, int level, int sopt_name, const void *optval,
372                 socklen_t optlen)
373 {
374     size_t len = sizeof (struct sockopt) + optlen;
375     struct sockopt *sock;
376     DWORD n;
377     BOOL result;
378     HANDLE _dev_h = (HANDLE)s;
379
380     /* allocate a data structure for communication */
381     sock = malloc(len);
382     if (sock == NULL)
383         return -1;
384
385     sock->sopt_dir = SOPT_SET;
386     sock->sopt_name = sopt_name;
387     sock->sopt_valsize = optlen;
388     sock->sopt_val = (void *)(sock+1);
389
390     memcpy(sock->sopt_val, optval, optlen);
391     result = DeviceIoControl (_dev_h, IP_FW_SETSOCKOPT, sock, len,
392                 NULL, 0, &n, NULL);
393     free (sock);
394
395     return (result ? 0 : -1);
396 }
397
398 int
399 wnd_getsockopt(int s, int level, int sopt_name, void *optval,
400                 socklen_t *optlen)
401 {
402     size_t len = sizeof (struct sockopt) + *optlen;
403     struct sockopt *sock;
404     DWORD n;
405     BOOL result;
406     HANDLE _dev_h = (HANDLE)s;
407
408     sock = malloc(len);
409     if (sock == NULL)
410         return -1;
411
412     sock->sopt_dir = SOPT_GET;
413     sock->sopt_name = sopt_name;
414     sock->sopt_valsize = *optlen;
415     sock->sopt_val = (void *)(sock+1);
416
417     memcpy (sock->sopt_val, optval, *optlen);
418
419     result = DeviceIoControl (_dev_h, IP_FW_GETSOCKOPT, sock, len,
420                 sock, len, &n, NULL);
421         //printf("len = %i, returned = %u, valsize = %i\n",len,n,sock->sopt_valsize);
422     *optlen = sock->sopt_valsize;
423     memcpy (optval, sock->sopt_val, *optlen);
424     free (sock);
425     return (result ? 0 : -1);
426 }
427
428 int
429 my_socket(int domain, int ty, int proto)
430 {
431     TCHAR *pcCommPort = TEXT("\\\\.\\Ipfw");
432     HANDLE _dev_h = INVALID_HANDLE_VALUE;
433
434     /* Special Handling For Accessing Device On Windows 2000 Terminal Server
435        See Microsoft KB Article 259131 */
436     if (_dev_h == INVALID_HANDLE_VALUE) {
437         _dev_h = CreateFile (pcCommPort,
438                 GENERIC_READ | GENERIC_WRITE,
439                 0, NULL,
440                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
441     }
442     if (_dev_h == INVALID_HANDLE_VALUE) {
443         printf("%s failed %u, cannot talk to kernel module\n",
444                 __FUNCTION__, (unsigned)GetLastError());
445         return -1;
446     }
447     return (int)_dev_h;
448 }
449
450 struct hostent* gethostbyname2(const char *name, int af)
451 {
452         return gethostbyname(name);
453 }
454
455 struct ether_addr* ether_aton(const char *a)
456 {
457         fprintf(stderr, "%s empty\n", __FUNCTION__);
458         return NULL;
459 }
460
461 #ifdef TCC
462 int     opterr = 1,             /* if error message should be printed */
463         optind = 1,             /* index into parent argv vector */
464         optopt,                 /* character checked for validity */
465         optreset;               /* reset getopt */
466 char    *optarg;                /* argument associated with option */
467
468 #define BADCH   (int)'?'
469 #define BADARG  (int)':'
470 #define EMSG    ""
471
472 #define PROGNAME        "ipfw"
473 /*
474  * getopt --
475  *      Parse argc/argv argument vector.
476  */
477 int
478 getopt(nargc, nargv, ostr)
479         int nargc;
480         char * const nargv[];
481         const char *ostr;
482 {
483         static char *place = EMSG;              /* option letter processing */
484         char *oli;                              /* option letter list index */
485
486         if (optreset || *place == 0) {          /* update scanning pointer */
487                 optreset = 0;
488                 place = nargv[optind];
489                 if (optind >= nargc || *place++ != '-') {
490                         /* Argument is absent or is not an option */
491                         place = EMSG;
492                         return (-1);
493                 }
494                 optopt = *place++;
495                 if (optopt == '-' && *place == 0) {
496                         /* "--" => end of options */
497                         ++optind;
498                         place = EMSG;
499                         return (-1);
500                 }
501                 if (optopt == 0) {
502                         /* Solitary '-', treat as a '-' option
503                            if the program (eg su) is looking for it. */
504                         place = EMSG;
505                         if (strchr(ostr, '-') == NULL)
506                                 return (-1);
507                         optopt = '-';
508                 }
509         } else
510                 optopt = *place++;
511
512         /* See if option letter is one the caller wanted... */
513         if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) {
514                 if (*place == 0)
515                         ++optind;
516                 if (opterr && *ostr != ':')
517                         (void)fprintf(stderr,
518                             "%s: illegal option -- %c\n", PROGNAME,
519                             optopt);
520                 return (BADCH);
521         }
522
523         /* Does this option need an argument? */
524         if (oli[1] != ':') {
525                 /* don't need argument */
526                 optarg = NULL;
527                 if (*place == 0)
528                         ++optind;
529         } else {
530                 /* Option-argument is either the rest of this argument or the
531                    entire next argument. */
532                 if (*place)
533                         optarg = place;
534                 else if (nargc > ++optind)
535                         optarg = nargv[optind];
536                 else {
537                         /* option-argument absent */
538                         place = EMSG;
539                         if (*ostr == ':')
540                                 return (BADARG);
541                         if (opterr)
542                                 (void)fprintf(stderr,
543                                     "%s: option requires an argument -- %c\n",
544                                     PROGNAME, optopt);
545                         return (BADCH);
546                 }
547                 place = EMSG;
548                 ++optind;
549         }
550         return (optopt);                        /* return option letter */
551 }
552
553 //static FILE *err_file = stderr;
554 void
555 verrx(int ex, int eval, const char *fmt, va_list ap)
556 {
557         fprintf(stderr, "%s: ", PROGNAME);
558         if (fmt != NULL)
559                 vfprintf(stderr, fmt, ap);
560         fprintf(stderr, "\n");
561         if (ex)
562                 exit(eval);
563 }               
564 void
565 errx(int eval, const char *fmt, ...)
566 {
567         va_list ap;
568         va_start(ap, fmt);
569         verrx(1, eval, fmt, ap);
570         va_end(ap);
571 }       
572
573 void
574 warnx(const char *fmt, ...)
575 {
576         va_list ap;
577         va_start(ap, fmt);
578         verrx(0, 0, fmt, ap);
579         va_end(ap);
580 }
581
582 char *
583 strsep(char **stringp, const char *delim)
584 {
585         char *s;
586         const char *spanp;
587         int c, sc;
588         char *tok;
589
590         if ((s = *stringp) == NULL)
591                 return (NULL);
592         for (tok = s;;) {
593                 c = *s++;
594                 spanp = delim;
595                 do {
596                         if ((sc = *spanp++) == c) {
597                                 if (c == 0)
598                                         s = NULL;
599                                 else
600                                         s[-1] = 0;
601                                 *stringp = s;
602                                 return (tok);
603                         }
604                 } while (sc != 0);
605         }
606         /* NOTREACHED */
607 }
608
609 static unsigned char
610 tolower(unsigned char c)
611 {
612         return (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c;
613 }
614
615 static int isdigit(unsigned char c)
616 {
617         return (c >= '0' && c <= '9');
618 }
619
620 static int isxdigit(unsigned char c)
621 {
622         return (index("0123456789ABCDEFabcdef", c) ? 1 : 0);
623 }
624
625 static int isspace(unsigned char c)
626 {
627         return (index(" \t\n\r", c) ? 1 : 0);
628 }
629
630 static int isascii(unsigned char c)
631 {
632         return (c < 128);
633 }
634
635 static int islower(unsigned char c)
636 {
637         return (c >= 'a' && c <= 'z');
638 }
639
640 int
641 strcasecmp(const char *s1, const char *s2)
642 {
643         const unsigned char
644                         *us1 = (const unsigned char *)s1,
645                         *us2 = (const unsigned char *)s2;
646
647         while (tolower(*us1) == tolower(*us2++))
648                 if (*us1++ == '\0')
649                         return (0);
650         return (tolower(*us1) - tolower(*--us2));
651 }
652
653 intmax_t
654 strtoimax(const char * restrict nptr, char ** restrict endptr, int base)
655 {
656         return strtol(nptr, endptr,base);
657 }
658
659 void
660 setservent(int a)
661 {
662 }
663
664 #define NS_INADDRSZ 128
665
666 int
667 inet_pton(int af, const char *src, void *dst)
668 {
669         static const char digits[] = "0123456789";
670         int saw_digit, octets, ch;
671         u_char tmp[NS_INADDRSZ], *tp;
672  
673         if (af != AF_INET) {
674                 errno = EINVAL;
675                 return -1;
676         }
677
678         saw_digit = 0;
679         octets = 0;
680         *(tp = tmp) = 0;
681         while ((ch = *src++) != '\0') {
682                 const char *pch;
683  
684                 if ((pch = strchr(digits, ch)) != NULL) {
685                         u_int new = *tp * 10 + (pch - digits);
686  
687                         if (saw_digit && *tp == 0)
688                                 return (0);
689                         if (new > 255)
690                                 return (0);
691                         *tp = new;
692                         if (!saw_digit) {
693                                 if (++octets > 4)
694                                         return (0);
695                                 saw_digit = 1;
696                         }
697                 } else if (ch == '.' && saw_digit) {
698                         if (octets == 4)
699                                 return (0);
700                         *++tp = 0;
701                         saw_digit = 0;
702                 } else
703                         return (0);
704         }
705         if (octets < 4)
706                 return (0);
707         memcpy(dst, tmp, NS_INADDRSZ);
708         return (1);
709 }
710
711 const char *
712 inet_ntop(int af, const void *_src, char *dst, socklen_t size)
713 {
714         static const char fmt[] = "%u.%u.%u.%u";
715         char tmp[sizeof "255.255.255.255"];
716         const u_char *src = _src;
717         int l;
718         if (af != AF_INET) {
719                 errno = EINVAL;
720                 return NULL;
721         }
722  
723         l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
724         if (l <= 0 || (socklen_t) l >= size) {
725                 errno = ENOSPC;
726                 return (NULL);
727         }
728         strlcpy(dst, tmp, size);
729         return (dst);
730 }
731
732 /*%
733  * Check whether "cp" is a valid ascii representation
734  * of an Internet address and convert to a binary address.
735  * Returns 1 if the address is valid, 0 if not.
736  * This replaces inet_addr, the return value from which
737  * cannot distinguish between failure and a local broadcast address.
738  */
739 int
740 inet_aton(const char *cp, struct in_addr *addr) {
741         u_long val;
742         int base, n;
743         char c;
744         u_int8_t parts[4];
745         u_int8_t *pp = parts;
746         int digit;
747
748         c = *cp;
749         for (;;) {
750                 /*
751                  * Collect number up to ``.''.
752                  * Values are specified as for C:
753                  * 0x=hex, 0=octal, isdigit=decimal.
754                  */
755                 if (!isdigit((unsigned char)c))
756                         return (0);
757                 val = 0; base = 10; digit = 0;
758                 if (c == '0') {
759                         c = *++cp;
760                         if (c == 'x' || c == 'X')
761                                 base = 16, c = *++cp;
762                         else {
763                                 base = 8;
764                                 digit = 1 ;
765                         }
766                 }
767                 for (;;) {
768                         if (isascii(c) && isdigit((unsigned char)c)) {
769                                 if (base == 8 && (c == '8' || c == '9'))
770                                         return (0);
771                                 val = (val * base) + (c - '0');
772                                 c = *++cp;
773                                 digit = 1;
774                         } else if (base == 16 && isascii(c) &&
775                                    isxdigit((unsigned char)c)) {
776                                 val = (val << 4) |
777                                         (c + 10 - (islower((unsigned char)c) ? 'a' : 'A'));
778                                 c = *++cp;
779                                 digit = 1;
780                         } else
781                                 break;
782                 }
783                 if (c == '.') {
784                         /*
785                          * Internet format:
786                          *      a.b.c.d
787                          *      a.b.c   (with c treated as 16 bits)
788                          *      a.b     (with b treated as 24 bits)
789                          */
790                         if (pp >= parts + 3 || val > 0xffU)
791                                 return (0);
792                         *pp++ = val;
793                         c = *++cp;
794                 } else
795                         break;
796         }
797         /*
798          * Check for trailing characters.
799          */
800         if (c != '\0' && (!isascii(c) || !isspace((unsigned char)c)))
801                 return (0);
802         /*
803          * Did we get a valid digit?
804          */
805         if (!digit)
806                 return (0);
807         /*
808          * Concoct the address according to
809          * the number of parts specified.
810          */
811         n = pp - parts + 1;
812         switch (n) {
813         case 1:                         /*%< a -- 32 bits */
814                 break;
815
816         case 2:                         /*%< a.b -- 8.24 bits */
817                 if (val > 0xffffffU)
818                         return (0);
819                 val |= parts[0] << 24;
820                 break;
821
822         case 3:                         /*%< a.b.c -- 8.8.16 bits */
823                 if (val > 0xffffU)
824                         return (0);
825                 val |= (parts[0] << 24) | (parts[1] << 16);
826                 break;
827
828         case 4:                         /*%< a.b.c.d -- 8.8.8.8 bits */
829                 if (val > 0xffU)
830                         return (0);
831                 val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
832                 break;
833         }
834         if (addr != NULL)
835                 addr->s_addr = htonl(val);
836         return (1);
837 }
838
839 #endif /* TCC */
840
841 #endif /* _WIN32 */