[Libevent-users] Re: Avoid potential SSL read spinlocks
Hi, Why does bev_ssl require deferred callbacks? Commit fc52dbac (see below) fixed an issue I was seeing where deferred error_cb(EOF)s were confusing clients which had disabled EV_READs (notably evhttp). If bev_ssl didn't require deferred cbs, the EOFs would have stayed queued at the OpenSSL layer (in the same way that EOFs normally stay queued in the kernel when bev_sock clients disable EV_READ). Thanks, Catalin diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c index 86a8619..3843b31 100644 --- a/bufferevent_openssl.c +++ b/bufferevent_openssl.c - while ((bev_ssl->bev.bev.enabled & EV_READ) && + if ((bev_ssl->bev.bev.enabled & EV_READ) && r = do_read(bev_ssl, n_to_read); - if (r <= 0) - break; *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] Re: Avoid potential SSL read spinlocks
On Mon, Nov 14, 2011 at 11:17 AM, Mark Ellzey wrote: > As far as I know, it doesn't. But I wrote this patch because if you are > using a deferred bev with bulk incoming data, it never drops back into > base_loop() until SSL_read() says there is nothing to read. This is due > to the fact that your callback for new data is never called until > libevent gets around to processing the deferred queue. This is actually causing an assert in my case :( The key is to get SSL_read to return both data (> 0) and EOF (= 0) within the same iteration of that (former) while loop. Then the flow looks like this: SSL_read() returns N bytes into an iov from evbuffer_reserve_space - no run_read_cb yet SSL_read() returns 0, which leads into conn_closed(), which queues a error_cb(EOF) Back in do_read, a read_cb is queued In the event loop, the read_cb is delivered first, completing the HTTP request. evhttp disables EV_READs and moves into state EVCON_DISCONNECTED. ...and then the error_cb(EOF) is delivered, confusing evhttp and causing assert(req != NULL) in evhttp_connection_fail. I've found it hard to point the finger at any particular piece of code... It depends on whether the bufferevent interface defines an ordering between EV_READ enable/disables and EOF/ERROR events. In the case of bev_sock, once disable(EV_READ) has been called, the fd is not checked for readability anymore, so read() doesn't have a chance to report any EOF or errors. Effectively, we're leaving these events queued in the kernel (which seems like a reasonable thing to do - evhttp no longer cares about that fd). Your patch indirectly does this - EOF/errors are now queued in OpenSSL because SSL_read is only called once for every read_cb. My only concern is that there may be cases where OpenSSL _needs_ us to call SSL_read repeatedly to make any progress, despite the underlying fd staying unreadable. Maybe during renegociation or application payloads that span SSL records? The man page doesn't have anything to that effect, but that doesn't mean the cases don't exist.. > This does not change any mechanics to openssl bufferevents without > deferred enabled. I agree that your patch doesn't affect non-deferred mechanics - but I haven't been able to get evhttp to stack on top of bev_ssl without deferred callbacks. It seems to send the request line, but gets stuck in write_cb's before sending request headers. I haven't investigated too much because it seems like it would only be a quick fix - sooner or later someone may want to use deferred cbs with bev_ssl, for the reasons described in the link you sent. BTW, sample/le-proxy.c also enabled deferred cbs on its bev_ssl. *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] Re: Avoid potential SSL read spinlocks
On Tue, Nov 15, 2011 at 10:25 AM, Mark Ellzey wrote: > Also, would be nice to have some code that reproduces the issue you are > having. Ok, try the attached patch against 9ae6e595 (just before your fixes). The use of an IP in the URL is incidental because of IPv6 issues on my system - you can probably use www.google.com directly. $ .libs/https-client https://74.125.224.80/ Response line: 302 Found > 302 Moved 302 Moved The document has moved http://www.google.com/";>here. [err] ../libevent/http.c:704: Assertion req != NULL failed in evhttp_connection_fail Aborted From 09e940ef9b71b612037e04b74f94879bfcbebd6a Mon Sep 17 00:00:00 2001 From: Catalin Patulea Date: Wed, 16 Nov 2011 11:22:04 -0800 Subject: [PATCH] Add sample/https-client.c, an example of stacking evhttp as a client on top of bufferevent_ssl. This reproduces an assert(req != NULL) in evhttp_connection_fail for me. --- sample/Makefile.am|4 + sample/https-client.c | 205 + 2 files changed, 209 insertions(+), 0 deletions(-) create mode 100644 sample/https-client.c diff --git a/sample/Makefile.am b/sample/Makefile.am index 61e72d6..2147b29 100644 --- a/sample/Makefile.am +++ b/sample/Makefile.am @@ -17,6 +17,10 @@ AM_CPPFLAGS += $(OPENSSL_INCS) noinst_PROGRAMS += le-proxy le_proxy_SOURCES = le-proxy.c le_proxy_LDADD = $(LDADD) ../libevent_openssl.la -lssl -lcrypto + +noinst_PROGRAMS += https-client +https_client_SOURCES = https-client.c +https_client_LDADD = $(LDADD) ../libevent_openssl.la -lssl -lcrypto endif verify: diff --git a/sample/https-client.c b/sample/https-client.c new file mode 100644 index 000..5013c68 --- /dev/null +++ b/sample/https-client.c @@ -0,0 +1,205 @@ +/* + This is an example of how to hook up evhttp with bufferevent_ssl + + It just GETs an https URL given on the command-line and prints the response + body to stdout. + + Actually, it also accepts plain http URLs to make it easy to compare http vs + https code paths. + + Loosely based on le-proxy.c. + */ + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct event_base *base; + +static void +http_request_done(struct evhttp_request *req, void *ctx) +{ + char buffer[256]; + int nread; + + if (req == NULL) { + fprintf(stderr, "some request failed - no idea which one though!\n"); + return; + } + + fprintf(stderr, "Response line: %d %s\n", + req->response_code, req->response_code_line); + + while ((nread = evbuffer_remove(req->input_buffer, buffer, sizeof(buffer))) > 0) { + fwrite("> ", 2, 1, stdout); + fwrite(buffer, nread, 1, stdout); + fwrite("\n", 1, 1, stdout); + } +} + +static void +syntax(void) +{ + fputs("Syntax:\n", stderr); + fputs(" https-client \n", stderr); + fputs("Example:\n", stderr); + fputs(" https-client https://ipcheckit.com/\n";, stderr); + + exit(1); +} + +static void +die(const char *msg) +{ + fputs(msg, stderr); + exit(1); +} + +int +main(int argc, char **argv) +{ + int r; + + struct evhttp_uri *http_uri; + const char *url, *scheme, *host, *path, *query; + char uri[256]; + int port; + + SSL_CTX *ssl_ctx; + SSL *ssl; + struct bufferevent *bev; + struct evhttp_connection *evcon; + struct evhttp_request *req; + + if (argc != 2) + syntax(); + + url = argv[1]; + http_uri = evhttp_uri_parse(url); + if (http_uri == NULL) { + die("malformed url"); + } + + scheme = evhttp_uri_get_scheme(http_uri); + if (scheme == NULL || (strcasecmp(scheme, "https") != 0 && + strcasecmp(scheme, "http") != 0)) { + die("url must be http or https"); + } + + host = evhttp_uri_get_host(http_uri); + if (host == NULL) { + die("url must have a host"); + } + + port = evhttp_uri_get_port(http_uri); + if (port == -1) { + port = (strcasecmp(scheme, "http") == 0) ? 80 : 443; + } + + path = evhttp_uri_get_path(http_uri); + if (path == NULL) { + path = "/"; + } + + query = evhttp_uri_get_query(http_uri); + if (query == NULL) { + snprintf(uri, sizeof(uri) - 1, "%s", path); + } else { + snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query); + } + uri[sizeof(uri) - 1] = '\0'; + + // Initialize OpenSSL + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + r = RAND_poll(); + if (r == 0) { + fprintf(stderr, "RAND_poll() failed.\n"); + return 1; + } + ssl_ctx = SSL_CTX_new(SSLv23_method()); + + // Create event base + base = event_base_new(); + if (!base) { + perror("event_base_new()"); + return 1; + } + + // Create OpenSSL bufferevent and stack evhttp on to
Re: [Libevent-users] Re: Avoid potential SSL read spinlocks
A few things I forgot to mention: Re: bev_ssl needing deferred or not, without OPT_DEFER_CALLBACKS, https-client does not work for me. I haven't investigated. Re: SSL_read loops. Consider the following scenario: - Peer sends two (small) SSL records in a row (perhaps in the same TCP segment) - fd becomes readable, libevent delivers read_cb to bev_ssl - bev_ssl calls SSL_read only once - does this read process only the first record or both? If it only processes the first record, then we should call SSL_read again. But the fd is no longer readable, so libevent won't dispatch read_cb. The second second record is queued somewhere above BIO but below SSL_read, and we get stuck. But just to emphasize - this entirely depends on (AFAICT undocumented) internal behaviour of SSL_read. If this were read(2) then the fd would remain readable from poll() so we would get a second read_cb, emptying the kernel queue. *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] Re: Avoid potential SSL read spinlocks
On Wed, Nov 16, 2011 at 11:52 AM, Catalin Patulea wrote: > - Peer sends two (small) SSL records in a row (perhaps in the same TCP > segment) > - fd becomes readable, libevent delivers read_cb to bev_ssl > - bev_ssl calls SSL_read only once - does this read process only the > first record or both? Ah, no, appdata records get coalesced when read through SSL_read. Verified this using Wireshark and logging SSL_read calls. Now I'm starting to wonder about whether close notifies would get delivered (assuming the client leaves EV_READ enabled).. *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
[Libevent-users] bev_ssl: Be more specific in event callbacks
evhttp is very strict when checking the event flags while reading the response body: evhttp_error_cb(struct bufferevent *bufev, short what, void *arg) { case EVCON_READING_BODY: if (!req->chunked && req->ntoread < 0 && what == (BEV_EVENT_READING|BEV_EVENT_EOF)) { /* EOF on read can be benign */ evhttp_connection_done(evcon); return; } break; bev_ssl currently only reports EVENT_EOF when the connection is closed, so it confuses evhttp. This patch makes bev_ssl additionally set one of BEV_EVENT_{READING|WRITING}, as appropriate, when the connection is closed. From f7d21c69ac8b0c57fab6ba4c7afe2250c646c35e Mon Sep 17 00:00:00 2001 From: Catalin Patulea Date: Mon, 21 Nov 2011 19:24:50 -0500 Subject: [PATCH 1/2] bev_ssl: Be more specific in event callbacks. evhttp in particular gets confused without at least one of BEV_EVENT_{READING|WRITING}. --- bufferevent_openssl.c | 10 ++ 1 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c index 89497fd..3ca906b 100644 --- a/bufferevent_openssl.c +++ b/bufferevent_openssl.c @@ -483,7 +483,7 @@ clear_wbor(struct bufferevent_openssl *bev_ssl) } static void -conn_closed(struct bufferevent_openssl *bev_ssl, int errcode, int ret) +conn_closed(struct bufferevent_openssl *bev_ssl, int when, int errcode, int ret) { int event = BEV_EVENT_ERROR; int dirty_shutdown = 0; @@ -529,6 +529,8 @@ conn_closed(struct bufferevent_openssl *bev_ssl, int errcode, int ret) stop_reading(bev_ssl); stop_writing(bev_ssl); + /* when is BEV_EVENT_{READING|WRITING} */ + event = when | event; _bufferevent_run_eventcb(&bev_ssl->bev.bev, event); } @@ -604,7 +606,7 @@ do_read(struct bufferevent_openssl *bev_ssl, int n_to_read) return -1; break; default: -conn_closed(bev_ssl, err, r); +conn_closed(bev_ssl, BEV_EVENT_READING, err, r); break; } blocked = 1; @@ -682,7 +684,7 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost) bev_ssl->last_write = space[i].iov_len; break; default: -conn_closed(bev_ssl, err, r); +conn_closed(bev_ssl, BEV_EVENT_WRITING, err, r); bev_ssl->last_write = -1; break; } @@ -979,7 +981,7 @@ do_handshake(struct bufferevent_openssl *bev_ssl) } return 0; default: - conn_closed(bev_ssl, err, r); + conn_closed(bev_ssl, BEV_EVENT_READING, err, r); return -1; } } -- 1.7.3.1
[Libevent-users] [PATCH] bev_ssl: Allow users to set allow_dirty_shutdown
By default, allow_dirty_shutdown is 0, reporting TCP-close-before-SSL-close as BEV_EVENT_ERROR. But many https servers out there do dirty shutdowns, so clients need to be able to set this flag. This patch simply adds a getter/setter for the flag. Default behaviour of bev_ssl does not change. From 9632ddb90393fcb4a8b537644d932e8eedd08c1c Mon Sep 17 00:00:00 2001 From: Catalin Patulea Date: Mon, 21 Nov 2011 19:57:19 -0500 Subject: [PATCH 2/2] Allow users to set allow_dirty_shutdown --- bufferevent_openssl.c| 23 ++- include/event2/bufferevent_ssl.h | 16 2 files changed, 38 insertions(+), 1 deletions(-) diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c index 3ca906b..6f13159 100644 --- a/bufferevent_openssl.c +++ b/bufferevent_openssl.c @@ -313,7 +313,7 @@ struct bufferevent_openssl { unsigned read_blocked_on_write : 1; /* When we next get data, we should say "write" instead of "read". */ unsigned write_blocked_on_read : 1; - /* XXX */ + /* Treat TCP close before SSL close on SSL >= v3 as clean EOF. */ unsigned allow_dirty_shutdown : 1; /* */ unsigned fd_is_set : 1; @@ -1389,6 +1389,27 @@ bufferevent_openssl_socket_new(struct event_base *base, base, NULL, fd, ssl, state, options); } +int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev) +{ + int allow_dirty_shutdown = 0; + struct bufferevent_openssl *bev_ssl; + BEV_LOCK(bev); + bev_ssl = upcast(bev); + allow_dirty_shutdown = bev_ssl->allow_dirty_shutdown; + BEV_UNLOCK(bev); + return allow_dirty_shutdown; +} + +void bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev, +int allow_dirty_shutdown) +{ + struct bufferevent_openssl *bev_ssl; + BEV_LOCK(bev); + bev_ssl = upcast(bev); + bev_ssl->allow_dirty_shutdown = allow_dirty_shutdown; + BEV_UNLOCK(bev); +} + unsigned long bufferevent_get_openssl_error(struct bufferevent *bev) { diff --git a/include/event2/bufferevent_ssl.h b/include/event2/bufferevent_ssl.h index bf6009a..30bf2d3 100644 --- a/include/event2/bufferevent_ssl.h +++ b/include/event2/bufferevent_ssl.h @@ -88,6 +88,22 @@ bufferevent_openssl_socket_new(struct event_base *base, enum bufferevent_ssl_state state, int options); +/** Control whether to report dirty SSL shutdowns. + +If the peer closes the TCP connection before closing the SSL channel, the +protocol is SSL >= v3, and allow_dirty_shutdown=0 (default), you will receive +BEV_EVENT_ERROR. + +If instead allow_dirty_shutdown=1, you will receive BEV_EVENT_EOF. + +On the other hand, if the protocol is < SSLv3, you will always receive +BEV_EVENT_EOF. +*/ + +int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev); +void bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev, +int allow_dirty_shutdown); + /** Return the underlying openssl SSL * object for an SSL bufferevent. */ struct ssl_st * bufferevent_openssl_get_ssl(struct bufferevent *bufev); -- 1.7.3.1
Re: [Libevent-users] detection of TCP disconnection
On Tue, Nov 22, 2011 at 7:18 PM, wrote: > However if i a do hard reset(power off) of the machine running the client, > it does not seem to work and I believe I am not getting a callback > function for the event on the server. I think the fundamental problem is distinguishing between 'hard reset' and 'network temporarily down'. In both cases, the clients sees it as silence from the server for some (arbitrarily long) period of time. Most apps just send application-level pings at some interval and handle unanswered pings in some manner that is appropriate to the application. *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] [PATCH] bev_ssl: Allow users to set allow_dirty_shutdown
On Thu, Nov 24, 2011 at 12:34 PM, Nick Mathewson wrote: > I tidied the documentation a little in a44cd2b0205df; and fixed a > couple of robustness issues in f3b89dec9eac2cf: you might want to > check those out to make sure I didn't mess it up for you. Still works fine here, thanks! *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] PATCH: bufferevent_openssl consider_read doesn't drain underlying BE in all cases
On Mon, Dec 19, 2011 at 5:34 PM, Haseeb Abdul Qadir wrote: > Thanks for resending this on the mailing list. > SSL_pending() ends up returning 0 in some cases even when the underlying BE's > input buffer has data left to consume This is exactly the condition I was afraid of back when 2aa036f was being discussed. I was never able to come up with a concrete scenario, but it seemed conceivable. Note that I was using an OpenSSL bev layered directly on top of BIO_socket, not looping back over an underlying bev. OTOH, if SSL_pending can return 0 with bytes queued in the evbuffer, then these bytes could just as well be queued in the kernel, when layering over BIO_socket. So it might be possible to reproduce the condition with bev_ssl-over-BIO_socket too. > I've created the patch below and it seems to fix the issue on my end. > https://github.com/haseebq/libevent/commit/c58d9d8d437d122a8b67d42a6126d81699ec6914 I'm a bit confused by the patch - n_to_read is a number of cleartext bytes to be read from SSL, right? You're setting it from evbuffer_get_length, which is a number of encrypted bytes on the underlying bev (below SSL). > Ps - I haven't tried to reproduce this bug in a stand alone project yet. Would you be able to provide a repro, or at least some more in-depth description of the conditions that prompted the issue to surface? *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] Re: evhttp: persistent connections over ssl with chunked transfer encoding bug?
On Thu, Dec 22, 2011 at 4:55 PM, Hochhaus, Andrew wrote: > You can also reproduce the behavior in a web browser by running the > server (main.cc), browsing to https://127.0.0.1:8889/ and then > refreshing a few times (until one of the TCP connections is reused). I can reproduce this too against master. (I tweaked the code just a little to read certs from disk) Here is my output from http.py: $ ./http.py host=www.google.com ssl=False chunked=None: success host=www.google.com ssl=True chunked=True: success host=127.0.0.1 ssl=False chunked=None: success host=127.0.0.1 ssl=True chunked=None: success host=127.0.0.1 ssl=False chunked=True: success host=127.0.0.1 ssl=True chunked=None: failure My code is at: https://github.com/cpatulea/Libevent/tree/ssltimeout I'll be digging into this to try to figure out what's going on. Catalin *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] Re: evhttp: persistent connections over ssl with chunked transfer encoding bug?
On Thu, Dec 22, 2011 at 5:12 PM, Catalin Patulea wrote: > I'll be digging into this to try to figure out what's going on. Seems like bev_ssl flushes data to SSL_write and notifies back more eagerly than bev_sock, which is confusing evhttp_send_reply_end. evhttp_send_reply_end tries to write the last chunk and get notified (send_done) when it's been written: evhttp_send_reply_end(struct evhttp_request *req) { <...> if (req->chunked) { evbuffer_add(output, "0\r\n\r\n", 5); evhttp_write_buffer(req->evcon, evhttp_send_done, NULL); evhttp_write_buffer is basically: /* Set call back */ evcon->cb = cb; evcon->cb_arg = arg; bufferevent_enable(evcon->bufev, EV_WRITE); The problem is that bev_ssl flushes the write and fires the write callback from within evbuffer_add, which is too early. When write_buffer does enable(EV_WRITE), no data is being flushed anymore, so the event never gets fired. bev_sock just asks to be notified for fd writability, and returns. write_buffer gets the chance to prepare a callback. Then the bev_sock writecb actually flushes the evbuffer to write(2). If it is deemed acceptable for a bev to flush and cb from evbuffer_add, then maybe bev_ssl should also fire a write_cb upon enable(EV_WRITE) when everything has already been flushed. Otherwise (and it seems most of evhttp is built on this assumption), bev_ssl should never flush anything unless it's being called from the event loop. *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
[Libevent-users] [PATCH] Force strict validation of HTTP version in response
Hi, I ran into this while evhttp was trying to fetch Shoutcast streams. The server responds with 'ICY 200 OK', which libevent intends to reject. But the sscanf return value isn't checked properly and the behaviour is undefined (in practice, sometimes accepts the response, sometimes rejects it). For small patches like this, should I just paste the patch in the message body? It would make it easier to glance at the patch, though there's a possibility Gmail might mangle the text. Catalin From 429903e6385823d780631734a959ddae4401069c Mon Sep 17 00:00:00 2001 From: Catalin Patulea Date: Tue, 10 Jan 2012 18:33:58 -0500 Subject: [PATCH] Force strict validation of HTTP version in response. This sometimes accepted invalid versions like 'ICY' (n = 0, major = undefined, sometimes > 1). --- http.c |2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/http.c b/http.c index 1fccc2c..67b985a 100644 --- a/http.c +++ b/http.c @@ -1468,7 +1468,7 @@ evhttp_parse_http_version(const char *version, struct evhttp_request *req) int major, minor; char ch; int n = sscanf(version, "HTTP/%d.%d%c", &major, &minor, &ch); - if (n > 2 || major > 1) { + if (n != 2 || major > 1) { event_debug(("%s: bad version %s on message %p from %s", __func__, version, req, req->remote_host)); return (-1); -- 1.7.3.1
[Libevent-users] evdns timeout behaviour
Hi, I'm interested in what happens when a freshly created evdns with more than one nameserver encounters a timeout on the first request. It seems evdns distinguishes between retransmits and reissues. Retransmits are triggered by timeouts and always point to the same ns, chosen at first-transmit time. Reissues, on the other hand, are triggered by bad responses from nameservers, like NOTIMPL or REFUSED, and do cause a new ns to be picked in a round-robin fashion. The net effect is that on a fresh evdns with 2 nameservers, a single evdns_resolve is tried only against one nameserver. A second evdns_resolve is needed to talk to the second ns. This is different from glibc (2.7), where a new nameserver appears to be chosen before each retransmit. I'm not sure whether glibc makes any distinction between retransmit and reissue. Can anyone comment on these semantics? What was the rationale? Is there any way to get evdns to behave like glibc without significant code changes? (I don't see anything obvious in evdns_set_option.) Catalin
Re: [Libevent-users] evdns timeout behaviour
On Sat, Jul 7, 2012 at 4:54 PM, Adrian Chadd wrote: > Eg, what does the bind "libc" implementation of name lookups do? (ie, > what user applications can do, not what BIND itself does.) Any idea what Debian package has this implementation? The only user-accessible resolver I can find in libbind-dev is lwres. lwres rotates for each rexmit (grouped to show bursts): 14:08:44.281278 IP 172.29.33.50.55508 > 2.3.4.5.53: 10299+% [1au] A? www.google.ca. (42) 14:08:44.281362 IP 172.29.33.50.24086 > 2.3.4.5.53: 39714+ [1au] NS? . (28) 14:08:46.281595 IP 172.29.33.50.29057 > 1.2.3.4.53: 23708+% [1au] A? www.google.ca. (42) 14:08:46.281645 IP 172.29.33.50.10276 > 1.2.3.4.53: 65136+ [1au] NS? . (28) 14:08:48.324396 IP 172.29.33.50.62960 > 2.3.4.5.53: 32037+% [1au] A? www.google.ca. (42) 14:08:50.324731 IP 172.29.33.50.22628 > 1.2.3.4.53: 53006+% [1au] A? www.google.ca. (42) *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] evdns timeout behaviour
On Sat, Jul 7, 2012 at 4:54 PM, Adrian Chadd wrote: > .. what do other operating systems do? BTW, glibc has a "rotate" option that load-balances across the list of nameservers - but this option only applies when starting a new lookup [1]. In the timeout case, a new nameserver is always tried [2]. My understanding is that multiple nameservers in glibc were originally intended for failover only, and client-side load-balancing (the rotate option) was added later as a feature. [1] http://sourceware.org/git/?p=glibc.git;a=blob;f=resolv/res_send.c;h=0a28cd784b99d45eb70150f3ac50b75b14203976;hb=HEAD#l460 [2] http://sourceware.org/git/?p=glibc.git;a=blob;f=resolv/res_send.c;h=0a28cd784b99d45eb70150f3ac50b75b14203976;hb=HEAD#l563 *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
[Libevent-users] [PATCH] http: on connection reset, detach the closed fd from the bufferevent
From: Catalin Patulea I'm curious what people think of a patch like this.. I've been seeing fds getting reused and bufferevents adding/removing events on the underlying evmap when they shouldn't be touching that fd anymore. This patch seems to fix it but I haven't fully considered the reprecussions of doing this. Any first impressions on this? Thanks, Catalin --- http.c |1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/http.c b/http.c index 22a46a9..8d871fd 100644 --- a/http.c +++ b/http.c @@ -1240,6 +1240,7 @@ evhttp_connection_reset_(struct evhttp_connection *evcon) tmp = bufferevent_get_input(evcon->bufev); evbuffer_drain(tmp, evbuffer_get_length(tmp)); + bufferevent_setfd(evcon->bufev, -1); evcon->state = EVCON_DISCONNECTED; } -- 1.7.7.3 *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
[Libevent-users] Re: [PATCH] http: on connection reset, detach the closed fd from the bufferevent
[-cc: my personal email, added by mistake] Ping. We've been seeing good results with this. On Wed, Dec 19, 2012 at 11:05 AM, Catalin Patulea wrote: > From: Catalin Patulea > > I'm curious what people think of a patch like this.. I've been seeing fds > getting reused and bufferevents adding/removing events on the underlying > evmap when they shouldn't be touching that fd anymore. > > This patch seems to fix it but I haven't fully considered the reprecussions > of doing this. > > Any first impressions on this? > > Thanks, > Catalin > --- > http.c |1 + > 1 files changed, 1 insertions(+), 0 deletions(-) > > diff --git a/http.c b/http.c > index 22a46a9..8d871fd 100644 > --- a/http.c > +++ b/http.c > @@ -1240,6 +1240,7 @@ evhttp_connection_reset_(struct evhttp_connection > *evcon) > tmp = bufferevent_get_input(evcon->bufev); > evbuffer_drain(tmp, evbuffer_get_length(tmp)); > > + bufferevent_setfd(evcon->bufev, -1); > evcon->state = EVCON_DISCONNECTED; > } > > -- > 1.7.7.3 > *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] Re: [PATCH] http: on connection reset, detach the closed fd from the bufferevent
On Tue, Feb 19, 2013 at 11:49 AM, Nick Mathewson wrote: > Could you be more specific about the good results, and how to > reproduce them? Or how to reproduce/observe the undesired behavior? > It would also be good to have an explanation of how the patch is > supposed to work. I had a lot of trouble reproducing this bug. It is exacerbated by high requests parallelism, where there is a high chance of having fds numbers get reused by the kernel and the evmap falls out of sync with the kernel's idea of which fds are and aren't used. I think the trigger is a connection that times out. Then evhttp_connection_reset_ closes the fd (evutil_closesocket) but the bufferevent still thinks it owns the fd and the fd is still present in the underlying evmap/epoll implementation. Then the kernel hands the same fd to a new bufferevent and that bufferevent starts getting events for the old one. This would cause assertions in the epoll layer like "Epoll ADD(X) on fd Y failed", Valgrind invalid reads or invalid writes and occasionally crashes. I'm sorry I can't be more specific than this. The way I tackled this was by adding a ton of cross-layer assertions like ("fd returned from accept is not present in evmap") or "fd passed to evutil_closesocket must have no remaining event interests attached to it" and gradually tracing back to who was violating these assumptions. We saw a 7-fold reduction in crash rate with this patch. *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
[Libevent-users] [PATCH] Add sample/https-client.c, an example of stacking evhttp as a client on top of bufferevent_ssl.
Signed-off-by: Catalin Patulea --- .gitignore| 1 + sample/https-client.c | 207 ++ sample/include.am | 5 ++ 3 files changed, 213 insertions(+) create mode 100644 sample/https-client.c diff --git a/.gitignore b/.gitignore index 057ec08..dad74e7 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,7 @@ libevent_openssl.pc /sample/hello-world /sample/http-server /sample/le-proxy +/sample/https-client /sample/signal-test /sample/time-test diff --git a/sample/https-client.c b/sample/https-client.c new file mode 100644 index 000..1ea6e27 --- /dev/null +++ b/sample/https-client.c @@ -0,0 +1,207 @@ +/* + This is an example of how to hook up evhttp with bufferevent_ssl + + It just GETs an https URL given on the command-line and prints the response + body to stdout. + + Actually, it also accepts plain http URLs to make it easy to compare http vs + https code paths. + + Loosely based on le-proxy.c. + */ + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#include +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct event_base *base; + +static void +http_request_done(struct evhttp_request *req, void *ctx) +{ + char buffer[256]; + int nread; + + if (req == NULL) { + fprintf(stderr, "some request failed - no idea which one though!\n"); + return; + } + + fprintf(stderr, "Response line: %d %s\n", + req->response_code, req->response_code_line); + + while ((nread = evbuffer_remove(req->input_buffer, buffer, sizeof(buffer))) + > 0) { + fwrite("> ", 2, 1, stdout); + fwrite(buffer, nread, 1, stdout); + fwrite("\n", 1, 1, stdout); + } +} + +static void +syntax(void) +{ + fputs("Syntax:\n", stderr); + fputs(" https-client \n", stderr); + fputs("Example:\n", stderr); + fputs(" https-client https://ip.appspot.com/\n";, stderr); + + exit(1); +} + +static void +die(const char *msg) +{ + fputs(msg, stderr); + exit(1); +} + +int +main(int argc, char **argv) +{ + int r; + + struct evhttp_uri *http_uri; + const char *url, *scheme, *host, *path, *query; + char uri[256]; + int port; + + SSL_CTX *ssl_ctx; + SSL *ssl; + struct bufferevent *bev; + struct evhttp_connection *evcon; + struct evhttp_request *req; + + if (argc != 2) + syntax(); + + url = argv[1]; + http_uri = evhttp_uri_parse(url); + if (http_uri == NULL) { + die("malformed url"); + } + + scheme = evhttp_uri_get_scheme(http_uri); + if (scheme == NULL || (strcasecmp(scheme, "https") != 0 && + strcasecmp(scheme, "http") != 0)) { + die("url must be http or https"); + } + + host = evhttp_uri_get_host(http_uri); + if (host == NULL) { + die("url must have a host"); + } + + port = evhttp_uri_get_port(http_uri); + if (port == -1) { + port = (strcasecmp(scheme, "http") == 0) ? 80 : 443; + } + + path = evhttp_uri_get_path(http_uri); + if (path == NULL) { + path = "/"; + } + + query = evhttp_uri_get_query(http_uri); + if (query == NULL) { + snprintf(uri, sizeof(uri) - 1, "%s", path); + } else { + snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query); + } + uri[sizeof(uri) - 1] = '\0'; + + // Initialize OpenSSL + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + r = RAND_poll(); + if (r == 0) { + fprintf(stderr, "RAND_poll() failed.\n"); + return 1; + } + ssl_ctx = SSL_CTX_new(SSLv23_method()); + + // Create event base + base = event_base_new(); + if (!base) { + perror("event_base_new()"); + return 1; + } + + // Create OpenSSL bufferevent and stack evhttp on top of it + ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { + fprintf(stderr, "SSL_new() failed\n"); + return 1; + } + + if (strcasecmp(scheme, "http") == 0) { + bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); + } else { + bev = bufferevent_openssl_socket_new(base, -1, ssl, +
Re: [Libevent-users] Re: [PATCH] http: on connection reset, detach the closed fd from the bufferevent
On Tue, Feb 19, 2013 at 4:17 PM, Nick Mathewson wrote: > But the patch isn't quite optimal for that -- we should do the > bufferevent_setfd() before we close the socket, not after. That way > there's never a non-deleted event that refers to the fd. The idea with doing the setfd there was in the case where evcon->fd == -1 but the bufferevent has an fd. Why is there a copy of the fd in evcon? Why not use bufferevent_getfd(evcon->bufev)? > How does the attached patch look to you? Does it work as well/any better? I'll see about canarying it and will reply once it's been running for a while. *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] Re: [PATCH] http: on connection reset, detach the closed fd from the bufferevent
I don't know. I did the debugging on this about 3 weeks ago and my cache is very cold at the moment. Shouldn't bufferevent_disable_hard_(bufev, EV_READ|EV_WRITE) have cleared any fd out of the evmap and epoll? Looking at be_socket_disable: if (event & EV_READ) { if (event_del(&bufev->ev_read) == -1) return -1; } /* Don't actually disable the write if we are trying to connect. */ if ((event & EV_WRITE) && ! bufev_p->connecting) { if (event_del(&bufev->ev_write) == -1) return -1; } In particular the '&& !connecting' part. Seems related to the occurrence of these on fds that time out. On Tue, Feb 19, 2013 at 4:30 PM, Catalin Patulea wrote: > On Tue, Feb 19, 2013 at 4:17 PM, Nick Mathewson wrote: >> But the patch isn't quite optimal for that -- we should do the >> bufferevent_setfd() before we close the socket, not after. That way >> there's never a non-deleted event that refers to the fd. > The idea with doing the setfd there was in the case where evcon->fd == > -1 but the bufferevent has an fd. > > Why is there a copy of the fd in evcon? Why not use > bufferevent_getfd(evcon->bufev)? > >> How does the attached patch look to you? Does it work as well/any better? > I'll see about canarying it and will reply once it's been running for a while. *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] [PATCH] Add sample/https-client.c, an example of stacking evhttp as a client on top of bufferevent_ssl.
On Tue, Feb 19, 2013 at 3:05 PM, Nick Mathewson wrote: >* It could sure use comments! Can you be more specific? This all feels like a lot of boilerplate to me. Parse the URL, initialize OpenSSL, create some bufferevents. I'm not sure what more I can say that a reader of bufferevent.h, bufferevent_ssl.sh and SSL_new(3) etc. doesn't already know. >* This is dangerous code; it doesn't do any certificate validation > so far as I can see, and as such gets zero protection from > man-in-the-middle attacks. People who don't know how to use TLS will > be copying our examples here, so we need to make sure to get the > security right. SSL_CTX_set_verify(SSL_VERIFY_PEER, NULL); sound about right to you? I'm trying to figure out whether OpenSSL distributes a set of CA certs and initializes the path or whether I need to do this myself - any idea? *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.
Re: [Libevent-users] [PATCH] Add sample/https-client.c, an example of stacking evhttp as a client on top of bufferevent_ssl.
On Tue, Feb 19, 2013 at 9:40 PM, Jardel Weyrich wrote: > 2) Call SSL_CTX_load_verify_locations passing the path of the CA > certificates installed by the aforementioned package - generally > /etc/ssl/certs/ca-certificates.crt Nick, does this seem like a reasonable solution? SSL_CTX_load_verify_locations(ssl_ctx, "/etc/ssl/certs/ca-certificates.crt", NULL); Anything more than this feels, to me, outside the scope of a libevent sample. In a related vein, is it possible to get OpenSSL to immediately dump errors to stderr? The only API I can find (ERR_*) let you inspect errors after the fact (ERR_get_error, ERR_print_error, etc.) but I would prefer not to clutter the sample with those calls unless necessary? *** To unsubscribe, send an e-mail to majord...@freehaven.net with unsubscribe libevent-usersin the body.