diff options
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | src/IO/IO.c | 21 | ||||
-rw-r--r-- | src/IO/Makefile.am | 7 | ||||
-rw-r--r-- | src/IO/Url.h | 5 | ||||
-rw-r--r-- | src/IO/http.c | 423 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/capi.c | 76 | ||||
-rw-r--r-- | src/dillo.cc | 3 |
8 files changed, 335 insertions, 206 deletions
diff --git a/configure.ac b/configure.ac index fb0a1121..b574f8d6 100644 --- a/configure.ac +++ b/configure.ac @@ -24,6 +24,8 @@ AC_ARG_ENABLE(insure, [ --enable-insure Try to compile and run with Ins , enable_insure=no) AC_ARG_ENABLE(ssl, [ --enable-ssl Enable ssl, https (ALPHA CODE)], , enable_ssl=no) +AC_ARG_WITH(ca-certs-file, [ --with-ca-certs-file=FILE Specify where to find a bundle of trusted CA certificates for SSL], CA_CERTS_FILE=$withval) +AC_ARG_WITH(ca-certs-dir, [ --with-ca-certs-dir=DIR Specify where to find a directory containing trusted CA certificates for SSL], CA_CERTS_DIR=$withval) AC_ARG_ENABLE(ipv6, [ --enable-ipv6 Build with support for IPv6], , ) AC_ARG_ENABLE(cookies,[ --disable-cookies Don't compile support for cookies], , enable_cookies=yes) @@ -506,6 +508,8 @@ AC_SUBST(LIBFLTK_CFLAGS) AC_SUBST(LIBFLTK_LIBS) AC_SUBST(LIBICONV_LIBS) AC_SUBST(LIBX11_LIBS) +AC_SUBST(CA_CERTS_FILE) +AC_SUBST(CA_CERTS_DIR) AC_SUBST(datadir) AC_CONFIG_FILES([ diff --git a/src/IO/IO.c b/src/IO/IO.c index 0addf486..e5c5fc79 100644 --- a/src/IO/IO.c +++ b/src/IO/IO.c @@ -21,6 +21,7 @@ #include "../klist.h" #include "IO.h" #include "iowatch.hh" +#include "ssl.h" /* * Symbolic defines for shutdown() function @@ -162,6 +163,7 @@ static bool_t IO_read(IOData_t *io) ssize_t St; bool_t ret = FALSE; int io_key = io->Key; + void *conn = a_Ssl_connection(io->FD); _MSG(" IO_read\n"); @@ -170,7 +172,8 @@ static bool_t IO_read(IOData_t *io) io->Status = 0; while (1) { - St = read(io->FD, Buf, IOBufLen); + St = conn ? a_Ssl_read(conn, Buf, IOBufLen) + : read(io->FD, Buf, IOBufLen); if (St > 0) { dStr_append_l(io->Buf, Buf, St); continue; @@ -214,12 +217,14 @@ static bool_t IO_write(IOData_t *io) { ssize_t St; bool_t ret = FALSE; + void *conn = a_Ssl_connection(io->FD); _MSG(" IO_write\n"); io->Status = 0; while (1) { - St = write(io->FD, io->Buf->str, io->Buf->len); + St = conn ? a_Ssl_write(conn, io->Buf->str, io->Buf->len) + : write(io->FD, io->Buf->str, io->Buf->len); if (St < 0) { /* Error */ if (errno == EINTR) { @@ -372,8 +377,8 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info, char *newline = memchr(io->Buf->str, '\n', io->Buf->len); int msglen = newline ? newline - io->Buf->str : 2048; - MSG_WARN("IO_write, closing with pending data not sent: " - "\"%s\"\n", dStr_printable(io->Buf, msglen)); + MSG("IO_write, closing with pending data not sent: \"%s\"\n", + dStr_printable(io->Buf, msglen)); } /* close FD, remove from ValidIOs and remove its watch */ IO_close_fd(io, Op == OpEnd ? IO_StopWr : IO_StopRdWr); @@ -381,7 +386,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info, dFree(Info); break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC IO 1B\n"); break; } } else { /* 1 FWD */ @@ -395,7 +400,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info, dFree(Info); break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC IO 1F\n"); break; } } @@ -424,7 +429,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info, dFree(Info); break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC IO 2B\n"); break; } } else { /* 2 FWD */ @@ -443,7 +448,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info, dFree(Info); break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC IO 2F\n"); break; } } diff --git a/src/IO/Makefile.am b/src/IO/Makefile.am index c889dae8..ff600521 100644 --- a/src/IO/Makefile.am +++ b/src/IO/Makefile.am @@ -1,6 +1,9 @@ AM_CPPFLAGS = \ -I$(top_srcdir) \ - -DDILLO_BINDIR='"$(bindir)/"' + -DDILLO_BINDIR='"$(bindir)/"' \ + -DCA_CERTS_FILE='"@CA_CERTS_FILE@"' \ + -DCA_CERTS_DIR='"@CA_CERTS_DIR@"' + AM_CFLAGS = @LIBFLTK_CFLAGS@ AM_CXXFLAGS = @LIBFLTK_CXXFLAGS@ @@ -12,6 +15,8 @@ libDiof_a_SOURCES = \ about.c \ Url.h \ http.c \ + ssl.h \ + ssl.c \ dpi.c \ IO.c \ iowatch.cc \ diff --git a/src/IO/Url.h b/src/IO/Url.h index d9333d67..3f5a559b 100644 --- a/src/IO/Url.h +++ b/src/IO/Url.h @@ -16,10 +16,7 @@ extern void a_Http_freeall(void); int a_Http_init(void); int a_Http_proxy_auth(void); void a_Http_set_proxy_passwd(const char *str); -char *a_Http_make_connect_str(const DilloUrl *url); -const char *a_Http_get_proxy_urlstr(); -Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, - int web_flags, bool_t use_proxy); +void a_Http_connect_done(int fd, bool_t success); void a_Http_ccc (int Op, int Branch, int Dir, ChainLink *Info, void *Data1, void *Data2); diff --git a/src/IO/http.c b/src/IO/http.c index 27d86263..5f97c0fd 100644 --- a/src/IO/http.c +++ b/src/IO/http.c @@ -27,6 +27,7 @@ #include <arpa/inet.h> /* for inet_ntop */ #include "IO.h" +#include "ssl.h" #include "Url.h" #include "../msg.h" #include "../klist.h" @@ -48,22 +49,22 @@ D_STMT_START { \ #define _MSG_BW(web, root, ...) -static const int HTTP_PORT = 80; - static const int HTTP_SOCKET_USE_PROXY = 0x1; -static const int HTTP_SOCKET_QUEUED = 0x4; -static const int HTTP_SOCKET_TO_BE_FREED = 0x8; +static const int HTTP_SOCKET_QUEUED = 0x2; +static const int HTTP_SOCKET_TO_BE_FREED = 0x4; +static const int HTTP_SOCKET_SSL = 0x8; -/* 'Url' and 'web' are just references (no need to deallocate them here). */ +/* 'web' is just a reference (no need to deallocate it here). */ typedef struct { int SockFD; - uint_t port; /* need a separate port in order to support PROXY */ + uint_t connect_port; uint_t flags; DilloWeb *web; /* reference to client's web structure */ + DilloUrl *url; Dlist *addr_list; /* Holds the DNS answer */ - int Err; /* Holds the errno of the connect() call */ ChainLink *Info; /* Used for CCC asynchronous operations */ char *connected_to; /* Used for per-host connection limit */ + Dstr *https_proxy_reply; } SocketData_t; /* Data structures and functions to queue sockets that need to be @@ -81,11 +82,11 @@ typedef struct { } FdMapEntry_t; static void Http_socket_enqueue(HostConnection_t *hc, SocketData_t* sock); -static SocketData_t* Http_socket_dequeue(HostConnection_t *hc); static HostConnection_t *Http_host_connection_get(const char *host); static void Http_host_connection_remove(HostConnection_t *hc); -static int Http_connect_socket(ChainLink *Info); -static void Http_send_query(ChainLink *Info, SocketData_t *S); +static void Http_connect_socket(ChainLink *Info, HostConnection_t *hc); +static char *Http_get_connect_str(const DilloUrl *url); +static void Http_send_query(SocketData_t *S); static void Http_socket_free(int SKey); /* @@ -170,7 +171,21 @@ static int Http_fd_map_cmp(const void *v1, const void *v2) int fd = VOIDP2INT(v2); const FdMapEntry_t *e = v1; - return (fd == e->fd) ? 0 : 1; + return (fd != e->fd); +} + +static void Http_fd_map_add_entry(SocketData_t *sd) +{ + FdMapEntry_t *e = dNew0(FdMapEntry_t, 1); + e->fd = sd->SockFD; + e->skey = VOIDP2INT(sd->Info->LocalKey); + + if (dList_find_custom(fd_map, INT2VOIDP(e->fd), Http_fd_map_cmp)) { + MSG_ERR("FD ENTRY ALREADY FOUND FOR %d\n", e->fd); + assert(0); + } + + dList_append(fd_map, e); } /* @@ -183,42 +198,78 @@ static void Http_fd_map_remove_entry(int fd) if (data) { dList_remove_fast(fd_map, data); dFree(data); + } else { + MSG("FD ENTRY NOT FOUND FOR %d\n", fd); + } +} + +void a_Http_connect_done(int fd, bool_t success) +{ + SocketData_t *sd; + FdMapEntry_t *fme = dList_find_custom(fd_map, INT2VOIDP(fd), + Http_fd_map_cmp); + + if (fme && (sd = a_Klist_get_data(ValidSocks, fme->skey))) { + ChainLink *info = sd->Info; + + if (success) { + a_Chain_bfcb(OpSend, info, &sd->SockFD, "FD"); + Http_send_query(sd); + } else { + MSG_BW(sd->web, 1, "Could not establish connection."); + MSG("fd %d is done and failed\n", sd->SockFD); + dClose(fd); + Http_socket_free(VOIDP2INT(info->LocalKey)); /* free sd */ + a_Chain_bfcb(OpAbort, info, NULL, "Both"); + dFree(info); + } + } else { + MSG("**** but no luck with fme %p or sd\n", fme); } } +static void Http_socket_activate(HostConnection_t *hc, SocketData_t *sd) +{ + dList_remove(hc->queue, sd); + sd->flags &= ~HTTP_SOCKET_QUEUED; + hc->active_conns++; + sd->connected_to = hc->host; +} + static void Http_connect_queued_sockets(HostConnection_t *hc) { SocketData_t *sd; - while (hc->active_conns < prefs.http_max_conns && - (sd = Http_socket_dequeue(hc))) { + int i; - sd->flags &= ~HTTP_SOCKET_QUEUED; + for (i = 0; + i < dList_length(hc->queue) && hc->active_conns < prefs.http_max_conns; + i++) { + sd = dList_nth_data(hc->queue, i); - if (sd->flags & HTTP_SOCKET_TO_BE_FREED) { - dFree(sd); - } else if (a_Web_valid(sd->web)) { - /* start connecting the socket */ - if (Http_connect_socket(sd->Info) < 0) { - ChainLink *Info = sd->Info; - MSG_BW(sd->web, 1, "ERROR: %s", dStrerror(sd->Err)); - a_Chain_bfcb(OpAbort, Info, NULL, "Both"); - Http_socket_free(VOIDP2INT(Info->LocalKey)); /* free sd */ - dFree(Info); - } else { - FdMapEntry_t *e = dNew0(FdMapEntry_t, 1); + if (!(sd->flags & HTTP_SOCKET_TO_BE_FREED)) { + int connect_ready = SSL_CONNECT_READY; - e->fd = sd->SockFD; - e->skey = VOIDP2INT(sd->Info->LocalKey); - dList_append(fd_map, e); + if (sd->flags & HTTP_SOCKET_SSL) + connect_ready = a_Ssl_connect_ready(sd->url); - hc->active_conns++; - a_Chain_bcb(OpSend, sd->Info, &sd->SockFD, "FD"); - a_Chain_fcb(OpSend, sd->Info, &sd->SockFD, "FD"); - Http_send_query(sd->Info, sd); - sd->connected_to = hc->host; + if (connect_ready == SSL_CONNECT_NEVER || !a_Web_valid(sd->web)) { + int SKey = VOIDP2INT(sd->Info->LocalKey); + + Http_socket_free(SKey); + } else if (connect_ready == SSL_CONNECT_READY) { + i--; + Http_socket_activate(hc, sd); + Http_connect_socket(sd->Info, hc); } } + if (sd->flags & HTTP_SOCKET_TO_BE_FREED) { + dList_remove(hc->queue, sd); + dFree(sd); + i--; + } } + + _MSG("Queue %s len %d\n", hc->host, dList_length(hc->queue)); } /* @@ -231,18 +282,25 @@ static void Http_socket_free(int SKey) if ((S = a_Klist_get_data(ValidSocks, SKey))) { a_Klist_remove(ValidSocks, SKey); + dStr_free(S->https_proxy_reply, 1); + if (S->flags & HTTP_SOCKET_QUEUED) { S->flags |= HTTP_SOCKET_TO_BE_FREED; + a_Url_free(S->url); } else { if (S->SockFD != -1) Http_fd_map_remove_entry(S->SockFD); + a_Ssl_reset_server_state(S->url); if (S->connected_to) { + a_Ssl_close_by_fd(S->SockFD); + HostConnection_t *hc = Http_host_connection_get(S->connected_to); hc->active_conns--; Http_connect_queued_sockets(hc); if (hc->active_conns == 0) Http_host_connection_remove(hc); } + a_Url_free(S->url); dFree(S); } } @@ -300,23 +358,22 @@ static Dstr *Http_make_content_type(const DilloUrl *url) /* * Make the http query string */ -Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, - int web_flags, bool_t use_proxy) +static Dstr *Http_make_query_str(DilloWeb *web, bool_t use_proxy) { char *ptr, *cookies, *referer, *auth; + const DilloUrl *url = web->url; Dstr *query = dStr_new(""), *request_uri = dStr_new(""), *proxy_auth = dStr_new(""); /* BUG: dillo doesn't actually understand application/xml yet */ const char *accept_hdr_value = - web_flags & WEB_Image ? "image/png,image/*;q=0.8,*/*;q=0.5" : - web_flags & WEB_Stylesheet ? "text/css,*/*;q=0.1" : + web->flags & WEB_Image ? "image/png,image/*;q=0.8,*/*;q=0.5" : + web->flags & WEB_Stylesheet ? "text/css,*/*;q=0.1" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; const char *connection_hdr_val = - (prefs.http_persistent_conns == TRUE && - !dStrAsciiCasecmp(URL_SCHEME(url), "http")) ? "keep-alive" : "close"; + (prefs.http_persistent_conns == TRUE) ? "keep-alive" : "close"; if (use_proxy) { dStr_sprintfa(request_uri, "%s%s", @@ -335,7 +392,7 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, (URL_PATH_(url) || URL_QUERY_(url)) ? "" : "/"); } - cookies = a_Cookies_get_query(url, requester); + cookies = a_Cookies_get_query(url, web->requester); auth = a_Auth_get_auth_str(url, request_uri->str); referer = Http_get_referer(url); if (URL_FLAGS(url) & URL_Post) { @@ -400,14 +457,13 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, const DilloUrl *requester, /* * Create and submit the HTTP query to the IO engine */ -static void Http_send_query(ChainLink *Info, SocketData_t *S) +static void Http_send_query(SocketData_t *S) { Dstr *query; DataBuf *dbuf; /* Create the query */ - query = a_Http_make_query_str(S->web->url, S->web->requester, S->web->flags, - S->flags & HTTP_SOCKET_USE_PROXY); + query = Http_make_query_str(S->web, S->flags & HTTP_SOCKET_USE_PROXY); dbuf = a_Chain_dbuf_new(query->str, query->len, 0); /* actually this message is sent too early. @@ -415,26 +471,49 @@ static void Http_send_query(ChainLink *Info, SocketData_t *S) _MSG_BW(S->web, 1, "Sending query to %s...", URL_HOST_(S->web->url)); /* send query */ - a_Chain_bcb(OpSend, Info, dbuf, NULL); + a_Chain_bcb(OpSend, S->Info, dbuf, NULL); dFree(dbuf); dStr_free(query, 1); } /* - * This function gets called after the DNS succeeds solving a hostname. + * Prepare an HTTPS connection. If necessary, tunnel it through a proxy. + * Then perform the SSL handshake. + */ +static void Http_connect_ssl(ChainLink *info) +{ + int SKey = VOIDP2INT(info->LocalKey); + SocketData_t *S = a_Klist_get_data(ValidSocks, SKey); + + if (S->flags & HTTP_SOCKET_USE_PROXY) { + char *connect_str = Http_get_connect_str(S->url); + DataBuf *dbuf = a_Chain_dbuf_new(connect_str, strlen(connect_str), 0); + + a_Chain_bfcb(OpSend, info, &S->SockFD, "FD"); + S->https_proxy_reply = dStr_new(NULL); + a_Chain_bcb(OpSend, info, dbuf, NULL); + + dFree(dbuf); + dFree(connect_str); + } else { + a_Ssl_handshake(S->SockFD, S->url); + } +} + +/* + * This function is called after the DNS succeeds in solving a hostname. * Task: Finish socket setup and start connecting the socket. - * Return value: 0 on success; -1 on error. */ -static int Http_connect_socket(ChainLink *Info) +static void Http_connect_socket(ChainLink *Info, HostConnection_t *hc) { int i, status; + SocketData_t *S; + DilloHost *dh; #ifdef ENABLE_IPV6 struct sockaddr_in6 name; #else struct sockaddr_in name; #endif - SocketData_t *S; - DilloHost *dh; socklen_t socket_len = 0; S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey)); @@ -442,10 +521,11 @@ static int Http_connect_socket(ChainLink *Info) /* TODO: iterate this address list until success, or end-of-list */ for (i = 0; (dh = dList_nth_data(S->addr_list, i)); ++i) { if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) { - S->Err = errno; MSG("Http_connect_socket ERROR: %s\n", dStrerror(errno)); continue; } + Http_fd_map_add_entry(S); + /* set NONBLOCKING and close on exec. */ fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL)); fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD)); @@ -459,10 +539,11 @@ static int Http_connect_socket(ChainLink *Info) struct sockaddr_in *sin = (struct sockaddr_in *)&name; socket_len = sizeof(struct sockaddr_in); sin->sin_family = dh->af; - sin->sin_port = S->port ? htons(S->port) : htons(HTTP_PORT); + sin->sin_port = htons(S->connect_port); memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen); if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl)) - MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr)); + MSG("Connecting to %s:%d\n", inet_ntoa(sin->sin_addr), + S->connect_port); break; } #ifdef ENABLE_IPV6 @@ -472,36 +553,34 @@ static int Http_connect_socket(ChainLink *Info) struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name; socket_len = sizeof(struct sockaddr_in6); sin6->sin6_family = dh->af; - sin6->sin6_port = - S->port ? htons(S->port) : htons(HTTP_PORT); + sin6->sin6_port = htons(S->connect_port); memcpy(&sin6->sin6_addr, dh->data, dh->alen); inet_ntop(dh->af, dh->data, buf, sizeof(buf)); if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl)) - MSG("Connecting to %s\n", buf); + MSG("Connecting to %s:%d\n", buf, S->connect_port); break; } #endif }/*switch*/ - MSG_BW(S->web, 1, "Contacting host..."); status = connect(S->SockFD, (struct sockaddr *)&name, socket_len); if (status == -1 && errno != EINPROGRESS) { - S->Err = errno; - dClose(S->SockFD); - MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err)); + MSG("Http_connect_socket ERROR: %s\n", dStrerror(errno)); + a_Http_connect_done(S->SockFD, FALSE); + } else if (S->flags & HTTP_SOCKET_SSL) { + Http_connect_ssl(Info); } else { - return 0; /* Success */ + a_Http_connect_done(S->SockFD, TRUE); } + return; } - - return -1; } /* * Test proxy settings and check the no_proxy domains list * Return value: whether to use proxy or not. */ -static int Http_must_use_proxy(const DilloUrl *url) +static int Http_must_use_proxy(const char *hostname) { char *np, *p, *tok; int ret = 0; @@ -509,14 +588,13 @@ static int Http_must_use_proxy(const DilloUrl *url) if (HTTP_Proxy) { ret = 1; if (prefs.no_proxy) { - const char *host = URL_HOST(url); - size_t host_len = strlen(host); + size_t host_len = strlen(hostname); np = dStrdup(prefs.no_proxy); for (p = np; (tok = dStrsep(&p, " ")); ) { int start = host_len - strlen(tok); - if (start >= 0 && dStrAsciiCasecmp(host + start, tok) == 0) { + if (start >= 0 && dStrAsciiCasecmp(hostname + start, tok) == 0) { /* no_proxy token is suffix of host string */ ret = 0; break; @@ -525,21 +603,21 @@ static int Http_must_use_proxy(const DilloUrl *url) dFree(np); } } - _MSG("Http_must_use_proxy: %s\n %s\n", URL_STR(url), ret ? "YES":"NO"); + _MSG("Http_must_use_proxy: %s\n %s\n", hostname, ret ? "YES":"NO"); return ret; } /* * Return a new string for the request used to tunnel HTTPS through a proxy. */ -char *a_Http_make_connect_str(const DilloUrl *url) +static char *Http_get_connect_str(const DilloUrl *url) { Dstr *dstr; const char *auth1; int auth_len; char *auth2, *proxy_auth, *retstr; - dReturn_val_if_fail(Http_must_use_proxy(url), NULL); + dReturn_val_if_fail(Http_must_use_proxy(URL_HOST(url)), NULL); dstr = dStr_new(""); auth1 = URL_AUTHORITY(url); @@ -571,14 +649,6 @@ char *a_Http_make_connect_str(const DilloUrl *url) } /* - * Return URL string of HTTP proxy, if any - */ -const char *a_Http_get_proxy_urlstr() -{ - return HTTP_Proxy ? URL_STR(HTTP_Proxy) : NULL; -} - -/* * Callback function for the DNS resolver. * Continue connecting the socket, or abort upon error condition. * S->web is checked to assert the operation wasn't aborted while waiting. @@ -586,34 +656,32 @@ const char *a_Http_get_proxy_urlstr() static void Http_dns_cb(int Status, Dlist *addr_list, void *data) { int SKey = VOIDP2INT(data); + bool_t clean_up = TRUE; SocketData_t *S; HostConnection_t *hc; S = a_Klist_get_data(ValidSocks, SKey); if (S) { - if (!a_Web_valid(S->web)) { - a_Chain_bfcb(OpAbort, S->Info, NULL, "Both"); - dFree(S->Info); + const char *host = URL_HOST((S->flags & HTTP_SOCKET_USE_PROXY) ? + HTTP_Proxy : S->url); + if (a_Web_valid(S->web)) { + if (Status == 0 && addr_list) { + + /* Successful DNS answer; save the IP */ + S->addr_list = addr_list; + clean_up = FALSE; + hc = Http_host_connection_get(host); + Http_socket_enqueue(hc, S); + Http_connect_queued_sockets(hc); + } else { + /* DNS wasn't able to resolve the hostname */ + MSG_BW(S->web, 0, "ERROR: DNS can't resolve %s", host); + } + } + if (clean_up) { Http_socket_free(SKey); - - } else if (Status == 0 && addr_list) { - /* Successful DNS answer; save the IP */ - S->addr_list = addr_list; - S->flags |= HTTP_SOCKET_QUEUED; - if (S->flags & HTTP_SOCKET_USE_PROXY) - hc = Http_host_connection_get(URL_HOST(HTTP_Proxy)); - else - hc = Http_host_connection_get(URL_HOST(S->web->url)); - Http_socket_enqueue(hc, S); - Http_connect_queued_sockets(hc); - } else { - /* DNS wasn't able to resolve the hostname */ - MSG_BW(S->web, 0, "ERROR: DNS can't resolve %s", - (S->flags & HTTP_SOCKET_USE_PROXY) ? URL_HOST_(HTTP_Proxy) : - URL_HOST_(S->web->url)); a_Chain_bfcb(OpAbort, S->Info, NULL, "Both"); dFree(S->Info); - Http_socket_free(SKey); } } } @@ -629,6 +697,7 @@ static int Http_get(ChainLink *Info, void *Data1) { SocketData_t *S; char *hostname; + const DilloUrl *url; S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey)); /* Reference Web data */ @@ -637,18 +706,20 @@ static int Http_get(ChainLink *Info, void *Data1) S->Info = Info; /* Proxy support */ - if (Http_must_use_proxy(S->web->url)) { - hostname = dStrdup(URL_HOST(HTTP_Proxy)); - S->port = URL_PORT(HTTP_Proxy); + if (Http_must_use_proxy(URL_HOST(S->web->url))) { + url = HTTP_Proxy; S->flags |= HTTP_SOCKET_USE_PROXY; } else { - hostname = dStrdup(URL_HOST(S->web->url)); - S->port = URL_PORT(S->web->url); - S->flags &= ~HTTP_SOCKET_USE_PROXY; + url = S->web->url; } + hostname = dStrdup(URL_HOST(url)); + S->connect_port = URL_PORT(url); + S->url = a_Url_dup(S->web->url); + if (!dStrAsciiCasecmp(URL_SCHEME(S->url), "https")) + S->flags |= HTTP_SOCKET_SSL; /* Let the user know what we'll do */ - MSG_BW(S->web, 1, "DNS resolving %s", URL_HOST_(S->web->url)); + MSG_BW(S->web, 1, "DNS resolving %s", hostname); /* Let the DNS engine resolve the hostname, and when done, * we'll try to connect the socket from the callback function */ @@ -659,6 +730,26 @@ static int Http_get(ChainLink *Info, void *Data1) } /* + * Can the old socket's fd be reused for the new socket? + * + * NOTE: old and new must come from the same HostConnection_t. + * This is not built to accept arbitrary sockets. + */ +static bool_t Http_socket_reuse_compatible(SocketData_t *old, + SocketData_t *new) +{ + if (a_Web_valid(new->web) && + old->connect_port == new->connect_port && + ((old->flags & HTTP_SOCKET_SSL) == (new->flags & HTTP_SOCKET_SSL)) && + ((old->flags & HTTP_SOCKET_SSL) == 0 || + (old->flags & HTTP_SOCKET_USE_PROXY) == 0 || + ((URL_PORT(old->url) == URL_PORT(new->url)) && + !dStrAsciiCasecmp(URL_HOST(old->url), URL_HOST(new->url))))) + return TRUE; + return FALSE; +} + +/* * If any entry in the socket data queue can reuse our connection, set it up * and send off a new query. */ @@ -673,33 +764,25 @@ static void Http_socket_reuse(int SKey) for (i = 0; i < n; i++) { new_sd = dList_nth_data(hc->queue, i); - if (old_sd->port == new_sd->port && - !(new_sd->flags & HTTP_SOCKET_TO_BE_FREED) && - a_Web_valid(new_sd->web)) { + if (!(new_sd->flags & HTTP_SOCKET_TO_BE_FREED) && + Http_socket_reuse_compatible(old_sd, new_sd)) { + const bool_t success = TRUE; + new_sd->SockFD = old_sd->SockFD; - Http_fd_map_remove_entry(old_sd->SockFD); - a_Klist_remove(ValidSocks, SKey); - dFree(old_sd); - - dList_remove(hc->queue, new_sd); - new_sd->flags &= ~HTTP_SOCKET_QUEUED; - FdMapEntry_t *e = dNew0(FdMapEntry_t, 1); - e->fd = new_sd->SockFD; - e->skey = VOIDP2INT(new_sd->Info->LocalKey); - dList_append(fd_map, e); - - a_Chain_bcb(OpSend, new_sd->Info, &new_sd->SockFD, "FD"); - a_Chain_fcb(OpSend, new_sd->Info, &new_sd->SockFD, "FD"); - Http_send_query(new_sd->Info, new_sd); - new_sd->connected_to = hc->host; + + old_sd->connected_to = NULL; + hc->active_conns--; + Http_socket_free(SKey); + + MSG("Reusing fd %d for %s\n", new_sd->SockFD,URL_STR(new_sd->url)); + Http_socket_activate(hc, new_sd); + Http_fd_map_add_entry(new_sd); + a_Http_connect_done(new_sd->SockFD, success); return; } } dClose(old_sd->SockFD); - Http_fd_map_remove_entry(old_sd->SockFD); - a_Klist_remove(ValidSocks, SKey); - hc->active_conns--; - dFree(old_sd); + Http_socket_free(SKey); } } @@ -710,8 +793,8 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info, void *Data1, void *Data2) { int SKey = VOIDP2INT(Info->LocalKey); - - (void)Data2; /* suppress unused parameter warning */ + SocketData_t *sd; + DataBuf *dbuf; dReturn_if_fail( a_Chain_check("a_Http_ccc", Op, Branch, Dir, Info) ); @@ -735,43 +818,81 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info, dFree(Info); break; case OpAbort: - /* something bad happened... */ - a_Chain_bcb(OpAbort, Info, NULL, NULL); + MSG("ABORT 1B\n"); Http_socket_free(SKey); + a_Chain_bcb(OpAbort, Info, NULL, NULL); dFree(Info); break; + default: + MSG_WARN("Unused CCC 1B Op %d\n", Op); + break; } } else { /* 1 FWD */ - SocketData_t *sd; /* HTTP send-query status branch */ switch (Op) { case OpAbort: + MSG("ABORT 1F\n"); if ((sd = a_Klist_get_data(ValidSocks, SKey))) - MSG_BW(sd->web, 1, "Can't get %s", URL_STR(sd->web->url)); - a_Chain_fcb(OpAbort, Info, NULL, "Both"); + MSG_BW(sd->web, 1, "Can't get %s", URL_STR(sd->url)); Http_socket_free(SKey); + a_Chain_fcb(OpAbort, Info, NULL, "Both"); dFree(Info); break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC 1F Op %d\n", Op); break; } } } else if (Branch == 2) { if (Dir == FWD) { + sd = a_Klist_get_data(ValidSocks, SKey); + assert(sd); /* Receiving from server */ switch (Op) { case OpSend: - /* Data1 = dbuf */ - a_Chain_fcb(OpSend, Info, Data1, "send_page_2eof"); + if (sd->https_proxy_reply) { + dbuf = Data1; + dStr_append(sd->https_proxy_reply, dbuf->Buf); + if (strstr(sd->https_proxy_reply->str, "\r\n\r\n")) { + if (sd->https_proxy_reply->len >= 12 && + sd->https_proxy_reply->str[9] == '2') { + /* e.g. "HTTP/1.1 200 Connection established[...]" */ + MSG("CONNECT through proxy succeeded. Reply:\n%s\n", + sd->https_proxy_reply->str); + dStr_free(sd->https_proxy_reply, 1); + sd->https_proxy_reply = NULL; + a_Ssl_handshake(sd->SockFD, sd->url); + } else { + MSG_BW(sd->web, 1, "Can't connect through proxy to %s", + URL_HOST(sd->url)); + MSG("CONNECT through proxy failed. Server sent:\n%s\n", + sd->https_proxy_reply->str); + Http_socket_free(SKey); + a_Chain_bfcb(OpAbort, Info, NULL, "Both"); + dFree(Info); + } + } + } else { + /* Data1 = dbuf */ + a_Chain_fcb(OpSend, Info, Data1, "send_page_2eof"); + } break; case OpEnd: - a_Chain_fcb(OpEnd, Info, NULL, NULL); - Http_socket_free(SKey); + if (sd->https_proxy_reply) { + MSG("CONNECT through proxy failed. " + "Full reply not received:\n%s\n", + sd->https_proxy_reply->len ? sd->https_proxy_reply->str : + "(nothing)"); + Http_socket_free(SKey); + a_Chain_bfcb(OpAbort, Info, NULL, "Both"); + } else { + Http_socket_free(SKey); + a_Chain_fcb(OpEnd, Info, NULL, NULL); + } dFree(Info); break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC 2F Op %d\n", Op); break; } } else { /* 2 BCK */ @@ -796,13 +917,13 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info, } break; case OpAbort: - a_Chain_bcb(OpAbort, Info, NULL, NULL); Http_socket_free(SKey); + a_Chain_bcb(OpAbort, Info, NULL, NULL); dFree(Info); break; - default: - MSG_WARN("Unused CCC\n"); - break; + default: + MSG_WARN("Unused CCC 2B Op %d\n", Op); + break; } } } @@ -814,6 +935,8 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info, */ static void Http_socket_enqueue(HostConnection_t *hc, SocketData_t* sock) { + sock->flags |= HTTP_SOCKET_QUEUED; + if ((sock->web->flags & WEB_Image) == 0) { int i, n = dList_length(hc->queue); @@ -829,14 +952,6 @@ static void Http_socket_enqueue(HostConnection_t *hc, SocketData_t* sock) dList_append(hc->queue, sock); } -static SocketData_t* Http_socket_dequeue(HostConnection_t *hc) -{ - SocketData_t *sd = dList_nth_data(hc->queue, 0); - - dList_remove(hc->queue, sd); - return sd; -} - static HostConnection_t *Http_host_connection_get(const char *host) { int i; @@ -873,8 +988,10 @@ static void Http_host_connection_remove_all() while (dList_length(host_connections) > 0) { hc = (HostConnection_t*) dList_nth_data(host_connections, 0); - while ((sd = Http_socket_dequeue(hc))) + while ((sd = dList_nth_data(hc->queue, 0))) { + dList_remove(hc->queue, sd); dFree(sd); + } Http_host_connection_remove(hc); } dList_free(host_connections); diff --git a/src/Makefile.am b/src/Makefile.am index 597a743b..57a68148 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@ dillo_LDADD = \ $(top_builddir)/dw/libDw-core.a \ $(top_builddir)/lout/liblout.a \ @LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBFLTK_LIBS@ @LIBZ_LIBS@ \ - @LIBICONV_LIBS@ @LIBPTHREAD_LIBS@ @LIBX11_LIBS@ + @LIBICONV_LIBS@ @LIBPTHREAD_LIBS@ @LIBX11_LIBS@ @LIBSSL_LIBS@ dillo_SOURCES = \ dillo.cc \ @@ -18,6 +18,7 @@ #include <string.h> #include <errno.h> +#include "config.h" #include "msg.h" #include "capi.h" #include "IO/IO.h" /* for IORead &friends */ @@ -269,6 +270,7 @@ static int Capi_url_uses_dpi(DilloUrl *url, char **server_ptr) Dstr *tmp; if ((dStrnAsciiCasecmp(url_str, "http:", 5) == 0) || + (dStrnAsciiCasecmp(url_str, "https:", 6) == 0) || (dStrnAsciiCasecmp(url_str, "about:", 6) == 0)) { /* URL doesn't use dpi (server = NULL) */ } else if (dStrnAsciiCasecmp(url_str, "dpi:/", 5) == 0) { @@ -299,39 +301,7 @@ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server) { char *cmd; - if (strcmp(server, "proto.https") == 0) { - /* Let's be kind and make the HTTP query string for the dpi */ - char *proxy_connect = a_Http_make_connect_str(web->url); - Dstr *http_query = a_Http_make_query_str(web->url, web->requester, - web->flags, FALSE); - - if ((uint_t) http_query->len > strlen(http_query->str)) { - /* Can't handle NULLs embedded in query data */ - MSG_ERR("HTTPS query truncated!\n"); - } - - /* BUG: WORKAROUND: request to only check the root URL's certificate. - * This avoids the dialog bombing that stems from loading multiple - * https images/resources in a single page. A proper fix would take - * either to implement the https-dpi as a server (with state), - * or to move back https handling into dillo. */ - if (proxy_connect) { - const char *proxy_urlstr = a_Http_get_proxy_urlstr(); - cmd = a_Dpip_build_cmd("cmd=%s proxy_url=%s proxy_connect=%s " - "url=%s query=%s check_cert=%s", - "open_url", proxy_urlstr, - proxy_connect, URL_STR(web->url), - http_query->str, - (web->flags & WEB_RootUrl) ? "true" : "false"); - } else { - cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s check_cert=%s", - "open_url", URL_STR(web->url),http_query->str, - (web->flags & WEB_RootUrl) ? "true" : "false"); - } - dFree(proxy_connect); - dStr_free(http_query, 1); - - } else if (strcmp(server, "downloads") == 0) { + if (strcmp(server, "downloads") == 0) { /* let the downloads server get it */ cmd = a_Dpip_build_cmd("cmd=%s url=%s destination=%s", "download", URL_STR(web->url), web->filename); @@ -444,8 +414,19 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData) } dFree(server); - } else if (!dStrAsciiCasecmp(scheme, "http")) { + } else if (!dStrAsciiCasecmp(scheme, "http") || + !dStrAsciiCasecmp(scheme, "https")) { /* http request */ + +#ifndef ENABLE_SSL + if (!dStrAsciiCasecmp(scheme, "https")) { + if (web->flags & WEB_RootUrl) + a_UIcmd_set_msg(web->bw, + "HTTPS was disabled at compilation time"); + a_Web_free(web); + return 0; + } +#endif if (reload) { a_Capi_conn_abort_by_url(web->url); /* create a new connection and start the CCC operations */ @@ -633,7 +614,8 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info, Capi_conn_ref(conn); Info->LocalKey = conn; conn->InfoSend = Info; - if (strcmp(conn->server, "http") == 0) { + if (strcmp(conn->server, "http") == 0 || + strcmp(conn->server, "https") == 0) { a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Http_ccc, 1, 1); a_Chain_bcb(OpStart, Info, Data2, NULL); } else { @@ -660,7 +642,7 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info, dFree(Info); break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC Capi 1B\n"); break; } } else { /* 1 FWD */ @@ -701,7 +683,7 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info, dFree(Info); break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC Capi 1F\n"); break; } } @@ -738,7 +720,7 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info, dFree(Info); break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC Capi 2B\n"); break; } } else { /* 2 FWD */ @@ -788,8 +770,24 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info, Capi_conn_unref(conn); dFree(Info); break; + case OpAbort: + conn = Info->LocalKey; + conn->InfoRecv = NULL; + a_Cache_process_dbuf(IOAbort, NULL, 0, conn->url); + if (Data2) { + if (!strcmp(Data2, "Both") && conn->InfoSend) { + /* abort the other branch too */ + a_Capi_ccc(OpAbort, 1, BCK, conn->InfoSend, NULL, NULL); + } + } + /* if URL == expect-url */ + a_Nav_cancel_expect_if_eq(conn->bw, conn->url); + /* finish conn */ + Capi_conn_unref(conn); + dFree(Info); + break; default: - MSG_WARN("Unused CCC\n"); + MSG_WARN("Unused CCC Capi 2F\n"); break; } } diff --git a/src/dillo.cc b/src/dillo.cc index 79160878..2bfab238 100644 --- a/src/dillo.cc +++ b/src/dillo.cc @@ -45,6 +45,7 @@ #include "dns.h" #include "web.hh" +#include "IO/ssl.h" #include "IO/Url.h" #include "IO/mime.h" #include "capi.h" @@ -476,6 +477,7 @@ int main(int argc, char **argv) a_Dns_init(); a_Web_init(); a_Http_init(); + a_Ssl_init(); a_Mime_init(); a_Capi_init(); a_Dicache_init(); @@ -597,6 +599,7 @@ int main(int argc, char **argv) a_Cache_freeall(); a_Dicache_freeall(); a_Http_freeall(); + a_Ssl_freeall(); a_Dns_freeall(); a_History_freeall(); a_Prefs_freeall(); |