[BACK]Return to url.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ftp

Annotation of src/usr.bin/ftp/url.c, Revision 1.1

1.1     ! kmos        1: /*
        !             2:  * Copyright (c) 2017 Sunil Nimmagadda <sunil@openbsd.org>
        !             3:  *
        !             4:  * Permission to use, copy, modify, and distribute this software for any
        !             5:  * purpose with or without fee is hereby granted, provided that the above
        !             6:  * copyright notice and this permission notice appear in all copies.
        !             7:  *
        !             8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
        !             9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
        !            11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            15:  */
        !            16:
        !            17: /*-
        !            18:  * Copyright (c) 1997 The NetBSD Foundation, Inc.
        !            19:  * All rights reserved.
        !            20:  *
        !            21:  * This code is derived from software contributed to The NetBSD Foundation
        !            22:  * by Jason Thorpe and Luke Mewburn.
        !            23:  *
        !            24:  * Redistribution and use in source and binary forms, with or without
        !            25:  * modification, are permitted provided that the following conditions
        !            26:  * are met:
        !            27:  * 1. Redistributions of source code must retain the above copyright
        !            28:  *    notice, this list of conditions and the following disclaimer.
        !            29:  * 2. Redistributions in binary form must reproduce the above copyright
        !            30:  *    notice, this list of conditions and the following disclaimer in the
        !            31:  *    documentation and/or other materials provided with the distribution.
        !            32:  *
        !            33:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
        !            34:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
        !            35:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        !            36:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
        !            37:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
        !            38:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
        !            39:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
        !            40:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
        !            41:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
        !            42:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        !            43:  * POSSIBILITY OF SUCH DAMAGE.
        !            44:  */
        !            45: #include <sys/types.h>
        !            46:
        !            47: #include <netinet/in.h>
        !            48: #include <resolv.h>
        !            49:
        !            50: #include <ctype.h>
        !            51: #include <err.h>
        !            52: #include <stdio.h>
        !            53: #include <stdlib.h>
        !            54: #include <string.h>
        !            55: #include <strings.h>
        !            56:
        !            57: #include "ftp.h"
        !            58: #include "xmalloc.h"
        !            59:
        !            60: #define BASICAUTH_LEN  1024
        !            61:
        !            62: static void    authority_parse(const char *, char **, char **, char **);
        !            63: static int     ipv6_parse(const char *, char **, char **);
        !            64: static int     unsafe_char(const char *);
        !            65:
        !            66: #ifndef NOSSL
        !            67: const char     *scheme_str[] = { "http:", "ftp:", "file:", "https:" };
        !            68: const char     *port_str[] = { "80", "21", NULL, "443" };
        !            69: #else
        !            70: const char     *scheme_str[] = { "http:", "ftp:", "file:" };
        !            71: const char     *port_str[] = { "80", "21", NULL };
        !            72: #endif
        !            73:
        !            74: int
        !            75: scheme_lookup(const char *str)
        !            76: {
        !            77:        size_t  i;
        !            78:
        !            79:        for (i = 0; i < nitems(scheme_str); i++)
        !            80:                if (strncasecmp(str, scheme_str[i], strlen(scheme_str[i])) == 0)
        !            81:                        return i;
        !            82:
        !            83:        return -1;
        !            84: }
        !            85:
        !            86: static int
        !            87: ipv6_parse(const char *str, char **host, char **port)
        !            88: {
        !            89:        char    *p;
        !            90:
        !            91:        if ((p = strchr(str, ']')) == NULL) {
        !            92:                warnx("%s: invalid IPv6 address: %s", __func__, str);
        !            93:                return 1;
        !            94:        }
        !            95:
        !            96:        *p++ = '\0';
        !            97:        if (strlen(str + 1) > 0)
        !            98:                *host = xstrdup(str + 1);
        !            99:
        !           100:        if (*p == '\0')
        !           101:                return 0;
        !           102:
        !           103:        if (*p++ != ':') {
        !           104:                warnx("%s: invalid port: %s", __func__, p);
        !           105:                free(*host);
        !           106:                return 1;
        !           107:        }
        !           108:
        !           109:        if (strlen(p) > 0)
        !           110:                *port = xstrdup(p);
        !           111:
        !           112:        return 0;
        !           113: }
        !           114:
        !           115: static void
        !           116: authority_parse(const char *str, char **host, char **port, char **basic_auth)
        !           117: {
        !           118:        char    *p;
        !           119:
        !           120:        if ((p = strchr(str, '@')) != NULL) {
        !           121:                *basic_auth = xcalloc(1, BASICAUTH_LEN);
        !           122:                if (b64_ntop((unsigned char *)str, p - str,
        !           123:                    *basic_auth, BASICAUTH_LEN) == -1)
        !           124:                        errx(1, "base64 encode failed");
        !           125:
        !           126:                str = ++p;
        !           127:        }
        !           128:
        !           129:        if ((p = strchr(str, ':')) != NULL) {
        !           130:                *p++ = '\0';
        !           131:                if (strlen(p) > 0)
        !           132:                        *port = xstrdup(p);
        !           133:        }
        !           134:
        !           135:        if (strlen(str) > 0)
        !           136:                *host = xstrdup(str);
        !           137: }
        !           138:
        !           139: struct url *
        !           140: url_parse(const char *str)
        !           141: {
        !           142:        struct url      *url;
        !           143:        const char      *p, *q;
        !           144:        char            *basic_auth, *host, *port, *path, *s;
        !           145:        size_t           len;
        !           146:        int              ipliteral, scheme;
        !           147:
        !           148:        p = str;
        !           149:        ipliteral = 0;
        !           150:        host = port = path = basic_auth = NULL;
        !           151:        while (isblank((unsigned char)*p))
        !           152:                p++;
        !           153:
        !           154:        if ((q = strchr(p, ':')) == NULL) {
        !           155:                warnx("%s: scheme missing: %s", __func__, str);
        !           156:                return NULL;
        !           157:        }
        !           158:
        !           159:        if ((scheme = scheme_lookup(p)) == -1) {
        !           160:                warnx("%s: invalid scheme: %s", __func__, p);
        !           161:                return NULL;
        !           162:        }
        !           163:
        !           164:        p = ++q;
        !           165:        if (strncmp(p, "//", 2) != 0) {
        !           166:                if (scheme == S_FILE)
        !           167:                        goto done;
        !           168:                else {
        !           169:                        warnx("%s: invalid url: %s", __func__, str);
        !           170:                        return NULL;
        !           171:                }
        !           172:        }
        !           173:
        !           174:        p += 2;
        !           175:        len = strlen(p);
        !           176:        /* Authority terminated by a '/' if present */
        !           177:        if ((q = strchr(p, '/')) != NULL)
        !           178:                len = q - p;
        !           179:
        !           180:        s = xstrndup(p, len);
        !           181:        if (*p == '[') {
        !           182:                if (ipv6_parse(s, &host, &port) != 0) {
        !           183:                        free(s);
        !           184:                        return NULL;
        !           185:                }
        !           186:                ipliteral = 1;
        !           187:        } else
        !           188:                authority_parse(s, &host, &port, &basic_auth);
        !           189:
        !           190:        free(s);
        !           191:        if (port == NULL && scheme != S_FILE)
        !           192:                port = xstrdup(port_str[scheme]);
        !           193:
        !           194:  done:
        !           195:        if (q != NULL)
        !           196:                path = xstrdup(q);
        !           197:
        !           198:        if (io_debug) {
        !           199:                fprintf(stderr,
        !           200:                    "scheme: %s\nhost: %s\nport: %s\npath: %s\n",
        !           201:                    scheme_str[scheme], host, port, path);
        !           202:        }
        !           203:
        !           204:        url = xcalloc(1, sizeof *url);
        !           205:        url->scheme = scheme;
        !           206:        url->host = host;
        !           207:        url->port = port;
        !           208:        url->path = path;
        !           209:        url->basic_auth = basic_auth;
        !           210:        url->ipliteral = ipliteral;
        !           211:        return url;
        !           212: }
        !           213:
        !           214: void
        !           215: url_free(struct url *url)
        !           216: {
        !           217:        if (url == NULL)
        !           218:                return;
        !           219:
        !           220:        free(url->host);
        !           221:        free(url->port);
        !           222:        free(url->path);
        !           223:        freezero(url->basic_auth, BASICAUTH_LEN);
        !           224:        free(url->fname);
        !           225:        free(url);
        !           226: }
        !           227:
        !           228: void
        !           229: url_connect(struct url *url, struct url *proxy, int timeout)
        !           230: {
        !           231:        switch (url->scheme) {
        !           232:        case S_HTTP:
        !           233:        case S_HTTPS:
        !           234:                http_connect(url, proxy, timeout);
        !           235:                break;
        !           236:        case S_FTP:
        !           237:                ftp_connect(url, proxy, timeout);
        !           238:                break;
        !           239:        }
        !           240: }
        !           241:
        !           242: struct url *
        !           243: url_request(struct url *url, struct url *proxy, off_t *offset, off_t *sz)
        !           244: {
        !           245:        switch (url->scheme) {
        !           246:        case S_HTTP:
        !           247:        case S_HTTPS:
        !           248:                return http_get(url, proxy, offset, sz);
        !           249:        case S_FTP:
        !           250:                return ftp_get(url, proxy, offset, sz);
        !           251:        case S_FILE:
        !           252:                return file_request(&child_ibuf, url, offset, sz);
        !           253:        }
        !           254:
        !           255:        return NULL;
        !           256: }
        !           257:
        !           258: void
        !           259: url_save(struct url *url, FILE *dst_fp, off_t *offset)
        !           260: {
        !           261:        switch (url->scheme) {
        !           262:        case S_HTTP:
        !           263:        case S_HTTPS:
        !           264:                http_save(url, dst_fp, offset);
        !           265:                break;
        !           266:        case S_FTP:
        !           267:                ftp_save(url, dst_fp, offset);
        !           268:                break;
        !           269:        case S_FILE:
        !           270:                file_save(url, dst_fp, offset);
        !           271:                break;
        !           272:        }
        !           273: }
        !           274:
        !           275: void
        !           276: url_close(struct url *url)
        !           277: {
        !           278:        switch (url->scheme) {
        !           279:        case S_HTTP:
        !           280:        case S_HTTPS:
        !           281:                http_close(url);
        !           282:                break;
        !           283:        case S_FTP:
        !           284:                ftp_quit(url);
        !           285:                break;
        !           286:        }
        !           287: }
        !           288:
        !           289: char *
        !           290: url_str(struct url *url)
        !           291: {
        !           292:        char    *host, *str;
        !           293:        int      custom_port;
        !           294:
        !           295:        custom_port = strcmp(url->port, port_str[url->scheme]) ? 1 : 0;
        !           296:        if (url->ipliteral)
        !           297:                xasprintf(&host, "[%s]", url->host);
        !           298:        else
        !           299:                host = xstrdup(url->host);
        !           300:
        !           301:        xasprintf(&str, "%s//%s%s%s%s",
        !           302:            scheme_str[url->scheme],
        !           303:            host,
        !           304:            custom_port ? ":" : "",
        !           305:            custom_port ? url->port : "",
        !           306:            url->path ? url->path : "/");
        !           307:
        !           308:        free(host);
        !           309:        return str;
        !           310: }
        !           311:
        !           312: /*
        !           313:  * Encode given URL, per RFC1738.
        !           314:  * Allocate and return string to the caller.
        !           315:  */
        !           316: char *
        !           317: url_encode(const char *path)
        !           318: {
        !           319:        size_t i, length, new_length;
        !           320:        char *epath, *epathp;
        !           321:
        !           322:        length = new_length = strlen(path);
        !           323:
        !           324:        /*
        !           325:         * First pass:
        !           326:         * Count unsafe characters, and determine length of the
        !           327:         * final URL.
        !           328:         */
        !           329:        for (i = 0; i < length; i++)
        !           330:                if (unsafe_char(path + i))
        !           331:                        new_length += 2;
        !           332:
        !           333:        epath = epathp = xmalloc(new_length + 1);       /* One more for '\0'. */
        !           334:
        !           335:        /*
        !           336:         * Second pass:
        !           337:         * Encode, and copy final URL.
        !           338:         */
        !           339:        for (i = 0; i < length; i++)
        !           340:                if (unsafe_char(path + i)) {
        !           341:                        snprintf(epathp, 4, "%%" "%02x",
        !           342:                            (unsigned char)path[i]);
        !           343:                        epathp += 3;
        !           344:                } else
        !           345:                        *(epathp++) = path[i];
        !           346:
        !           347:        *epathp = '\0';
        !           348:        return epath;
        !           349: }
        !           350:
        !           351: /*
        !           352:  * Determine whether the character needs encoding, per RFC1738:
        !           353:  *     - No corresponding graphic US-ASCII.
        !           354:  *     - Unsafe characters.
        !           355:  */
        !           356: static int
        !           357: unsafe_char(const char *c0)
        !           358: {
        !           359:        const char *unsafe_chars = " <>\"#{}|\\^~[]`";
        !           360:        const unsigned char *c = (const unsigned char *)c0;
        !           361:
        !           362:        /*
        !           363:         * No corresponding graphic US-ASCII.
        !           364:         * Control characters and octets not used in US-ASCII.
        !           365:         */
        !           366:        return (iscntrl(*c) || !isascii(*c) ||
        !           367:
        !           368:            /*
        !           369:             * Unsafe characters.
        !           370:             * '%' is also unsafe, if is not followed by two
        !           371:             * hexadecimal digits.
        !           372:             */
        !           373:            strchr(unsafe_chars, *c) != NULL ||
        !           374:            (*c == '%' && (!isxdigit(*++c) || !isxdigit(*++c))));
        !           375: }
        !           376:
        !           377: void
        !           378: log_request(const char *prefix, struct url *url, struct url *proxy)
        !           379: {
        !           380:        char    *host;
        !           381:        int      custom_port;
        !           382:
        !           383:        if (url->scheme == S_FILE)
        !           384:                return;
        !           385:
        !           386:        custom_port = strcmp(url->port, port_str[url->scheme]) ? 1 : 0;
        !           387:        if (url->ipliteral)
        !           388:                xasprintf(&host, "[%s]", url->host);
        !           389:        else
        !           390:                host = xstrdup(url->host);
        !           391:
        !           392:        if (proxy)
        !           393:                log_info("%s %s//%s%s%s%s"
        !           394:                    " (via %s//%s%s%s)\n",
        !           395:                    prefix,
        !           396:                    scheme_str[url->scheme],
        !           397:                    host,
        !           398:                    custom_port ? ":" : "",
        !           399:                    custom_port ? url->port : "",
        !           400:                    url->path ? url->path : "",
        !           401:
        !           402:                    /* via proxy part */
        !           403:                    (proxy->scheme == S_HTTP) ? "http" : "https",
        !           404:                    proxy->host,
        !           405:                    proxy->port ? ":" : "",
        !           406:                    proxy->port ? proxy->port : "");
        !           407:        else
        !           408:                log_info("%s %s//%s%s%s%s\n",
        !           409:                    prefix,
        !           410:                    scheme_str[url->scheme],
        !           411:                    host,
        !           412:                    custom_port ? ":" : "",
        !           413:                    custom_port ? url->port : "",
        !           414:                    url->path ? url->path : "");
        !           415:
        !           416:        free(host);
        !           417: }