initial version, corresponding to ipfw3-2012
[ipfw-google.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 12264 2013-04-27 20:21:06Z luigi $
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 length 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 = (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 = (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         long 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 (fscanf(fp, "%ld", &d) != 1) {
340                         ret = -1;
341                 } else if (*oldlenp == sizeof(int)) {
342                         int dst = d;
343                         memcpy(oldp, &dst, *oldlenp);
344                 } else if (*oldlenp == sizeof(long)) {
345                         memcpy(oldp, &d, *oldlenp);
346                 } else {
347                         fprintf(stderr, "unknown paramerer len %d\n",
348                                 (int)*oldlenp);
349                 }
350                 fclose(fp);
351         }
352
353         if (newp && newlen) { /* write */
354                 fp = fopen(filename, "w");
355                 if (fp == NULL) {
356                         fprintf(stderr, "%s fopen error writing filename %s\n", __FUNCTION__, filename);
357                         return -1;
358                 }
359                 if (newlen == sizeof(int)) {
360                         if (fprintf(fp, "%d", *(int *)newp) < 1)
361                                 ret = -1;
362                 } else if (newlen == sizeof(long)) {
363                         if (fprintf(fp, "%ld", *(long *)newp) < 1)
364                                 ret = -1;
365                 } else {
366                         fprintf(stderr, "unknown paramerer len %d\n",
367                                 (int)newlen);
368                 }
369
370                 fclose(fp);
371         }
372
373         return ret;
374 #endif /* __linux__ */
375 }
376
377 #ifdef _WIN32
378 /*
379  * On windows, set/getsockopt are mapped to DeviceIoControl()
380  */
381 int
382 wnd_setsockopt(int s, int level, int sopt_name, const void *optval,
383                 socklen_t optlen)
384 {
385     size_t len = sizeof (struct sockopt) + optlen;
386     struct sockopt *sock;
387     DWORD n;
388     BOOL result;
389     HANDLE _dev_h = (HANDLE)s;
390
391     /* allocate a data structure for communication */
392     sock = malloc(len);
393     if (sock == NULL)
394         return -1;
395
396     sock->sopt_dir = SOPT_SET;
397     sock->sopt_name = sopt_name;
398     sock->sopt_valsize = optlen;
399     sock->sopt_val = (void *)(sock+1);
400
401     memcpy(sock->sopt_val, optval, optlen);
402     result = DeviceIoControl (_dev_h, IP_FW_SETSOCKOPT, sock, len,
403                 NULL, 0, &n, NULL);
404     free (sock);
405
406     return (result ? 0 : -1);
407 }
408
409 int
410 wnd_getsockopt(int s, int level, int sopt_name, void *optval,
411                 socklen_t *optlen)
412 {
413     size_t len = sizeof (struct sockopt) + *optlen;
414     struct sockopt *sock;
415     DWORD n;
416     BOOL result;
417     HANDLE _dev_h = (HANDLE)s;
418
419     sock = malloc(len);
420     if (sock == NULL)
421         return -1;
422
423     sock->sopt_dir = SOPT_GET;
424     sock->sopt_name = sopt_name;
425     sock->sopt_valsize = *optlen;
426     sock->sopt_val = (void *)(sock+1);
427
428     memcpy (sock->sopt_val, optval, *optlen);
429
430     result = DeviceIoControl (_dev_h, IP_FW_GETSOCKOPT, sock, len,
431                 sock, len, &n, NULL);
432         //printf("len = %i, returned = %u, valsize = %i\n",len,n,sock->sopt_valsize);
433     *optlen = sock->sopt_valsize;
434     memcpy (optval, sock->sopt_val, *optlen);
435     free (sock);
436     return (result ? 0 : -1);
437 }
438
439 int
440 my_socket(int domain, int ty, int proto)
441 {
442     TCHAR *pcCommPort = TEXT("\\\\.\\Ipfw");
443     HANDLE _dev_h = INVALID_HANDLE_VALUE;
444
445     /* Special Handling For Accessing Device On Windows 2000 Terminal Server
446        See Microsoft KB Article 259131 */
447     if (_dev_h == INVALID_HANDLE_VALUE) {
448         _dev_h = CreateFile (pcCommPort,
449                 GENERIC_READ | GENERIC_WRITE,
450                 0, NULL,
451                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
452     }
453     if (_dev_h == INVALID_HANDLE_VALUE) {
454         printf("%s failed %u, cannot talk to kernel module\n",
455                 __FUNCTION__, (unsigned)GetLastError());
456         return -1;
457     }
458     return (int)_dev_h;
459 }
460
461 struct hostent* gethostbyname2(const char *name, int af)
462 {
463         return gethostbyname(name);
464 }
465
466 struct ether_addr* ether_aton(const char *a)
467 {
468         fprintf(stderr, "%s empty\n", __FUNCTION__);
469         return NULL;
470 }
471
472 #ifdef TCC
473 int     opterr = 1,             /* if error message should be printed */
474         optind = 1,             /* index into parent argv vector */
475         optopt,                 /* character checked for validity */
476         optreset;               /* reset getopt */
477 char    *optarg;                /* argument associated with option */
478
479 #define BADCH   (int)'?'
480 #define BADARG  (int)':'
481 #define EMSG    ""
482
483 #define PROGNAME        "ipfw"
484 /*
485  * getopt --
486  *      Parse argc/argv argument vector.
487  */
488 int
489 getopt(nargc, nargv, ostr)
490         int nargc;
491         char * const nargv[];
492         const char *ostr;
493 {
494         static char *place = EMSG;              /* option letter processing */
495         char *oli;                              /* option letter list index */
496
497         if (optreset || *place == 0) {          /* update scanning pointer */
498                 optreset = 0;
499                 place = nargv[optind];
500                 if (optind >= nargc || *place++ != '-') {
501                         /* Argument is absent or is not an option */
502                         place = EMSG;
503                         return (-1);
504                 }
505                 optopt = *place++;
506                 if (optopt == '-' && *place == 0) {
507                         /* "--" => end of options */
508                         ++optind;
509                         place = EMSG;
510                         return (-1);
511                 }
512                 if (optopt == 0) {
513                         /* Solitary '-', treat as a '-' option
514                            if the program (eg su) is looking for it. */
515                         place = EMSG;
516                         if (strchr(ostr, '-') == NULL)
517                                 return (-1);
518                         optopt = '-';
519                 }
520         } else
521                 optopt = *place++;
522
523         /* See if option letter is one the caller wanted... */
524         if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) {
525                 if (*place == 0)
526                         ++optind;
527                 if (opterr && *ostr != ':')
528                         (void)fprintf(stderr,
529                             "%s: illegal option -- %c\n", PROGNAME,
530                             optopt);
531                 return (BADCH);
532         }
533
534         /* Does this option need an argument? */
535         if (oli[1] != ':') {
536                 /* don't need argument */
537                 optarg = NULL;
538                 if (*place == 0)
539                         ++optind;
540         } else {
541                 /* Option-argument is either the rest of this argument or the
542                    entire next argument. */
543                 if (*place)
544                         optarg = place;
545                 else if (nargc > ++optind)
546                         optarg = nargv[optind];
547                 else {
548                         /* option-argument absent */
549                         place = EMSG;
550                         if (*ostr == ':')
551                                 return (BADARG);
552                         if (opterr)
553                                 (void)fprintf(stderr,
554                                     "%s: option requires an argument -- %c\n",
555                                     PROGNAME, optopt);
556                         return (BADCH);
557                 }
558                 place = EMSG;
559                 ++optind;
560         }
561         return (optopt);                        /* return option letter */
562 }
563
564 //static FILE *err_file = stderr;
565 void
566 verrx(int ex, int eval, const char *fmt, va_list ap)
567 {
568         fprintf(stderr, "%s: ", PROGNAME);
569         if (fmt != NULL)
570                 vfprintf(stderr, fmt, ap);
571         fprintf(stderr, "\n");
572         if (ex)
573                 exit(eval);
574 }
575 void
576 errx(int eval, const char *fmt, ...)
577 {
578         va_list ap;
579         va_start(ap, fmt);
580         verrx(1, eval, fmt, ap);
581         va_end(ap);
582 }
583
584 void
585 warnx(const char *fmt, ...)
586 {
587         va_list ap;
588         va_start(ap, fmt);
589         verrx(0, 0, fmt, ap);
590         va_end(ap);
591 }
592
593 char *
594 strsep(char **stringp, const char *delim)
595 {
596         char *s;
597         const char *spanp;
598         int c, sc;
599         char *tok;
600
601         if ((s = *stringp) == NULL)
602                 return (NULL);
603         for (tok = s;;) {
604                 c = *s++;
605                 spanp = delim;
606                 do {
607                         if ((sc = *spanp++) == c) {
608                                 if (c == 0)
609                                         s = NULL;
610                                 else
611                                         s[-1] = 0;
612                                 *stringp = s;
613                                 return (tok);
614                         }
615                 } while (sc != 0);
616         }
617         /* NOTREACHED */
618 }
619
620 static unsigned char
621 tolower(unsigned char c)
622 {
623         return (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c;
624 }
625
626 static int isdigit(unsigned char c)
627 {
628         return (c >= '0' && c <= '9');
629 }
630
631 static int isxdigit(unsigned char c)
632 {
633         return (strchr("0123456789ABCDEFabcdef", c) ? 1 : 0);
634 }
635
636 static int isspace(unsigned char c)
637 {
638         return (strchr(" \t\n\r", c) ? 1 : 0);
639 }
640
641 static int isascii(unsigned char c)
642 {
643         return (c < 128);
644 }
645
646 static int islower(unsigned char c)
647 {
648         return (c >= 'a' && c <= 'z');
649 }
650
651 int
652 strcasecmp(const char *s1, const char *s2)
653 {
654         const unsigned char
655                         *us1 = (const unsigned char *)s1,
656                         *us2 = (const unsigned char *)s2;
657
658         while (tolower(*us1) == tolower(*us2++))
659                 if (*us1++ == '\0')
660                         return (0);
661         return (tolower(*us1) - tolower(*--us2));
662 }
663
664 intmax_t
665 strtoimax(const char * restrict nptr, char ** restrict endptr, int base)
666 {
667         return strtol(nptr, endptr,base);
668 }
669
670 void
671 setservent(int a)
672 {
673 }
674
675 #define NS_INADDRSZ 128
676
677 int
678 inet_pton(int af, const char *src, void *dst)
679 {
680         static const char digits[] = "0123456789";
681         int saw_digit, octets, ch;
682         u_char tmp[NS_INADDRSZ], *tp;
683
684         if (af != AF_INET) {
685                 errno = EINVAL;
686                 return -1;
687         }
688
689         saw_digit = 0;
690         octets = 0;
691         *(tp = tmp) = 0;
692         while ((ch = *src++) != '\0') {
693                 const char *pch;
694
695                 if ((pch = strchr(digits, ch)) != NULL) {
696                         u_int new = *tp * 10 + (pch - digits);
697
698                         if (saw_digit && *tp == 0)
699                                 return (0);
700                         if (new > 255)
701                                 return (0);
702                         *tp = new;
703                         if (!saw_digit) {
704                                 if (++octets > 4)
705                                         return (0);
706                                 saw_digit = 1;
707                         }
708                 } else if (ch == '.' && saw_digit) {
709                         if (octets == 4)
710                                 return (0);
711                         *++tp = 0;
712                         saw_digit = 0;
713                 } else
714                         return (0);
715         }
716         if (octets < 4)
717                 return (0);
718         memcpy(dst, tmp, NS_INADDRSZ);
719         return (1);
720 }
721
722 const char *
723 inet_ntop(int af, const void *_src, char *dst, socklen_t size)
724 {
725         static const char fmt[] = "%u.%u.%u.%u";
726         char tmp[sizeof "255.255.255.255"];
727         const u_char *src = _src;
728         int l;
729         if (af != AF_INET) {
730                 errno = EINVAL;
731                 return NULL;
732         }
733
734         l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
735         if (l <= 0 || (socklen_t) l >= size) {
736                 errno = ENOSPC;
737                 return (NULL);
738         }
739         strlcpy(dst, tmp, size);
740         return (dst);
741 }
742
743 /*%
744  * Check whether "cp" is a valid ascii representation
745  * of an Internet address and convert to a binary address.
746  * Returns 1 if the address is valid, 0 if not.
747  * This replaces inet_addr, the return value from which
748  * cannot distinguish between failure and a local broadcast address.
749  */
750 int
751 inet_aton(const char *cp, struct in_addr *addr) {
752         u_long val;
753         int base, n;
754         char c;
755         u_int8_t parts[4];
756         u_int8_t *pp = parts;
757         int digit;
758
759         c = *cp;
760         for (;;) {
761                 /*
762                  * Collect number up to ``.''.
763                  * Values are specified as for C:
764                  * 0x=hex, 0=octal, isdigit=decimal.
765                  */
766                 if (!isdigit((unsigned char)c))
767                         return (0);
768                 val = 0; base = 10; digit = 0;
769                 if (c == '0') {
770                         c = *++cp;
771                         if (c == 'x' || c == 'X')
772                                 base = 16, c = *++cp;
773                         else {
774                                 base = 8;
775                                 digit = 1 ;
776                         }
777                 }
778                 for (;;) {
779                         if (isascii(c) && isdigit((unsigned char)c)) {
780                                 if (base == 8 && (c == '8' || c == '9'))
781                                         return (0);
782                                 val = (val * base) + (c - '0');
783                                 c = *++cp;
784                                 digit = 1;
785                         } else if (base == 16 && isascii(c) &&
786                                    isxdigit((unsigned char)c)) {
787                                 val = (val << 4) |
788                                         (c + 10 - (islower((unsigned char)c) ? 'a' : 'A'));
789                                 c = *++cp;
790                                 digit = 1;
791                         } else
792                                 break;
793                 }
794                 if (c == '.') {
795                         /*
796                          * Internet format:
797                          *      a.b.c.d
798                          *      a.b.c   (with c treated as 16 bits)
799                          *      a.b     (with b treated as 24 bits)
800                          */
801                         if (pp >= parts + 3 || val > 0xffU)
802                                 return (0);
803                         *pp++ = val;
804                         c = *++cp;
805                 } else
806                         break;
807         }
808         /*
809          * Check for trailing characters.
810          */
811         if (c != '\0' && (!isascii(c) || !isspace((unsigned char)c)))
812                 return (0);
813         /*
814          * Did we get a valid digit?
815          */
816         if (!digit)
817                 return (0);
818         /*
819          * Concoct the address according to
820          * the number of parts specified.
821          */
822         n = pp - parts + 1;
823         switch (n) {
824         case 1:                         /*%< a -- 32 bits */
825                 break;
826
827         case 2:                         /*%< a.b -- 8.24 bits */
828                 if (val > 0xffffffU)
829                         return (0);
830                 val |= parts[0] << 24;
831                 break;
832
833         case 3:                         /*%< a.b.c -- 8.8.16 bits */
834                 if (val > 0xffffU)
835                         return (0);
836                 val |= (parts[0] << 24) | (parts[1] << 16);
837                 break;
838
839         case 4:                         /*%< a.b.c.d -- 8.8.8.8 bits */
840                 if (val > 0xffU)
841                         return (0);
842                 val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
843                 break;
844         }
845         if (addr != NULL)
846                 addr->s_addr = htonl(val);
847         return (1);
848 }
849
850 #endif /* TCC */
851
852 #endif /* _WIN32 */