diff --git a/docs/curl.1 b/docs/curl.1 index 02cc3fb63..aec3e48f5 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -927,6 +927,52 @@ in web browsers, so curl does the conversion by default to maintain consistency. However, a server may require a POST to remain a POST after such a redirection. This option is meaningful only when using \fI-L/--location\fP (Added in 7.19.1) +.IP "--proto " +Tells curl to use the listed protocols for its initial retrieval. Protocols +are evaluated left to right, are comma separated, and are each a protocol +name or 'all', optionally prefixed by zero or more modifiers. Available +modifiers are: +.RS +.TP 3 +.B + +Permit this protocol in addition to protocols already permitted (this is +the default if no modifier is used). +.TP +.B - +Deny this protocol, removing it from the list of protocols already permitted. +.TP +.B = +Permit only this protocol (ignoring the list already permitted), though +subject to later modification by subsequent entries in the comma separated +list. +.RE +.IP +For example: +.RS +.TP 15 +.B --proto -ftps +uses the default protocols, but disables ftps +.TP +.B --proto -all,https,+http +only enables http and https +.TP +.B --proto =http,https +also only enables http and https +.RE +.IP +Unknown protocols produce a warning. This allows scripts to safely rely on +being able to disable potentially dangerous protocols, without relying upon +support for that protocol being built into curl to avoid an error. + +This option can be used multiple times, in which case the effect is the same +as concatenating the protocols into one instance of the option. + +(Added in 7.20.2) +.IP "--proto-redir " +Tells curl to use the listed protocols after a redirect. See --proto for +how protocols are represented. + +(Added in 7.20.2) .IP "--proxy-anyauth" Tells curl to pick a suitable authentication method when communicating with the given proxy. This might cause an extra request/response round-trip. (Added diff --git a/src/main.c b/src/main.c index 2dd6dc873..684869379 100644 --- a/src/main.c +++ b/src/main.c @@ -478,6 +478,10 @@ struct Configurable { bool disable_epsv; bool disable_eprt; bool ftp_pret; + long proto; + bool proto_present; + long proto_redir; + bool proto_redir_present; curl_off_t resume_from; char *postfields; curl_off_t postfieldsize; @@ -841,6 +845,8 @@ static void help(void) " --post301 Do not switch to GET after following a 301 redirect (H)", " --post302 Do not switch to GET after following a 302 redirect (H)", " -#/--progress-bar Display transfer progress as a progress bar", + " --proto Enable/disable specified protocols", + " --proto-redir Enable/disable specified protocols on redirect", " -x/--proxy Use HTTP proxy on given port", " --proxy-anyauth Pick \"any\" proxy authentication method (H)", " --proxy-basic Use Basic authentication on the proxy (H)", @@ -1492,6 +1498,109 @@ static int str2num(long *val, const char *str) return retcode; } +/* + * Parse the string and modify the long in the given address. Return + * non-zero on failure, zero on success. + * + * The string is a list of protocols + * + * Since this function gets called with the 'nextarg' pointer from within the + * getparameter a lot, we must check it for NULL before accessing the str + * data. + */ + +static long proto2num(struct Configurable *config, long *val, const char *str) +{ + char *buffer; + const char *sep = ","; + char *token; + + static struct sprotos { + const char *name; + long bit; + } const protos[] = { + { "all", CURLPROTO_ALL }, + { "http", CURLPROTO_HTTP }, + { "https", CURLPROTO_HTTPS }, + { "ftp", CURLPROTO_FTP }, + { "ftps", CURLPROTO_FTPS }, + { "scp", CURLPROTO_SCP }, + { "sftp", CURLPROTO_SFTP }, + { "telnet", CURLPROTO_TELNET }, + { "ldap", CURLPROTO_LDAP }, + { "ldaps", CURLPROTO_LDAPS }, + { "dict", CURLPROTO_DICT }, + { "file", CURLPROTO_FILE }, + { "tftp", CURLPROTO_TFTP }, + { "imap", CURLPROTO_IMAP }, + { "imaps", CURLPROTO_IMAPS }, + { "pop3", CURLPROTO_POP3 }, + { "pop3s", CURLPROTO_POP3S }, + { "smtp", CURLPROTO_SMTP }, + { "smtps", CURLPROTO_SMTPS }, + { "rtsp", CURLPROTO_RTSP }, + { NULL, 0 } + }; + + if(!str) + return 1; + + buffer = strdup(str); /* because strtok corrupts it */ + + for (token = strtok(buffer, sep); + token; + token = strtok(NULL, sep)) { + enum e_action { allow, deny, set } action = allow; + + struct sprotos const *pp; + + /* Process token modifiers */ + while (!ISALNUM(*token)) { /* may be NULL if token is all modifiers */ + switch (*token++) { + case '=': + action = set; + break; + case '-': + action = deny; + break; + case '+': + action = allow; + break; + default: /* Includes case of terminating NULL */ + free(buffer); + return 1; + } + } + + for (pp=protos; pp->name; pp++) { + if (curlx_raw_equal(token, pp->name)) { + switch (action) { + case deny: + *val &= ~(pp->bit); + break; + case allow: + *val |= pp->bit; + break; + case set: + *val = pp->bit; + break; + } + break; + } + } + + if (!(pp->name)) { /* unknown protocol */ + /* If they have specified only this protocol, we say treat it as + if no protocols are allowed */ + if (action == set) + *val = 0; + warnf(config, "unrecognized protocol '%s'\n", token); + } + } + free(buffer); + return 0; +} + /** * Parses the given string looking for an offset (which may be * a larger-than-integer value). @@ -1752,6 +1861,8 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"$A", "mail-from", TRUE}, {"$B", "mail-rcpt", TRUE}, {"$C", "ftp-pret", FALSE}, + {"$D", "proto", TRUE}, + {"$E", "proto-redir", TRUE}, {"0", "http1.0", FALSE}, {"1", "tlsv1", FALSE}, {"2", "sslv2", FALSE}, @@ -2294,6 +2405,16 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ case 'C': /* --ftp-pret */ config->ftp_pret = toggle; break; + case 'D': /* --proto */ + config->proto_present = 1; + if(proto2num(config, &config->proto, nextarg)) + return PARAM_BAD_USE; + break; + case 'E': /* --proto-redir */ + config->proto_redir_present = 1; + if(proto2num(config, &config->proto_redir, nextarg)) + return PARAM_BAD_USE; + break; } break; case '#': /* --progress-bar */ @@ -4362,6 +4483,11 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) config->use_httpget=FALSE; config->create_dirs=FALSE; config->maxredirs = DEFAULT_MAXREDIRS; + config->proto = CURLPROTO_ALL; /* FIXME: better to read from library */ + config->proto_present = FALSE; + config->proto_redir = + CURLPROTO_ALL & ~(CURLPROTO_FILE|CURLPROTO_SCP); /* not FILE or SCP */ + config->proto_redir_present = FALSE; if(argc>1 && (!curlx_strnequal("--", argv[1], 2) && (argv[1][0] == '-')) && @@ -5200,6 +5326,11 @@ operate(struct Configurable *config, int argc, argv_item_t argv[]) if(config->ftp_pret) my_setopt(curl, CURLOPT_FTP_USE_PRET, TRUE); + if (config->proto_present) + my_setopt(curl, CURLOPT_PROTOCOLS, config->proto); + if (config->proto_redir_present) + my_setopt(curl, CURLOPT_REDIR_PROTOCOLS, config->proto_redir); + if ((urlnode->flags & GETOUT_USEREMOTE) && config->content_disposition) { my_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);