A little more about how my proxy works, My proxy behaves like number two in the previous email. It is purely a bidirectional proxy with no clear tracking of the protocol. With the only exception being it reads the entire (unencrypted) Connect request from the client, and then goes and connects to the server. Once the connection to the server is OK it creates two separate structures that are wrappers for communication over the ssl protocol. These structures are both aware of each other, and can schedule a write, or a read with the other.
It's multi threaded with non-blocking I/O. I'm not sure exactly what you mean by socket discovery, but I think you are asking how my program determines when something is ready? If that's the case then my program uses a select statement to watch the file descriptor to see if it's ready for read or write. It uses a call back system to perform the correct action based on which fd_set was ready. The issue with my file upload shouldn't be lying in the blocking issue since it's non-blocking I/O. It looks like the issue with a SSL_read call from the perspective of the program as the client is returning zero during the upload (which makes sense) but the way my proxy is handling connections the only reason it could have read is if there was a SSL_get_error() that returned a SSL_ERROR_WANT_READ code. I am going to post my ssl_read and ssl_write wrapper functions, and anything else that's important to those two here: void ProxySSLConnection::ssl_read() { if (!_ssl) return; DataBuffer *buf = _other->_buffer; int ret = SSL_read(_ssl, buf->end(), buf->len_avail()); if (ret > 0) { buf->len += ret; _other->schedule_send_buffer(); schedule_read(&ProxySSLConnection::ssl_read); } else { handle_ssl_error(ret, &ProxySSLConnection::ssl_read, "read"); } } void ProxySSLConnection::ssl_write() { if (!_ssl) return; int ret = SSL_write(_ssl, _buffer->begin(), _buffer->len); //printf("WRITING: \n"); //These lines are just debugging. //for(char * buf = _buffer->begin();buf != _buffer->end();buf++){ // printf("%c", *buf); //} if (ret > 0) { _buffer->erase_front(ret); if (!_buffer->empty()) { schedule_write(&ProxySSLConnection::ssl_write); } } else { handle_ssl_error(ret, &ProxySSLConnection::ssl_write, "write"); } } The handle_ssl_error function is: void ProxySSLConnection::handle_ssl_error(int ret, handler_function handler, const char * caller) { int error = SSL_get_error(_ssl, ret); switch (error) { case SSL_ERROR_WANT_READ: schedule_read(handler); break; case SSL_ERROR_WANT_WRITE: schedule_write(handler); break; case SSL_ERROR_ZERO_RETURN: _proxy_thread->shutdown(); break; default: unsigned long err = ERR_get_error(); while (err != 0) { char* err_string = ERR_error_string(err, NULL); cout << err_string << endl; err = ERR_get_error(); } close_connection(); _proxy_thread->shutdown(); break; } } If the following case is added to the switch then the upload is fixed, but the threads never exit: case SSL_ERROR_SYSCALL: //This case is where the uploads fail. schedule_write(handler); break; The caller variable is just for debug purposes. As per Dave Thompson's request I was using it to determine which call was causing the error, and was then calling perror(caller) since I was getting a SSL_ERROR_SYSCALL with a ret of 0, and the perror printed "read: Success". Schedule Read and Write: void ProxySSLConnection::schedule_write(handler_function handler) { _write_handler = handler; _proxy_thread->register_write_callback(this); } void ProxySSLConnection::schedule_read(handler_function handler) { _read_handler = handler; _proxy_thread->register_read_callback(this); } _proxy_thread is the calling thread that created the ProxySSLConnection class. register_read and register_write callbacks are literally just doing an FD_SET() on the file descriptor of the _ssl connection and a fd_set called read_set, and write_set respectively. In each ProxyThread there select statement inside of a main loop that is woken up by either a signal from another thread, or from a file descriptor being ready. When the file descriptor is ready the thread will call on_read or on_write of the appropriate ProxySSLConnection on_read, and on_write: void ProxySSLConnection::on_read() { handle_callback(&_read_handler); } void ProxySSLConnection::on_write() { handle_callback(&_write_handler); } void ProxySSLConnection::handle_callback(handler_function *handler) { if (*handler) { handler_function the_handler = *handler; *handler = NULL; (this->*the_handler)(); schedule_send_buffer(); } } If you need anything else to help you understand how my proxy works please let me know and I will be happy to provide what I can. I know that this can be confusing, but I tried to lay it out in as logical of an order as possible. It's hard to type english descriptions of code. Thank you for any help you can give. -Sam On Tue, Aug 31, 2010 at 9:59 PM, David Schwartz <dav...@webmaster.com>wrote: > > > I'm writing a SSL proxy (which is working great except for this issue) > > and every time I got to attach a file in an email the connection resets, > > and it gets caught in an infinite retransmit loop. > > There are two totally different ways you can make an SSL proxy, and to > figure out your issue, we really need to know which type. > > 1) An SSL proxy can understand the underlying protocol, know which side is > supposed to transmit when, and only try to read from that side. In this > case, it's vital that the proxy correctly track the protocol and not be > reading from one side when it's the other side's turn to send. > > 2) An SSL proxy can ignore the underlying protocol and not know which side > is supposed to transmit when. In this case, the proxy must always be ready > to read from either side. It must never block indefinitely trying to read > from one side. > > You can also have a hybrid. For example, you can read only from the client > side until you get the full request, and then once you process the request, > you switch to bidirectional proxying. > > It is very common for people to naively assume that their code will > magically know which side to read from. I assure, this is not the case. > Unless you carefully track the protocol, all you know is that the client has > to send some data first. But once it does, all bets are off -- again, unless > you carefully track the protocol. > > Also, you don't mention whether your I/O is blocking or non-blocking, and > if non-blocking, how your socket discovery works. This can be subtle with > OpenSSL and your mistake might lie there. For example, if you using blocking > I/O, you can't just block one thread in SSL_read in each direction, because > if you do, there's nothing you can do when SSL_read returns (since the > connection you need to send on is in use, potentially indefinitely, by the > other thread). > > DS > > ______________________________________________________________________ > OpenSSL Project http://www.openssl.org > User Support Mailing List openssl-users@openssl.org > Automated List Manager majord...@openssl.org > -- Sam Jantz Software Engineer