X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuclient.git;a=blobdiff_plain;f=uclient-http.c;h=bff6d38ca861870109aa5b1edd65723cae53d0d5;hp=c3d1d5a7e06d04a8ae4d1e3a384e6bf289d60f9b;hb=7e7fe908839199cb28613d1f57eaeecf7d735bd3;hpb=efdfa39932526150b5303ddedcee9ce0ed835802 diff --git a/uclient-http.c b/uclient-http.c index c3d1d5a..bff6d38 100644 --- a/uclient-http.c +++ b/uclient-http.c @@ -12,9 +12,6 @@ #include "uclient-utils.h" #include "uclient-backend.h" -static struct ustream_ssl_ctx *ssl_ctx; -static uint32_t nc; - enum auth_type { AUTH_TYPE_UNKNOWN, AUTH_TYPE_NONE, @@ -47,11 +44,13 @@ static const char * const request_types[__REQ_MAX] = { struct uclient_http { struct uclient uc; + struct ustream_ssl_ctx *ssl_ctx; struct ustream *us; struct ustream_fd ufd; struct ustream_ssl ussl; + bool ssl_ctx_ext; bool ssl; bool eof; bool connection_close; @@ -64,6 +63,8 @@ struct uclient_http { long read_chunked; long content_length; + uint32_t nc; + struct blob_buf headers; struct blob_buf meta; }; @@ -275,7 +276,7 @@ static bool strmatch(char **str, const char *prefix) static void get_cnonce(char *dest) { - uint32_t val = nc; + uint32_t val = 0; FILE *f; f = fopen("/dev/urandom", "r"); @@ -376,7 +377,7 @@ uclient_http_add_auth_digest(struct uclient_http *uh) if (!realm || !data.qop || !data.nonce) return; - sprintf(nc_str, "%08x", nc++); + sprintf(nc_str, "%08x", uh->nc++); get_cnonce(cnonce_str); data.qop = "auth"; @@ -501,6 +502,19 @@ static void uclient_parse_http_line(struct uclient_http *uh, char *data) char *sep; if (uh->state == HTTP_STATE_REQUEST_DONE) { + char *code; + + /* HTTP/1.1 */ + strsep(&data, " "); + + code = strsep(&data, " "); + if (!code) + goto error; + + uh->uc.status_code = strtoul(code, &sep, 10); + if (sep && *sep) + goto error; + uh->state = HTTP_STATE_RECV_HEADERS; return; } @@ -524,6 +538,12 @@ static void uclient_parse_http_line(struct uclient_http *uh, char *data) sep++; blobmsg_add_string(&uh->meta, name, sep); + return; + +error: + uh->uc.status_code = 400; + uh->eof = true; + uclient_notify_eof(uh); } static void __uclient_notify_read(struct uclient_http *uh) @@ -634,13 +654,13 @@ static int uclient_setup_https(struct uclient_http *uh) if (ret) return ret; - if (!ssl_ctx) - ssl_ctx = ustream_ssl_context_new(false); + if (!uh->ssl_ctx) + uh->ssl_ctx = ustream_ssl_context_new(false); us->string_data = true; us->notify_state = uclient_ssl_notify_state; us->notify_read = uclient_ssl_notify_read; - ustream_ssl_init(&uh->ussl, &uh->ufd.stream, ssl_ctx, false); + ustream_ssl_init(&uh->ussl, &uh->ufd.stream, uh->ssl_ctx, false); return 0; } @@ -648,6 +668,7 @@ static int uclient_setup_https(struct uclient_http *uh) static int uclient_http_connect(struct uclient *cl) { struct uclient_http *uh = container_of(cl, struct uclient_http, uc); + int ret; uclient_http_init_request(uh); @@ -657,9 +678,14 @@ static int uclient_http_connect(struct uclient *cl) uh->ssl = cl->url->prefix == PREFIX_HTTPS; if (uh->ssl) - return uclient_setup_https(uh); + ret = uclient_setup_https(uh); else - return uclient_setup_http(uh); + ret = uclient_setup_http(uh); + + if (ret) + uh->state = HTTP_STATE_ERROR; + + return ret; } static struct uclient *uclient_http_alloc(void) @@ -672,10 +698,19 @@ static struct uclient *uclient_http_alloc(void) return &uh->uc; } +static void uclient_http_free_ssl_ctx(struct uclient_http *uh) +{ + if (uh->ssl_ctx && !uh->ssl_ctx_ext) + ustream_ssl_context_free(uh->ssl_ctx); + + uh->ssl_ctx_ext = false; +} + static void uclient_http_free(struct uclient *cl) { struct uclient_http *uh = container_of(cl, struct uclient_http, uc); + uclient_http_free_ssl_ctx(uh); uclient_http_free_url_state(cl); blob_buf_free(&uh->headers); blob_buf_free(&uh->meta); @@ -741,7 +776,8 @@ uclient_http_send_data(struct uclient *cl, char *buf, unsigned int len) uclient_http_send_headers(uh); ustream_printf(uh->us, "%X\r\n", len); - ustream_write(uh->us, buf, len, false); + if (len > 0) + ustream_write(uh->us, buf, len, false); ustream_printf(uh->us, "\r\n"); return len; @@ -830,7 +866,58 @@ uclient_http_read(struct uclient *cl, char *buf, unsigned int len) return len; } -const struct uclient_backend uclient_backend_http __hidden = { +bool uclient_http_redirect(struct uclient *cl) +{ + struct uclient_http *uh = container_of(cl, struct uclient_http, uc); + struct blobmsg_policy location = { + .name = "location", + .type = BLOBMSG_TYPE_STRING, + }; + struct uclient_url *url = cl->url; + struct blob_attr *tb; + + if (cl->backend != &uclient_backend_http) + return false; + + switch (cl->status_code) { + case 301: + case 302: + case 307: + break; + default: + return false; + } + + blobmsg_parse(&location, 1, &tb, blob_data(uh->meta.head), blob_len(uh->meta.head)); + if (!tb) + return false; + + url = uclient_get_url(blobmsg_data(tb), url->auth); + if (!url) + return false; + + free(cl->url); + cl->url = url; + uclient_http_connect(cl); + uclient_http_request_done(cl); + + return true; +} + +int uclient_http_set_ssl_ctx(struct uclient *cl, struct ustream_ssl_ctx *ctx) +{ + struct uclient_http *uh = container_of(cl, struct uclient_http, uc); + + uclient_http_free_url_state(cl); + + uclient_http_free_ssl_ctx(uh); + uh->ssl_ctx = ctx; + uh->ssl_ctx_ext = !!ctx; + + return 0; +} + +const struct uclient_backend uclient_backend_http = { .prefix = uclient_http_prefix, .alloc = uclient_http_alloc,