X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuclient.git;a=blobdiff_plain;f=uclient-http.c;h=8053be000f92a7588f6096463a99b0ebb194cc4b;hp=c3d1d5a7e06d04a8ae4d1e3a384e6bf289d60f9b;hb=25e44fc1e666fb333b3c53bcda90e44b0b74bf19;hpb=efdfa39932526150b5303ddedcee9ce0ed835802 diff --git a/uclient-http.c b/uclient-http.c index c3d1d5a..8053be0 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,14 @@ 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_require_validation; + bool ssl_ctx_ext; bool ssl; bool eof; bool connection_close; @@ -64,6 +64,8 @@ struct uclient_http { long read_chunked; long content_length; + uint32_t nc; + struct blob_buf headers; struct blob_buf meta; }; @@ -117,6 +119,14 @@ static void uclient_http_free_url_state(struct uclient *cl) uclient_http_disconnect(uh); } +static void uclient_http_error(struct uclient_http *uh, int code) +{ + uh->state = HTTP_STATE_ERROR; + uh->us->eof = true; + ustream_state_change(uh->us); + uclient_backend_set_error(&uh->uc, code); +} + static void uclient_notify_eof(struct uclient_http *uh) { struct ustream *us = uh->us; @@ -275,7 +285,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 +386,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 +511,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 +547,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) @@ -532,7 +561,7 @@ static void __uclient_notify_read(struct uclient_http *uh) char *data; int len; - if (uh->state < HTTP_STATE_REQUEST_DONE) + if (uh->state < HTTP_STATE_REQUEST_DONE || uh->state == HTTP_STATE_ERROR) return; data = ustream_get_read_buf(uh->us, &len); @@ -622,6 +651,34 @@ static void uclient_ssl_notify_state(struct ustream *us) uclient_notify_eof(uh); } +static void uclient_ssl_notify_error(struct ustream_ssl *ssl, int error, const char *str) +{ + struct uclient_http *uh = container_of(ssl, struct uclient_http, ussl); + + uclient_http_error(uh, UCLIENT_ERROR_CONNECT); +} + +static void uclient_ssl_notify_verify_error(struct ustream_ssl *ssl, int error, const char *str) +{ + struct uclient_http *uh = container_of(ssl, struct uclient_http, ussl); + + if (!uh->ssl_require_validation) + return; + + uclient_http_error(uh, UCLIENT_ERROR_SSL_INVALID_CERT); +} + +static void uclient_ssl_notify_connected(struct ustream_ssl *ssl) +{ + struct uclient_http *uh = container_of(ssl, struct uclient_http, ussl); + + if (!uh->ssl_require_validation) + return; + + if (!uh->ussl.valid_cn) + uclient_http_error(uh, UCLIENT_ERROR_SSL_CN_MISMATCH); +} + static int uclient_setup_https(struct uclient_http *uh) { struct ustream *us = &uh->ussl.stream; @@ -634,13 +691,17 @@ 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); + uh->ussl.notify_error = uclient_ssl_notify_error; + uh->ussl.notify_verify_error = uclient_ssl_notify_verify_error; + uh->ussl.notify_connected = uclient_ssl_notify_connected; + ustream_ssl_init(&uh->ussl, &uh->ufd.stream, uh->ssl_ctx, false); + ustream_ssl_set_peer_cn(&uh->ussl, uh->uc.url->host); return 0; } @@ -648,6 +709,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 +719,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) + uclient_http_error(uh, UCLIENT_ERROR_CONNECT); + + return ret; } static struct uclient *uclient_http_alloc(void) @@ -672,10 +739,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 +817,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 +907,62 @@ 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, bool require_validation) +{ + struct uclient_http *uh = container_of(cl, struct uclient_http, uc); + + if (cl->backend != &uclient_backend_http) + return -1; + + uclient_http_free_url_state(cl); + + uclient_http_free_ssl_ctx(uh); + uh->ssl_ctx = ctx; + uh->ssl_ctx_ext = !!ctx; + uh->ssl_require_validation = !!ctx && require_validation; + + return 0; +} + +const struct uclient_backend uclient_backend_http = { .prefix = uclient_http_prefix, .alloc = uclient_http_alloc,