X-Git-Url: http://git.openwrt.org/?p=project%2Fuclient.git;a=blobdiff_plain;f=uclient-http.c;h=349e69cdd4aeb7a13859d0db71f5f277d3b7e580;hp=e58cf274b3a6cef5b06929462c7638f3d648afa1;hb=32ff717ed316e2d29563dcccfb272144dee9973c;hpb=07b4860975bd919fc8a8ce75d14ca8216eab70aa diff --git a/uclient-http.c b/uclient-http.c index e58cf27..349e69c 100644 --- a/uclient-http.c +++ b/uclient-http.c @@ -15,10 +15,12 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include #include #include +#include #include #include @@ -90,6 +92,8 @@ struct uclient_http { long read_chunked; long content_length; + int usock_flags; + uint32_t nc; struct blob_buf headers; @@ -120,7 +124,9 @@ static int uclient_do_connect(struct uclient_http *uh, const char *port) memset(&uh->uc.remote_addr, 0, sizeof(uh->uc.remote_addr)); - fd = usock_inet(USOCK_TCP, uh->uc.url->host, port, &uh->uc.remote_addr); + fd = usock_inet_timeout(USOCK_TCP | USOCK_NONBLOCK | uh->usock_flags, + uh->uc.url->host, port, &uh->uc.remote_addr, + uh->uc.timeout_msecs); if (fd < 0) return -1; @@ -143,7 +149,8 @@ static void uclient_http_disconnect(struct uclient_http *uh) if (uh->ssl) ustream_free(&uh->ussl.stream); ustream_free(&uh->ufd.stream); - close(uh->ufd.fd.fd); + if(uh->ufd.fd.fd) + close(uh->ufd.fd.fd); uh->us = NULL; } @@ -192,6 +199,10 @@ static void uclient_notify_eof(struct uclient_http *uh) return; } + if ((uh->content_length < 0 && uh->read_chunked >= 0) || + uh->content_length == 0) + uh->uc.data_eof = true; + uclient_backend_set_eof(&uh->uc); if (uh->connection_close) @@ -278,7 +289,19 @@ static void uclient_http_process_headers(struct uclient_http *uh) uh->auth_type = uclient_http_update_auth_type(uh); } -static void +static bool uclient_request_supports_body(enum request_type req_type) +{ + switch (req_type) { + case REQ_POST: + case REQ_PUT: + case REQ_DELETE: + return true; + default: + return false; + } +} + +static int uclient_http_add_auth_basic(struct uclient_http *uh) { struct uclient_url *url = uh->uc.url; @@ -286,11 +309,16 @@ uclient_http_add_auth_basic(struct uclient_http *uh) char *auth_buf; if (auth_len > 512) - return; + return -EINVAL; auth_buf = alloca(base64_len(auth_len) + 1); + if (!auth_buf) + return -ENOMEM; + base64_encode(url->auth, auth_len, auth_buf); ustream_printf(uh->us, "Authorization: Basic %s\r\n", auth_buf); + + return 0; } static char *digest_unquote_sep(char **str) @@ -359,11 +387,14 @@ get_cnonce(char *dest) { uint32_t val = 0; FILE *f; + size_t n; f = fopen("/dev/urandom", "r"); if (f) { - fread(&val, sizeof(val), 1, f); + n = fread(&val, sizeof(val), 1, f); fclose(f); + if (n != 1) + return; } bin_to_hex(dest, &val, sizeof(val)); @@ -404,7 +435,7 @@ static void add_field(char **buf, int *ofs, int *len, const char *name, const ch *ofs = cur - *buf; } -static void +static int uclient_http_add_auth_digest(struct uclient_http *uh) { struct uclient_url *url = uh->uc.url; @@ -412,6 +443,7 @@ uclient_http_add_auth_digest(struct uclient_http *uh) const char *user, *password; char *buf, *next; int len, ofs; + int err = 0; char cnonce_str[9]; char nc_str[9]; @@ -425,10 +457,17 @@ uclient_http_add_auth_digest(struct uclient_http *uh) }; len = strlen(uh->auth_str) + 1; - if (len > 512) - return; + if (len > 512) { + err = -EINVAL; + goto fail; + } buf = alloca(len); + if (!buf) { + err = -ENOMEM; + goto fail; + } + strcpy(buf, uh->auth_str); /* skip auth type */ @@ -466,8 +505,10 @@ uclient_http_add_auth_digest(struct uclient_http *uh) *dest = digest_unquote_sep(&next); } - if (!realm || !data.qop || !data.nonce) - return; + if (!realm || !data.qop || !data.nonce) { + err = -EINVAL; + goto fail; + } sprintf(nc_str, "%08x", uh->nc++); get_cnonce(cnonce_str); @@ -481,10 +522,17 @@ uclient_http_add_auth_digest(struct uclient_http *uh) char *user_buf; len = password - url->auth; - if (len > 256) - return; + if (len > 256) { + err = -EINVAL; + goto fail; + } user_buf = alloca(len + 1); + if (!user_buf) { + err = -ENOMEM; + goto fail; + } + strncpy(user_buf, url->auth, len); user_buf[len] = 0; user = user_buf; @@ -511,59 +559,77 @@ uclient_http_add_auth_digest(struct uclient_http *uh) add_field(&buf, &ofs, &len, "opaque", opaque); ustream_printf(uh->us, "Authorization: Digest nc=%s, qop=%s%s\r\n", data.nc, data.qop, buf); + free(buf); + + return 0; + +fail: + return err; } -static void +static int uclient_http_add_auth_header(struct uclient_http *uh) { if (!uh->uc.url->auth) - return; + return 0; switch (uh->auth_type) { case AUTH_TYPE_UNKNOWN: case AUTH_TYPE_NONE: break; case AUTH_TYPE_BASIC: - uclient_http_add_auth_basic(uh); - break; + return uclient_http_add_auth_basic(uh); case AUTH_TYPE_DIGEST: - uclient_http_add_auth_digest(uh); - break; + return uclient_http_add_auth_digest(uh); } + + return 0; } -static void +static int uclient_http_send_headers(struct uclient_http *uh) { struct uclient_url *url = uh->uc.url; struct blob_attr *cur; enum request_type req_type = uh->req_type; - int rem; + bool literal_ipv6; + int err; + size_t rem; if (uh->state >= HTTP_STATE_HEADERS_SENT) - return; + return 0; if (uh->uc.proxy_url) url = uh->uc.proxy_url; + literal_ipv6 = strchr(url->host, ':'); + ustream_printf(uh->us, "%s %s HTTP/1.1\r\n" - "Host: %s\r\n", - request_types[req_type], - url->location, url->host); + "Host: %s%s%s%s%s\r\n", + request_types[req_type], url->location, + literal_ipv6 ? "[" : "", + url->host, + literal_ipv6 ? "]" : "", + url->port ? ":" : "", + url->port ? url->port : ""); blobmsg_for_each_attr(cur, uh->headers.head, rem) ustream_printf(uh->us, "%s: %s\r\n", blobmsg_name(cur), (char *) blobmsg_data(cur)); - if (uh->req_type == REQ_POST || uh->req_type == REQ_PUT) + if (uclient_request_supports_body(uh->req_type)) ustream_printf(uh->us, "Transfer-Encoding: chunked\r\n"); - uclient_http_add_auth_header(uh); + err = uclient_http_add_auth_header(uh); + if (err) + return err; ustream_printf(uh->us, "\r\n"); uh->state = HTTP_STATE_HEADERS_SENT; + + return 0; } static void uclient_http_headers_complete(struct uclient_http *uh) @@ -885,6 +951,9 @@ static struct uclient *uclient_http_alloc(void) struct uclient_http *uh; uh = calloc_a(sizeof(*uh)); + if (!uh) + return NULL; + uh->disconnect_t.cb = uclient_http_disconnect_cb; blob_buf_init(&uh->headers, 0); @@ -912,7 +981,7 @@ int uclient_http_set_request_type(struct uclient *cl, const char *type) { struct uclient_http *uh = container_of(cl, struct uclient_http, uc); - int i; + unsigned int i; if (cl->backend != &uclient_backend_http) return -1; @@ -960,11 +1029,14 @@ static int uclient_http_send_data(struct uclient *cl, const char *buf, unsigned int len) { struct uclient_http *uh = container_of(cl, struct uclient_http, uc); + int err; if (uh->state >= HTTP_STATE_REQUEST_DONE) return -1; - uclient_http_send_headers(uh); + err = uclient_http_send_headers(uh); + if (err) + return err; if (len > 0) { ustream_printf(uh->us, "%X\r\n", len); @@ -979,12 +1051,16 @@ static int uclient_http_request_done(struct uclient *cl) { struct uclient_http *uh = container_of(cl, struct uclient_http, uc); + int err; if (uh->state >= HTTP_STATE_REQUEST_DONE) return -1; - uclient_http_send_headers(uh); - if (uh->req_type == REQ_POST || uh->req_type == REQ_PUT) + err = uclient_http_send_headers(uh); + if (err) + return err; + + if (uclient_request_supports_body(uh->req_type)) ustream_printf(uh->us, "0\r\n\r\n"); uh->state = HTTP_STATE_REQUEST_DONE; @@ -1032,16 +1108,17 @@ uclient_http_read(struct uclient *cl, char *buf, unsigned int len) } } - if (len > data_end - data) - len = data_end - data; + unsigned int diff = data_end - data; + if (len > diff) + len = diff; if (uh->read_chunked >= 0) { - if (len > uh->read_chunked) + if (len > (unsigned long) uh->read_chunked) len = uh->read_chunked; uh->read_chunked -= len; } else if (uh->content_length >= 0) { - if (len > uh->content_length) + if (len > (unsigned long) uh->content_length) len = uh->content_length; uh->content_length -= len; @@ -1094,12 +1171,19 @@ int uclient_http_redirect(struct uclient *cl) if (!tb) return false; - url = uclient_get_url(blobmsg_data(tb), url->auth); + url = uclient_get_url_location(url, blobmsg_data(tb)); if (!url) return false; - free(cl->url); - cl->url = url; + if (cl->proxy_url) { + free(cl->proxy_url); + cl->proxy_url = url; + } + else { + free(cl->url); + cl->url = url; + } + if (uclient_http_connect(cl)) return -1; @@ -1126,6 +1210,28 @@ int uclient_http_set_ssl_ctx(struct uclient *cl, const struct ustream_ssl_ops *o return 0; } +int uclient_http_set_address_family(struct uclient *cl, int af) +{ + struct uclient_http *uh = container_of(cl, struct uclient_http, uc); + + if (cl->backend != &uclient_backend_http) + return -1; + + switch (af) { + case AF_INET: + uh->usock_flags = USOCK_IPV4ONLY; + break; + case AF_INET6: + uh->usock_flags = USOCK_IPV6ONLY; + break; + default: + uh->usock_flags = 0; + break; + } + + return 0; +} + const struct uclient_backend uclient_backend_http = { .prefix = uclient_http_prefix,