Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / usr.bin / ftp / fetch.c
1 /*      $NetBSD: fetch.c,v 1.16.2.1 1997/11/18 01:00:22 mellon Exp $    */
2
3 /*-
4  * Copyright (c) 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason Thorpe and Luke Mewburn.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * 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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * $NetBSD: fetch.c,v 1.16.2.1 1997/11/18 01:00:22 mellon Exp $
39  * $FreeBSD: src/usr.bin/ftp/fetch.c,v 1.12.2.6 2002/10/19 12:50:26 roam Exp $
40  * $DragonFly: src/usr.bin/ftp/Attic/fetch.c,v 1.2 2003/06/17 04:29:26 dillon Exp $
41  */
42
43 #include <sys/cdefs.h>
44
45 /*
46  * FTP User Program -- Command line file retrieval
47  */
48
49 #include <sys/types.h>
50 #include <sys/param.h>
51 #include <sys/socket.h>
52
53 #include <netinet/in.h>
54
55 #include <arpa/ftp.h>
56 #include <arpa/inet.h>
57
58 #include <ctype.h>
59 #include <err.h>
60 #include <errno.h>
61 #include <netdb.h>
62 #include <fcntl.h>
63 #include <signal.h>
64 #include <stdio.h>
65 #include <stdlib.h>
66 #include <string.h>
67 #include <unistd.h>
68
69 #include "ftp_var.h"
70
71 static int      url_get __P((const char *, const char *));
72 void            aborthttp __P((int));
73
74
75 #define FTP_URL         "ftp://"        /* ftp URL prefix */
76 #define HTTP_URL        "http://"       /* http URL prefix */
77 #define FTP_PROXY       "ftp_proxy"     /* env var with ftp proxy location */
78 #define HTTP_PROXY      "http_proxy"    /* env var with http proxy location */
79
80
81 #define EMPTYSTRING(x)  ((x) == NULL || (*(x) == '\0'))
82
83 jmp_buf httpabort;
84
85 /*
86  * Retrieve URL, via the proxy in $proxyvar if necessary.
87  * Modifies the string argument given.
88  * Returns -1 on failure, 0 on success
89  */
90 static int
91 url_get(origline, proxyenv)
92         const char *origline;
93         const char *proxyenv;
94 {
95         struct addrinfo hints;
96         struct addrinfo *res0, *res;
97         char nameinfo[2 * INET6_ADDRSTRLEN + 1];
98         int i, out, isftpurl;
99         char *port;
100         volatile int s;
101         ssize_t len;
102         char c, *cp, *ep, *http_buffer, *portnum, *path, buf[4096];
103         const char *savefile;
104         char *line, *proxy, *host;
105         volatile sig_t oldintr;
106         off_t hashbytes;
107         int error;
108
109         s = -1;
110         proxy = NULL;
111         isftpurl = 0;
112         res0 = NULL;
113
114 #ifdef __GNUC__                 /* XXX: to shut up gcc warnings */
115         (void)&savefile;
116         (void)&proxy;
117         (void)&res0;
118 #endif
119
120         line = strdup(origline);
121         if (line == NULL)
122                 errx(1, "Can't allocate memory to parse URL");
123         if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
124                 host = line + sizeof(HTTP_URL) - 1;
125         else if (strncasecmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
126                 host = line + sizeof(FTP_URL) - 1;
127                 isftpurl = 1;
128         } else
129                 errx(1, "url_get: Invalid URL '%s'", line);
130
131         path = strchr(host, '/');               /* find path */
132         if (EMPTYSTRING(path)) {
133                 if (isftpurl)
134                         goto noftpautologin;
135                 warnx("Invalid URL (no `/' after host): %s", origline);
136                 goto cleanup_url_get;
137         }
138         *path++ = '\0';
139         if (EMPTYSTRING(path)) {
140                 if (isftpurl)
141                         goto noftpautologin;
142                 warnx("Invalid URL (no file after host): %s", origline);
143                 goto cleanup_url_get;
144         }
145
146         savefile = strrchr(path, '/');                  /* find savefile */
147         if (savefile != NULL)
148                 savefile++;
149         else
150                 savefile = path;
151         if (EMPTYSTRING(savefile)) {
152                 if (isftpurl)
153                         goto noftpautologin;
154                 warnx("Invalid URL (no file after directory): %s", origline);
155                 goto cleanup_url_get;
156         }
157
158         if (proxyenv != NULL) {                         /* use proxy */
159                 proxy = strdup(proxyenv);
160                 if (proxy == NULL)
161                         errx(1, "Can't allocate memory for proxy URL.");
162                 if (strncasecmp(proxy, HTTP_URL, sizeof(HTTP_URL) - 1) == 0)
163                         host = proxy + sizeof(HTTP_URL) - 1;
164                 else if (strncasecmp(proxy, FTP_URL, sizeof(FTP_URL) - 1) == 0)
165                         host = proxy + sizeof(FTP_URL) - 1;
166                 else {
167                         warnx("Malformed proxy URL: %s", proxyenv);
168                         goto cleanup_url_get;
169                 }
170                 if (EMPTYSTRING(host)) {
171                         warnx("Malformed proxy URL: %s", proxyenv);
172                         goto cleanup_url_get;
173                 }
174                 *--path = '/';                  /* add / back to real path */
175                 path = strchr(host, '/');       /* remove trailing / on host */
176                 if (! EMPTYSTRING(path))
177                         *path++ = '\0';
178                 path = line;
179         }
180
181         if (*host == '[' && (portnum = strrchr(host, ']'))) {   /* IPv6 URL */
182                 *portnum++ = '\0';
183                 host++;
184                 if (*portnum == ':')
185                         portnum++;
186                 else
187                         portnum = NULL;
188         } else {
189                 portnum = strrchr(host, ':');   /* find portnum */
190                 if (portnum != NULL)
191                         *portnum++ = '\0';
192         }
193         
194         if (debug)
195                 printf("host %s, port %s, path %s, save as %s.\n",
196                     host, portnum, path, savefile);
197
198         if (! EMPTYSTRING(portnum)) {
199                 port = portnum;
200         } else
201                 port = httpport;
202
203         memset(&hints, 0, sizeof(hints));
204         hints.ai_family = family;
205         hints.ai_socktype = SOCK_STREAM;
206         error = getaddrinfo(host, port, &hints, &res);
207         res0 = res;
208         if (error) {
209                 warnx("%s: %s", host, gai_strerror(error));
210                 if (error == EAI_SYSTEM)
211                         warnx("%s: %s", host, strerror(errno));
212                 goto cleanup_url_get;
213         }
214
215         while (1)
216       {
217         ai_unmapped(res);
218         s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
219         if (s == -1) {
220                 res = res->ai_next;
221                 if (res)
222                         continue;
223                 warn("Can't create socket");
224                 goto cleanup_url_get;
225         }
226
227         if (dobind) {
228                 struct addrinfo *bindres;
229                 int binderr = -1;
230
231                 for (bindres = bindres0;
232                      bindres != NULL;
233                      bindres = bindres->ai_next)
234                         if (bindres->ai_family == res->ai_family)
235                                 break;
236                 if (bindres == NULL)
237                         bindres = bindres0;
238                 binderr = bind(s, bindres->ai_addr, bindres->ai_addrlen);
239                 if (binderr == -1)
240               {
241                 res = res->ai_next;
242                 if (res) {
243                         (void)close(s);
244                         continue;
245                 }
246                 getnameinfo(bindres->ai_addr, bindres->ai_addrlen,
247                             nameinfo, sizeof(nameinfo), NULL, 0,
248                             NI_NUMERICHOST);
249                 /* XXX check error? */
250                 warn("Can't bind to %s", nameinfo);
251                 goto cleanup_url_get;
252               }
253         }
254
255         if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
256                 res = res->ai_next;
257                 if (res) {
258                         (void)close(s);
259                         continue;
260                 }
261                 warn("Can't connect to %s", host);
262                 goto cleanup_url_get;
263         }
264
265         break;
266       }
267         freeaddrinfo(res0);
268         res0 = NULL;
269
270         /*
271          * Construct and send the request.  We're expecting a return
272          * status of "200". Proxy requests don't want leading /.
273          */
274         if (!proxy) {
275                 printf("Requesting %s\n", origline);
276                 len = asprintf(&http_buffer,
277                     "GET /%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path, host);
278         } else {
279                 printf("Requesting %s (via %s)\n", origline, proxyenv);
280                 len = asprintf(&http_buffer,
281                     "GET %s HTTP/1.0\r\n\r\n", path);
282         }
283         if (len < 0 || http_buffer == NULL) {
284                 warnx("Failed to format HTTP request");
285                 goto cleanup_url_get;
286         }
287         if (write(s, http_buffer, len) < len) {
288                 warn("Writing HTTP request");
289                 free(http_buffer);
290                 goto cleanup_url_get;
291         }
292         free(http_buffer);
293         memset(buf, 0, sizeof(buf));
294         for (cp = buf; cp < buf + sizeof(buf); ) {
295                 if (read(s, cp, 1) != 1)
296                         goto improper;
297                 if (*cp == '\r')
298                         continue;
299                 if (*cp == '\n')
300                         break;
301                 cp++;
302         }
303         buf[sizeof(buf) - 1] = '\0';            /* sanity */
304         cp = strchr(buf, ' ');
305         if (cp == NULL)
306                 goto improper;
307         else
308                 cp++;
309         if (strncmp(cp, "200", 3)) {
310                 warnx("Error retrieving file: %s", cp);
311                 goto cleanup_url_get;
312         }
313
314         /*
315          * Read the rest of the header.
316          */
317         memset(buf, 0, sizeof(buf));
318         c = '\0';
319         for (cp = buf; cp < buf + sizeof(buf); ) {
320                 if (read(s, cp, 1) != 1)
321                         goto improper;
322                 if (*cp == '\r')
323                         continue;
324                 if (*cp == '\n' && c == '\n')
325                         break;
326                 c = *cp;
327                 cp++;
328         }
329         buf[sizeof(buf) - 1] = '\0';            /* sanity */
330
331         /*
332          * Look for the "Content-length: " header.
333          */
334 #define CONTENTLEN "Content-Length: "
335         for (cp = buf; *cp != '\0'; cp++) {
336                 if (tolower((unsigned char)*cp) == 'c' &&
337                     strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0)
338                         break;
339         }
340         if (*cp != '\0') {
341                 cp += sizeof(CONTENTLEN) - 1;
342                 ep = strchr(cp, '\n');
343                 if (ep == NULL)
344                         goto improper;
345                 else
346                         *ep = '\0';
347                 filesize = strtol(cp, &ep, 10);
348                 if (filesize < 1 || *ep != '\0')
349                         goto improper;
350         } else
351                 filesize = -1;
352
353         /* Open the output file. */
354         out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, 0666);
355         if (out < 0) {
356                 warn("Can't open %s", savefile);
357                 goto cleanup_url_get;
358         }
359
360         /* Trap signals */
361         oldintr = NULL;
362         if (setjmp(httpabort)) {
363                 if (oldintr)
364                         (void)signal(SIGINT, oldintr);
365                 goto cleanup_url_get;
366         }
367         oldintr = signal(SIGINT, aborthttp);
368
369         bytes = 0;
370         hashbytes = mark;
371         progressmeter(-1);
372
373         /* Finally, suck down the file. */
374         i = 0;
375         while ((len = read(s, buf, sizeof(buf))) > 0) {
376                 bytes += len;
377                 for (cp = buf; len > 0; len -= i, cp += i) {
378                         if ((i = write(out, cp, len)) == -1) {
379                                 warn("Writing %s", savefile);
380                                 goto cleanup_url_get;
381                         }
382                         else if (i == 0)
383                                 break;
384                 }
385                 if (hash && !progress) {
386                         while (bytes >= hashbytes) {
387                                 (void)putchar('#');
388                                 hashbytes += mark;
389                         }
390                         (void)fflush(stdout);
391                 }
392         }
393         if (hash && !progress && bytes > 0) {
394                 if (bytes < mark)
395                         (void)putchar('#');
396                 (void)putchar('\n');
397                 (void)fflush(stdout);
398         }
399         if (len != 0) {
400                 warn("Reading from socket");
401                 goto cleanup_url_get;
402         }
403         progressmeter(1);
404         if (verbose)
405                 puts("Successfully retrieved file.");
406         (void)signal(SIGINT, oldintr);
407
408         close(s);
409         close(out);
410         if (proxy)
411                 free(proxy);
412         free(line);
413         return (0);
414
415 noftpautologin:
416         warnx(
417             "Auto-login using ftp URLs isn't supported when using $ftp_proxy");
418         goto cleanup_url_get;
419
420 improper:
421         warnx("Improper response from %s", host);
422
423 cleanup_url_get:
424         if (s != -1)
425                 close(s);
426         if (proxy)
427                 free(proxy);
428         free(line);
429         if (res0 != NULL)
430                 freeaddrinfo(res0);
431         return (-1);
432 }
433
434 /*
435  * Abort a http retrieval
436  */
437 void
438 aborthttp(notused)
439         int notused;
440 {
441
442         alarmtimer(0);
443         puts("\nhttp fetch aborted.");
444         (void)fflush(stdout);
445         longjmp(httpabort, 1);
446 }
447
448 /*
449  * Retrieve multiple files from the command line, transferring
450  * files of the form "host:path", "ftp://host/path" using the
451  * ftp protocol, and files of the form "http://host/path" using
452  * the http protocol.
453  * If path has a trailing "/", then return (-1);
454  * the path will be cd-ed into and the connection remains open,
455  * and the function will return -1 (to indicate the connection
456  * is alive).
457  * If an error occurs the return value will be the offset+1 in
458  * argv[] of the file that caused a problem (i.e, argv[x]
459  * returns x+1)
460  * Otherwise, 0 is returned if all files retrieved successfully.
461  */
462 int
463 auto_fetch(argc, argv)
464         int argc;
465         char *argv[];
466 {
467         static char lasthost[MAXHOSTNAMELEN];
468         char *xargv[5];
469         char *cp, *line, *host, *dir, *file, *portnum;
470         char *user, *pass;
471         char *ftpproxy, *httpproxy;
472         int rval, xargc;
473         volatile int argpos;
474         int dirhasglob, filehasglob;
475         char rempath[MAXPATHLEN];
476
477         argpos = 0;
478
479         if (setjmp(toplevel)) {
480                 if (connected)
481                         disconnect(0, NULL);
482                 return (argpos + 1);
483         }
484         (void)signal(SIGINT, (sig_t)intr);
485         (void)signal(SIGPIPE, (sig_t)lostpeer);
486
487         ftpproxy = getenv(FTP_PROXY);
488         httpproxy = getenv(HTTP_PROXY);
489
490         /*
491          * Loop through as long as there's files to fetch.
492          */
493         for (rval = 0; (rval == 0) && (argpos < argc); free(line), argpos++) {
494                 if (strchr(argv[argpos], ':') == NULL)
495                         break;
496                 host = dir = file = portnum = user = pass = NULL;
497
498                 /*
499                  * We muck with the string, so we make a copy.
500                  */
501                 line = strdup(argv[argpos]);
502                 if (line == NULL)
503                         errx(1, "Can't allocate memory for auto-fetch.");
504
505                 /*
506                  * Try HTTP URL-style arguments first.
507                  */
508                 if (strncasecmp(line, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
509                         if (url_get(line, httpproxy) == -1)
510                                 rval = argpos + 1;
511                         continue;
512                 }
513
514                 /*
515                  * Try FTP URL-style arguments next. If ftpproxy is
516                  * set, use url_get() instead of standard ftp.
517                  * Finally, try host:file.
518                  */
519                 host = line;
520                 if (strncasecmp(line, FTP_URL, sizeof(FTP_URL) - 1) == 0) {
521                         if (ftpproxy) {
522                                 if (url_get(line, ftpproxy) == -1)
523                                         rval = argpos + 1;
524                                 continue;
525                         }
526                         host += sizeof(FTP_URL) - 1;
527                         dir = strchr(host, '/');
528
529                                 /* look for [user:pass@]host[:port] */
530                         pass = strpbrk(host, ":@/");
531                         if (pass == NULL || *pass == '/') {
532                                 pass = NULL;
533                                 goto parsed_url;
534                         }
535                         if (pass == host || *pass == '@') {
536 bad_ftp_url:
537                                 warnx("Invalid URL: %s", argv[argpos]);
538                                 rval = argpos + 1;
539                                 continue;
540                         }
541                         *pass++ = '\0';
542                         cp = strpbrk(pass, ":@/");
543                         if (cp == NULL || *cp == '/') {
544                                 portnum = pass;
545                                 pass = NULL;
546                                 goto parsed_url;
547                         }
548                         if (EMPTYSTRING(cp) || *cp == ':')
549                                 goto bad_ftp_url;
550                         *cp++ = '\0';
551                         user = host;
552                         if (EMPTYSTRING(user))
553                                 goto bad_ftp_url;
554                         host = cp;
555                         portnum = strchr(host, ':');
556                         if (portnum != NULL)
557                                 *portnum++ = '\0';
558                 } else {                        /* classic style `host:file' */
559                         char *end_brace;
560                         
561                         if (*host == '[' &&
562                             (end_brace = strrchr(host, ']')) != NULL) {
563                                 /*IPv6 addr in []*/
564                                 host++;
565                                 *end_brace = '\0';
566                                 dir = strchr(end_brace + 1, ':');
567                         } else
568                                 dir = strchr(host, ':');
569                 }
570 parsed_url:
571                 if (EMPTYSTRING(host)) {
572                         rval = argpos + 1;
573                         continue;
574                 }
575
576                 /*
577                  * If dir is NULL, the file wasn't specified
578                  * (URL looked something like ftp://host)
579                  */
580                 if (dir != NULL)
581                         *dir++ = '\0';
582
583                 /*
584                  * Extract the file and (if present) directory name.
585                  */
586                 if (! EMPTYSTRING(dir)) {
587                         cp = strrchr(dir, '/');
588                         if (cp != NULL) {
589                                 *cp++ = '\0';
590                                 file = cp;
591                         } else {
592                                 file = dir;
593                                 dir = NULL;
594                         }
595                 }
596                 if (debug)
597                         printf("user %s:%s host %s port %s dir %s file %s\n",
598                             user, pass, host, portnum, dir, file);
599
600                 /*
601                  * Set up the connection if we don't have one.
602                  */
603                 if (strcmp(host, lasthost) != 0) {
604                         int oautologin;
605
606                         (void)strcpy(lasthost, host);
607                         if (connected)
608                                 disconnect(0, NULL);
609                         xargv[0] = __progname;
610                         xargv[1] = host;
611                         xargv[2] = NULL;
612                         xargc = 2;
613                         if (! EMPTYSTRING(portnum)) {
614                                 xargv[2] = portnum;
615                                 xargv[3] = NULL;
616                                 xargc = 3;
617                         }
618                         oautologin = autologin;
619                         if (user != NULL)
620                                 autologin = 0;
621                         setpeer(xargc, xargv);
622                         autologin = oautologin;
623                         if ((connected == 0)
624                          || ((connected == 1) && !login(host, user, pass)) ) {
625                                 warnx("Can't connect or login to host `%s'",
626                                     host);
627                                 rval = argpos + 1;
628                                 continue;
629                         }
630
631                         /* Always use binary transfers. */
632                         setbinary(0, NULL);
633                 }
634                         /* cd back to '/' */
635                 xargv[0] = "cd";
636                 xargv[1] = "/";
637                 xargv[2] = NULL;
638                 cd(2, xargv);
639                 if (! dirchange) {
640                         rval = argpos + 1;
641                         continue;
642                 }
643
644                 dirhasglob = filehasglob = 0;
645                 if (doglob) {
646                         if (! EMPTYSTRING(dir) &&
647                             strpbrk(dir, "*?[]{}") != NULL)
648                                 dirhasglob = 1;
649                         if (! EMPTYSTRING(file) &&
650                             strpbrk(file, "*?[]{}") != NULL)
651                                 filehasglob = 1;
652                 }
653
654                 /* Change directories, if necessary. */
655                 if (! EMPTYSTRING(dir) && !dirhasglob) {
656                         xargv[0] = "cd";
657                         xargv[1] = dir;
658                         xargv[2] = NULL;
659                         cd(2, xargv);
660                         if (! dirchange) {
661                                 rval = argpos + 1;
662                                 continue;
663                         }
664                 }
665
666                 if (EMPTYSTRING(file)) {
667                         rval = -1;
668                         continue;
669                 }
670
671                 if (!verbose)
672                         printf("Retrieving %s/%s\n", dir ? dir : "", file);
673
674                 if (dirhasglob) {
675                         snprintf(rempath, sizeof(rempath), "%s/%s", dir, file);
676                         file = rempath;
677                 }
678
679                 /* Fetch the file(s). */
680                 xargv[0] = "get";
681                 xargv[1] = file;
682                 xargv[2] = NULL;
683                 if (dirhasglob || filehasglob) {
684                         int ointeractive;
685
686                         ointeractive = interactive;
687                         interactive = 0;
688                         xargv[0] = "mget";
689                         mget(2, xargv);
690                         interactive = ointeractive;
691                 } else
692                         get(2, xargv);
693
694                 if ((code / 100) != COMPLETE)
695                         rval = argpos + 1;
696         }
697         if (connected && rval != -1)
698                 disconnect(0, NULL);
699         return (rval);
700 }
701
702 int
703 isurl(p)
704         const char *p;
705 {
706         char *path, pton_buf[16];
707
708         if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0
709          || strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) {
710                 return 1;
711         }
712         if (*p == '[' && (path = strrchr(p, ']')) != NULL) /*IPv6 addr in []*/
713                 return (*(++path) == ':') ? 1 : 0;
714 #ifdef INET6
715         if (inet_pton(AF_INET6, p, pton_buf) == 1) /* raw IPv6 addr */
716                 return 0;
717 #endif
718         if (strchr(p, ':') != NULL) /* else, if ':' exist */
719                 return 1;
720         return 0;
721 }