* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <sys/socket.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <stdint.h>
+#include <string.h>
#include <fcntl.h>
#include <libubox/ustream.h>
long read_chunked;
long content_length;
+ int usock_flags;
+
uint32_t nc;
struct blob_buf headers;
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;
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;
}
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)
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;
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)
{
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));
*ofs = cur - *buf;
}
-static void
+static int
uclient_http_add_auth_digest(struct uclient_http *uh)
{
struct uclient_url *url = uh->uc.url;
const char *user, *password;
char *buf, *next;
int len, ofs;
+ int err = 0;
char cnonce_str[9];
char nc_str[9];
};
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 */
*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);
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;
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)
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);
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;
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);
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;
}
}
- 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;
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;
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,