ef46a62014-05-02Henrik Grubbström (Grubba) #pike 7.8 #if constant(SSL.Cipher.CipherAlgorithm) //! Interface similar to @[Stdio.File]. //! //! @ul //! @item //! Handles blocking and nonblocking mode. //! @item //! Handles callback mode in an arbitrary backend (also in blocking //! mode). //! @item //! Read and write operations might each do both reading and //! writing. In callback mode that means that installing either a //! read or a write callback might install both internally. It also //! means that reading in one thread while writing in another //! doesn't work. //! @item //! Callback changing operations like @[set_blocking] and //! @[set_nonblocking] aren't atomic. //! @item //! Apart from the above, thread safety/atomicity characteristics //! are retained. //! @item //! Blocking characterstics are retained for all functions. //! @item //! @[is_open], connection init (@[create]) and close (@[close]) can //! do both reading and writing. //! @item //! @[destroy] attempts to close the stream properly by sending the //! close packet, but since it can't do blocking I/O it's not //! certain that it will succeed. The stream should therefore always //! be closed with an explicit @[close] call. //! @item //! Abrupt remote close without the proper handshake gets the errno //! @[System.EPIPE]. //! @item //! Objects do not contain cyclic references, so they are closed and //! destructed timely when dropped. //! @endul // #define DEBUG // #define SSL3_DEBUG // #define SSL3_DEBUG_MORE // #define SSL3_DEBUG_TRANSPORT #ifdef SSL3_DEBUG protected string stream_descr; #define SSL3_DEBUG_MSG(X...) \ werror ("[thr:" + this_thread()->id_number() + \ "," + (stream ? "" : "ex ") + stream_descr + "] " + X) #ifdef SSL3_DEBUG_MORE #define SSL3_DEBUG_MORE_MSG(X...) SSL3_DEBUG_MSG (X) #endif #else #define SSL3_DEBUG_MSG(X...) 0 #endif #ifndef SSL3_DEBUG_MORE_MSG #define SSL3_DEBUG_MORE_MSG(X...) 0 #endif protected Stdio.File stream; // The stream is closed by shutdown(), which is called directly or // indirectly from destroy() or close() but not from anywhere else. // // Note that a close in nonblocking callback mode might not happen // right away. In that case stream remains set after close() returns, // suitable callbacks are installed for the close packet exchange, and // close_state >= NORMAL_CLOSE. The stream is closed by the callbacks // as soon the close packets are done, or if an error occurs. protected int(-1..65535) linger_time = -1; // The linger behaviour set by linger(). protected SSL.connection conn; // Always set when stream is. Destructed with destroy() at shutdown // since it contains cyclic references. Noone else gets to it, though. protected array(string) write_buffer; // Encrypted data to be written. protected String.Buffer read_buffer; // Decrypted data that has been read. protected mixed callback_id; protected function(void|object,void|mixed:int) accept_callback; protected function(void|mixed,void|string:int) read_callback; protected function(void|mixed:int) write_callback; protected function(void|mixed:int) close_callback; protected Pike.Backend real_backend; // The real backend for the stream. protected Pike.Backend local_backend; // Internally all I/O is done using callbacks. When the real backend // can't be used, either because we aren't in callback mode (i.e. the // user hasn't registered any callbacks), or because we're to do a // blocking operation, this local one takes its place. It's // created on demand. protected int nonblocking_mode; protected enum CloseState { ABRUPT_CLOSE = -1, STREAM_OPEN = 0, STREAM_UNINITIALIZED = 1, NORMAL_CLOSE = 2, // The caller has requested a normal close. CLEAN_CLOSE = 3, // The caller has requested a clean close. } protected CloseState close_state = STREAM_UNINITIALIZED; // ABRUPT_CLOSE is set if there's a remote close without close packet. // The stream is still considered open locally, but reading or writing // to it trigs System.EPIPE. protected enum ClosePacketSendState { CLOSE_PACKET_NOT_SCHEDULED = 0, CLOSE_PACKET_SCHEDULED, CLOSE_PACKET_QUEUED_OR_DONE, CLOSE_PACKET_MAYBE_IGNORED_WRITE_ERROR, CLOSE_PACKET_WRITE_ERROR, } protected ClosePacketSendState close_packet_send_state; // State for the close packet we send. The trickiness here is that if // there's an error writing it, that error should sometimes be // ignored: // // The remote end is allowed to close the connection immediately // without waiting for a response, and we can't close without // attempting to send a close packet first (RFC 2246, section 7.2.1). // So if we've received a close packet we need to try to send one but // ignore any error (typically EPIPE). // // Also, if there is an error we can't report it immediately since the // remote close packet might be sitting in the input buffer. protected int local_errno; // If nonzero, override the errno on the stream with this. protected int cb_errno; // Stores the errno from failed I/O in a callback so that the next // visible I/O operation can report it properly. protected int got_extra_read_call_out; // 1 when we have a call out to ssl_read_callback. We get this when we // need to call read_callback or close_callback but can't do that // right away from ssl_read_callback. See comments in that function // for more details. -1 if we've switched to non-callback mode and // therefore has removed the call out temporarily but need to restore // it when switching back. 0 otherwise. // // -1 is also set before a call to update_internal_state when we want // to schedule an extra read call out; update_internal_state will then // do the actual call out installation if possible. protected int alert_cb_called; // Need to know if the alert callback has been called in // ssl_read_callback since it can't continue in that case. This is // only set temporarily while ssl_read_callback runs. protected constant epipe_errnos = (< System.EPIPE, System.ECONNRESET, #if constant(System.WSAECONNRESET) // The following is returned by winsock on windows. // Pike ought to map it to System.ECONNRESET. System.WSAECONNRESET, #endif >); // Multiset containing the errno codes that can occur if the remote // end has closed the connection. // This macro is used in all user called functions that can report I/O // errors, both at the beginning and after // ssl_(read|write|close)_callback calls. #define FIX_ERRNOS(ERROR, NO_ERROR) do { \ if (cb_errno) { \ /* Got a stored error from a previous callback that has failed. */ \ local_errno = cb_errno; \ cb_errno = 0; \ {ERROR;} \ } \ else { \ local_errno = 0; \ {NO_ERROR;} \ } \ } while (0) // Ignore user installed callbacks if a close has been requested locally. #define CALLBACK_MODE \ ((read_callback || write_callback || close_callback || accept_callback) && \ close_state < NORMAL_CLOSE) #define SSL_HANDSHAKING (!conn->handshake_finished && \ close_state != ABRUPT_CLOSE) #define SSL_CLOSING_OR_CLOSED \ (close_packet_send_state >= CLOSE_PACKET_SCHEDULED || \ /* conn->closing & 2 is more accurate, but the following is quicker \ * and only overlaps a bit with the check above. */ \ conn->closing) // Always wait for input during handshaking and when we expect the // remote end to respond to our close packet. We should also check the // input buffer for a close packet if there was a failure to write our // close packet. #define SSL_INTERNAL_READING \ (SSL_HANDSHAKING || \ (close_state == CLEAN_CLOSE ? \ conn->closing == 1 : \ close_packet_send_state == CLOSE_PACKET_MAYBE_IGNORED_WRITE_ERROR)) // Try to write when there's data in the write buffer or when we have // a close packet to send. The packet is queued separately by // ssl_write_callback in the latter case. #define SSL_INTERNAL_WRITING (sizeof (write_buffer) || \ close_packet_send_state == CLOSE_PACKET_SCHEDULED) #ifdef DEBUG #if constant (Thread.thread_create) #define THREAD_T Thread.Thread #define THIS_THREAD() this_thread() protected void thread_error (string msg, THREAD_T other_thread) { #if 0 && constant (_locate_references) werror ("%s\n%O got %d refs", msg, this, _refs (this)); _locate_references (this); #endif error ("%s" "%s\n" "User callbacks: a=%O r=%O w=%O c=%O\n" "Internal callbacks: r=%O w=%O c=%O\n" "Backend: %O This thread: %O Other thread: %O\n" "%s", msg, !stream ? "Got no stream" : stream->is_open() ? "Stream is open" : "Stream is closed", accept_callback, read_callback, write_callback, close_callback, stream && stream->query_read_callback(), stream && stream->query_write_callback(), stream && stream->query_close_callback(), stream && stream->query_backend(), this_thread(), other_thread, other_thread ? ("Other thread backtrace:\n" + describe_backtrace (other_thread->backtrace()) + "----------\n") : ""); } #else // !constant (Thread.thread_create) #define THREAD_T int #define THIS_THREAD() 1 protected void thread_error (string msg, THREAD_T other_thread) { error ("%s" "%s\n" "User callbacks: a=%O r=%O w=%O c=%O\n" "Internal callbacks: r=%O w=%O c=%O\n" "Backend: %O\n", msg, !stream ? "Got no stream" : stream->is_open() ? "Stream is open" : "Stream is closed", accept_callback, read_callback, write_callback, close_callback, stream && stream->query_read_callback(), stream && stream->query_write_callback(), stream && stream->query_close_callback(), stream && stream->query_backend()); } #endif // !constant (Thread.thread_create) protected THREAD_T op_thread; // FIXME: Looks like the following check can give false alarms since // an fd object can lose all refs even if some callbacks still are // registered. #define CHECK_CB_MODE(CUR_THREAD) do { \ if (Pike.Backend backend = stream && stream->query_backend()) { \ THREAD_T backend_thread = backend->executing_thread(); \ if (backend_thread && backend_thread != CUR_THREAD && \ (stream->query_read_callback() || \ stream->query_write_callback() || \ stream->query_close_callback())) \ /* NB: The other thread backtrace might not be relevant at \ * all here. */ \ thread_error ("In callback mode in a different backend.\n", \ backend_thread); \ } \ } while (0) #define LOW_CHECK(OP_THREAD, CUR_THREAD, \ IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do { \ if (IN_CALLBACK) { \ if (CALLED_FROM_REAL_BACKEND) { \ if (OP_THREAD) \ thread_error ("Called from real backend while doing an operation.\n", \ OP_THREAD); \ } \ else \ if (!OP_THREAD) \ error ("Called from local backend outside an operation.\n"); \ } \ \ if (OP_THREAD && OP_THREAD != CUR_THREAD) \ thread_error ("Doing operation in another thread.\n", OP_THREAD); \ \ CHECK_CB_MODE (CUR_THREAD); \ } while (0) #define CHECK(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do { \ THREAD_T cur_thread = THIS_THREAD(); \ LOW_CHECK (op_thread, cur_thread, IN_CALLBACK, CALLED_FROM_REAL_BACKEND); \ } while (0) #define ENTER(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do { \ THREAD_T old_op_thread; \ { \ THREAD_T cur_thread = THIS_THREAD(); \ old_op_thread = op_thread; \ /* Relying on the interpreter lock here. */ \ op_thread = cur_thread; \ } \ LOW_CHECK (old_op_thread, op_thread, IN_CALLBACK, CALLED_FROM_REAL_BACKEND); \ mixed _op_err = catch #define RESTORE do {op_thread = old_op_thread;} while (0) #define RETURN(RET_VAL) do {RESTORE; return (RET_VAL);} while (0) #define LEAVE \ ; \ RESTORE; \ if (_op_err) throw (_op_err); \ } while (0) #else // !DEBUG #define CHECK_CB_MODE(CUR_THREAD) do {} while (0) #define CHECK(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do {} while (0) #define ENTER(IN_CALLBACK, CALLED_FROM_REAL_BACKEND) do #define RESTORE do {} while (0) #define RETURN(RET_VAL) return (RET_VAL) #define LEAVE while (0) #endif // !DEBUG // stream is assumed to be operational on entry but might be zero // afterwards. cb_errno is assumed to be 0 on entry. #define RUN_MAYBE_BLOCKING(REPEAT_COND, NONWAITING_MODE, \ ENABLE_READS, ERROR_CODE) do { \ run_local_backend: { \ CHECK_CB_MODE (THIS_THREAD()); \ if (!local_backend) local_backend = Pike.SmallBackend(); \ stream->set_backend (local_backend); \ stream->set_id (0); \ \ while (1) { \ float|int(0..0) action; \ \ if (got_extra_read_call_out) { \ /* Do whatever ssl_read_callback needs to do before we \ * continue. Since the first arg is zero here it won't call \ * any user callbacks, so they are superseded as they should \ * be if we're doing an explicit read (or a write or close, \ * which legitimately might cause reading to be done). Don't \ * need to bother with the return value from ssl_read_callback \ * since we'll propagate the error, if any, just below. */ \ if (got_extra_read_call_out > 0) \ real_backend->remove_call_out (ssl_read_callback); \ ssl_read_callback (0, 0); /* Will clear got_extra_read_call_out. */ \ action = 0.0; \ } \ \ else { \ stream->set_write_callback (SSL_INTERNAL_WRITING && ssl_write_callback); \ \ if (ENABLE_READS) { \ stream->set_read_callback (ssl_read_callback); \ stream->set_close_callback (ssl_close_callback); \ } \ else { \ stream->set_read_callback (0); \ /* Installing a close callback without a read callback \ * currently doesn't work well in Stdio.File. */ \ stream->set_close_callback (0); \ } \ \ /* When we fail to write the close packet we should check if \ * a close packet is in the input buffer before signalling \ * the error. That means installing the read callbacks \ * without waiting in the backend. */ \ int zero_timeout = NONWAITING_MODE || \ close_packet_send_state == CLOSE_PACKET_MAYBE_IGNORED_WRITE_ERROR; \ \ SSL3_DEBUG_MSG ("Running local backend [r:%O w:%O], %s timeout\n", \ !!stream->query_read_callback(), \ !!stream->query_write_callback(), \ zero_timeout ? "zero" : "infinite"); \ \ action = local_backend (zero_timeout ? 0.0 : 0); \ } \ \ if (!action && \ close_packet_send_state == CLOSE_PACKET_MAYBE_IGNORED_WRITE_ERROR) { \ SSL3_DEBUG_MSG ("Did not get a remote close - " \ "signalling delayed error from writing close message\n"); \ cleanup_on_error(); \ close_packet_send_state = CLOSE_PACKET_WRITE_ERROR; \ cb_errno = System.EPIPE; \ close_state = ABRUPT_CLOSE; \ } \ \ FIX_ERRNOS ({ \ SSL3_DEBUG_MSG ("Local backend ended with error\n"); \ if (stream) { \ stream->set_id (1); \ update_internal_state (1); \ /* Switch backend after updating the installed callbacks. */ \ stream->set_backend (real_backend); \ CHECK_CB_MODE (THIS_THREAD()); \ } \ {ERROR_CODE;} \ break run_local_backend; \ }, 0); \ \ if (!stream) { \ SSL3_DEBUG_MSG ("Local backend ended after close\n"); \ break run_local_backend; \ } \ \ if (NONWAITING_MODE && !action) { \ SSL3_DEBUG_MSG ("Nonwaiting local backend ended - nothing to do\n"); \ break; \ } \ \ if (!(REPEAT_COND)) { \ SSL3_DEBUG_MSG ("Local backend ended - repeat condition false\n"); \ break; \ } \ } \ \ stream->set_id (1); \ update_internal_state (1); \ /* Switch backend after updating the installed callbacks. */ \ stream->set_backend (real_backend); \ CHECK_CB_MODE (THIS_THREAD()); \ } \ } while (0) protected void create (Stdio.File stream, SSL.context ctx, int|void is_client, int|void is_blocking, SSL.Constants.ProtocolVersion|void min_version, SSL.Constants.ProtocolVersion|void max_version) //! Create an SSL connection over an open @[stream]. //! //! @param stream //! Open socket or pipe to create the connection over. //! //! @param ctx //! The SSL context. //! //! @param is_client //! If is set then a client-side connection is started, //! server-side otherwise. //! //! @param is_blocking //! If is set then the stream is initially set in blocking //! mode, nonblocking mode otherwise. //! //! @param min_version //! The minimum minor version of SSL to support. //! Defaults to @[PROTOCOL_SSL_3_0]. //! //! @param max_version //! The maximum minor version of SSL to support. //! Defaults to @[PROTOCOL_minor]. //! //! The backend used by @[stream] is taken over and restored after the //! connection is closed (see @[close] and @[shutdown]). The callbacks //! and id in @[stream] are overwritten. //! //! @throws //! Throws errors on handshake failure in blocking client mode. { SSL3_DEBUG_MSG ("SSL.sslfile->create (%O, %O, %O, %O, %O, %O)\n", stream, ctx, is_client, is_blocking, min_version, max_version); ENTER (0, 0) { global::stream = stream; #ifdef SSL3_DEBUG if (stream->query_fd) stream_descr = "fd:" + stream->query_fd(); else stream_descr = replace (sprintf ("%O", stream), "%", "%%"); #endif write_buffer = ({}); read_buffer = String.Buffer(); real_backend = stream->query_backend(); close_state = STREAM_OPEN; #if 0 // Unnecessary to init stuff to zero. callback_id = 0; local_backend = 0; close_packet_send_state = CLOSE_PACKET_NOT_SCHEDULED; local_errno = cb_errno = 0; got_extra_read_call_out = 0; alert_cb_called = 0; #endif stream->set_read_callback (0); stream->set_write_callback (0); stream->set_close_callback (0); stream->set_id (1); if(!ctx->random) ctx->random = Crypto.Random.random_string; conn = SSL.connection (!is_client, ctx, min_version, max_version); if(is_blocking) { set_blocking(); if (is_client && !direct_write()) { int err = errno(); shutdown(); local_errno = err; error("SSL.sslfile: SSL handshake failure: %s\n", strerror(err)); } } else { set_nonblocking(); if (is_client) queue_write(); } } LEAVE; } mixed get_server_names() { return conn->server_names; } //! @returns //! Returns peer certificate information, if any. mapping get_peer_certificate_info() { return conn->session->cert_data; } //! @returns //! Returns the peer certificate chain, if any. array get_peer_certificates() { return conn->session->peer_certificate_chain; } //! Set the linger time on @[close()]. int(0..1) linger(int(-1..65535)|void seconds) { if (!stream) return 0; if (zero_type(seconds)) seconds = -1; if (seconds == linger_time) { // Noop. return 1; } if (stream->linger && !stream->linger(seconds)) return 0; linger_time = seconds; return 1; } int close (void|string how, void|int clean_close, void|int dont_throw) //! Close the connection. Both the read and write ends are always //! closed //! //! @param how //! This argument is only for @[Stdio.File] compatibility //! and must be either @expr{"rw"@} or @expr{0@}. //! //! @param clean_close //! If set then close messages are exchanged to shut down //! the SSL connection but not the underlying stream. It may then //! continue to be used for other communication afterwards. The //! default is to send a close message and then close the stream //! without waiting for a response. //! //! @param dont_throw //! I/O errors are normally thrown, but that can be turned off with //! @[dont_throw]. In that case @[errno] is set instead and @expr{0@} is //! returned. @expr{1@} is always returned otherwise. It's not an error to //! close an already closed connection. //! //! @note //! If a clean close is requested in nonblocking mode then the stream //! is most likely not closed right away, and the backend is then //! still needed for a while afterwards to exchange the close packets. //! @[is_open] returns 2 in that time window. //! //! @note //! I/O errors from both reading and writing might occur in blocking //! mode. //! //! @note //! If a clean close is requested and data following the close message //! is received at the same time, then this object will read it and //! has no way to undo that. That data can be retrieved with @[read] //! afterwards. //! //! @seealso //! @[shutdown] { SSL3_DEBUG_MSG ("SSL.sslfile->close (%O, %O, %O)\n", how, clean_close, dont_throw); if (how && how != "rw") error ("Can only close the connection in both directions simultaneously.\n"); ENTER (0, 0) { if (close_state > STREAM_OPEN) { SSL3_DEBUG_MSG ("SSL.sslfile->close: Already closed (%d)\n", close_state); RETURN (1); } close_state = clean_close ? CLEAN_CLOSE : NORMAL_CLOSE; FIX_ERRNOS ({ SSL3_DEBUG_MSG ("SSL.sslfile->close: Shutdown after error\n"); int err = errno(); shutdown(); // Get here e.g. if a close callback calls close after an // error, so never throw. (I'm somewhat suspicious to this, // but I guess I did it with good reason.. :P /mast) local_errno = err; RETURN (0); }, 0); if (close_packet_send_state == CLOSE_PACKET_NOT_SCHEDULED) close_packet_send_state = CLOSE_PACKET_SCHEDULED; // Even in nonblocking mode we call direct_write here to try to // put the close packet in the send buffer before we return. That // way it has a fair chance to get sent even when we're called // from destroy() (in which case it won't work to just install the // write callback as usual and wait for the backend to call it). if (!direct_write()) { // Should be shut down after close(), even if an error occurred. int err = errno(); shutdown(); if (dont_throw) { local_errno = err; RETURN (0); } else if( err != System.EPIPE ) // Errors are normally thrown from close(). error ("Failed to close SSL connection: %s\n", strerror (err)); } if (stream && (stream->query_read_callback() || stream->query_write_callback())) { SSL3_DEBUG_MSG ("SSL.sslfile->close: Close underway\n"); RESTORE; update_internal_state(); return 1; } else { // The local backend run by direct_write has typically already // done this, but it might happen that it doesn't do anything in // case close packets already have been exchanged. shutdown(); SSL3_DEBUG_MSG ("SSL.sslfile->close: Close done\n"); } } LEAVE; return 1; } protected void cleanup_on_error() // Called when any error occurs on the stream. (Doesn't handle errno // reporting since it might involve either local_errno and/or // cb_errno.) { // The session should be purged when an error has occurred. if (conn->session) // Check conn->session since it doesn't exist before the // handshake. conn->context->purge_session (conn->session); } Stdio.File shutdown() //! Shut down the SSL connection without sending any more packets. //! //! If the connection is open then the underlying (still open) stream //! is returned. //! //! If a nonclean (i.e. normal) close has been requested then the //! underlying stream is closed now if it wasn't closed already, and //! zero is returned. //! //! If a clean close has been requested (see the second argument to //! @[close]) then the behavior depends on the state of the close //! packet exchange: The first @[shutdown] call after a successful //! exchange returns the (still open) underlying stream, and later //! calls return zero and clears @[errno]. If the exchange hasn't //! finished then the stream is closed, zero is returned, and @[errno] //! will return @[System.EPIPE]. //! //! @seealso //! @[close], @[set_alert_callback] { ENTER (0, 0) { if (!stream) { SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Already shut down\n"); RETURN (0); } if (close_state == STREAM_OPEN || close_state == NORMAL_CLOSE) // If we didn't request a clean close then we pretend to have // received a close message. According to the standard it's ok // anyway as long as the transport isn't used for anything else. conn->closing |= 2; SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): %s\n", close_state != ABRUPT_CLOSE && conn->closing & 2 && close_packet_send_state == CLOSE_PACKET_QUEUED_OR_DONE && !sizeof (write_buffer) ? "Proper close" : close_state == STREAM_OPEN ? "Not closed" : "Abrupt close"); if ((conn->closing & 2) && sizeof (conn->left_over || "")) { #ifdef DEBUG werror ("Warning: Got buffered data after close in %O: %O%s\n", this, conn->left_over[..99], sizeof (conn->left_over) > 100 ? "..." : ""); #endif read_buffer = String.Buffer (sizeof (conn->left_over)); read_buffer->add (conn->left_over); close_state = STREAM_OPEN; } int conn_closing = conn->closing; destruct (conn); // Necessary to avoid garbage. write_buffer = ({}); #if 0 accept_callback = 0; read_callback = 0; write_callback = 0; close_callback = 0; #endif if (got_extra_read_call_out > 0) real_backend->remove_call_out (ssl_read_callback); got_extra_read_call_out = 0; Stdio.File stream = global::stream; global::stream = 0; stream->set_read_callback (0); stream->set_write_callback (0); stream->set_close_callback (0); switch (close_state) { case CLEAN_CLOSE: if (conn_closing == 3) { SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Clean close - " "leaving stream\n"); local_errno = 0; RETURN (stream); } else { SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Close packets not fully " "exchanged after clean close (%d) - closing stream\n", conn_closing); stream->close(); local_errno = System.EPIPE; RETURN (0); } case STREAM_OPEN: close_state = STREAM_UNINITIALIZED; SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Not closed - leaving stream\n"); local_errno = 0; RETURN (stream); default: SSL3_DEBUG_MSG ("SSL.sslfile->shutdown(): Nonclean close - closing stream\n"); if (stream->linger) stream->linger(0); stream->close(); local_errno = stream->errno(); RETURN (0); } } LEAVE; } protected void destroy() //! Try to close down the connection properly since it's customary to //! close files just by dropping them. No guarantee can be made that //! the close packet gets sent successfully though, because we can't //! risk blocking I/O here. You should call @[close] explicitly. //! //! @seealso //! @[close] { SSL3_DEBUG_MSG ("SSL.sslfile->destroy()\n"); // We don't know which thread this will be called in if the refcount // garb or the gc got here. That's not a race problem since it won't // be registered in a backend in that case. ENTER (0, 0) { if (stream) { if (close_state == STREAM_OPEN && // Don't bother with closing nicely if there's an error from // an earlier operation. close() will throw an error for it. !cb_errno) { set_nonblocking_keep_callbacks(); close (0, 0, 1); } else shutdown(); } } LEAVE; } string read (void|int length, void|int(0..1) not_all) //! Read some (decrypted) data from the connection. Works like //! @[Stdio.File.read]. //! //! @note //! I/O errors from both reading and writing might occur in blocking //! mode. //! //! @seealso //! @[write] { SSL3_DEBUG_MSG ("SSL.sslfile->read (%d, %d)\n", length, not_all); ENTER (0, 0) { if (close_state > STREAM_OPEN) error ("Not open.\n"); FIX_ERRNOS ({ SSL3_DEBUG_MSG ("SSL.sslfile->read: Propagating old callback error: %s\n", strerror (local_errno)); RETURN (0); }, 0); if (stream) if (not_all) { if (!sizeof (read_buffer)) RUN_MAYBE_BLOCKING (!sizeof (read_buffer) && !(conn->closing & 2), 0, 1, if (sizeof (read_buffer)) { // Got data to return first. Push the // error back so it'll get reported by // the next call. cb_errno = local_errno; local_errno = 0; } else RETURN (0);); } else { if (sizeof (read_buffer) < length || zero_type (length)) RUN_MAYBE_BLOCKING ((sizeof (read_buffer) < length || zero_type (length)) && !(conn->closing & 2), nonblocking_mode, 1, if (sizeof (read_buffer)) { // Got data to return first. Push the // error back so it'll get reported by // the next call. cb_errno = local_errno; local_errno = 0; } else RETURN (0);); } string res = read_buffer->get(); if (!zero_type (length)) { read_buffer->add (res[length..]); res = res[..length-1]; } SSL3_DEBUG_MSG ("SSL.sslfile->read: Read done, returning %d bytes " "(%d still in buffer)\n", sizeof (res), sizeof (read_buffer)); RETURN (res); } LEAVE; } int write (string|array(string) data, mixed... args) //! Write some (unencrypted) data to the connection. Works like //! @[Stdio.File.write] except that this function often buffers some data //! internally, so there's no guarantee that all the consumed data has //! been successfully written to the stream in nonblocking mode. It //! keeps the internal buffering to a minimum, however. //! //! @note //! This function returns zero if attempts are made to write data //! during the handshake phase and the mode is nonblocking. //! //! @note //! I/O errors from both reading and writing might occur in blocking //! mode. //! //! @seealso //! @[read] { if (sizeof (args)) data = sprintf (arrayp (data) ? data * "" : data, @args); SSL3_DEBUG_MSG ("SSL.sslfile->write (%t[%d])\n", data, sizeof (data)); ENTER (0, 0) { if (close_state > STREAM_OPEN) error ("Not open.\n"); FIX_ERRNOS ({ SSL3_DEBUG_MSG ("SSL.sslfile->write: Propagating old callback error: %s\n", strerror (local_errno)); RETURN (-1); }, 0); if (nonblocking_mode && SSL_HANDSHAKING) { SSL3_DEBUG_MSG ("SSL.sslfile->write: " "Still in handshake - cannot accept application data\n"); RETURN (0); } // Take care of any old data first. if (!direct_write()) RETURN (-1); int written = 0; if (arrayp (data)) { int idx = 0, pos = 0; while (idx < sizeof (data) && !sizeof (write_buffer) && // Always stop after writing DATA_CHUNK_SIZE in // nonblocking mode, so that we don't loop here // arbitrarily long if the write is very large and the // bottleneck is in the encryption. (!nonblocking_mode || written < Stdio.DATA_CHUNK_SIZE)) { int size = sizeof (data[idx]) - pos; if (size > SSL.Constants.PACKET_MAX_SIZE) { // send_streaming_data will pick the first PACKET_MAX_SIZE // bytes of the string, so do that right away in the same // range operation. int n = conn->send_streaming_data ( data[idx][pos..pos + SSL.Constants.PACKET_MAX_SIZE - 1]); SSL3_DEBUG_MSG ("SSL.sslfile->write: Queued data[%d][%d..%d]\n", idx, pos, pos + n - 1); written += n; pos += n; } else { // Try to fill a packet. int end; for (end = idx + 1; end < sizeof (data); end++) { int newsize = size + sizeof (data[end]); if (newsize > SSL.Constants.PACKET_MAX_SIZE) break; size = newsize; } if (conn->send_streaming_data ( `+ (data[idx][pos..], @data[idx+1..end-1])) < size) error ("Unexpected PACKET_MAX_SIZE discrepancy wrt send_streaming_data.\n"); SSL3_DEBUG_MSG ("SSL.sslfile->write: " "Queued data[%d][%d..%d] + data[%d..%d]\n", idx, pos, sizeof (data[idx]) - 1, idx + 1, end - 1); written += size; idx = end; pos = 0; } if (!direct_write()) RETURN (written); } } else // data is a string. while (written < sizeof (data) && !sizeof (write_buffer) && // Limit the amount written in a single call, for the // same reason as above. (!nonblocking_mode || written < Stdio.DATA_CHUNK_SIZE)) { int n = conn->send_streaming_data ( data[written..written + SSL.Constants.PACKET_MAX_SIZE - 1]); SSL3_DEBUG_MSG ("SSL.sslfile->write: Queued data[%d..%d]\n", written, written + n - 1); written += n; if (!direct_write()) RETURN (written); } SSL3_DEBUG_MSG ("SSL.sslfile->write: Write %t done, accepted %d bytes\n", data, written); RETURN (written); } LEAVE; } int renegotiate() //! Renegotiate the connection by starting a new handshake. Note that //! the accept callback will be called again when the handshake is //! finished. //! //! Returns zero if there are any I/O errors. @[errno()] will give the //! details. //! //! @note //! The read buffer is not cleared - a @expr{read()@} afterwards will //! return data from both before and after the renegotiation. //! //! @bugs //! Data in the write queue in nonblocking mode is not properly //! written before resetting the connection. Do a blocking //! @expr{write("")@} first to avoid problems with that. { SSL3_DEBUG_MSG ("SSL.sslfile->renegotiate()\n"); ENTER (0, 0) { if (close_state > STREAM_OPEN) error ("Not open.\n"); FIX_ERRNOS ({ SSL3_DEBUG_MSG ("SSL.sslfile->renegotiate: " "Propagating old callback error: %s\n", strerror (local_errno)); RETURN (0); }, 0); if (!stream) { SSL3_DEBUG_MSG ("SSL.sslfile->renegotiate: " "Connection closed - simulating System.EPIPE\n"); cleanup_on_error(); local_errno = System.EPIPE; RETURN (0); } // FIXME: Change this state with a packet instead so that things // currently in the queue aren't affect by it. conn->expect_change_cipher = 0; conn->certificate_state = 0; conn->handshake_finished = 0; update_internal_state(); conn->send_packet(conn->hello_request()); RETURN (direct_write()); } LEAVE; } void set_callbacks (void|function(mixed, string:int) read, void|function(mixed:int) write, void|function(mixed:int) close, void|function(mixed, string:int) read_oob, void|function(mixed:int) write_oob, void|function(void|mixed:int) accept) //! Installs all the specified callbacks at once. Use @[UNDEFINED] //! to keep the current setting for a callback. //! //! Like @[set_nonblocking], the callbacks are installed atomically. //! As opposed to @[set_nonblocking], this function does not do //! anything with the stream, and it doesn't even have to be open. //! //! @bugs //! @[read_oob] and @[write_oob] are currently ignored. //! //! @seealso //! @[set_read_callback], @[set_write_callback], //! @[set_close_callback], @[set_accept_callback], @[query_callbacks] { SSL3_DEBUG_MSG ("SSL.sslfile->set_callbacks (%O, %O, %O, %O, %O, %O)\n%s", read, write, close, read_oob, write_oob, accept, "" || describe_backtrace (backtrace())); ENTER(0, 0) { // Bypass the ::set_xxx_callback functions; we instead enable all // the event bits at once through the _enable_callbacks call at the end. if (!zero_type(read)) read_callback = read; if (!zero_type(write)) write_callback = write; if (!zero_type(close)) close_callback = close; #if 0 if (!zero_type(read_oob)) read_oob_callback = read_oob; if (!zero_type (write_oob_cb)) write_oob_callback = write_oob; #endif if (!zero_type(accept)) accept_callback = accept; if (stream) update_internal_state(); } LEAVE; } //! @returns //! Returns the currently set callbacks in the same order //! as the arguments to @[set_callbacks]. //! //! @seealso //! @[set_callbacks], @[set_nonblocking] array(function(mixed,void|string:int)) query_callbacks() { return ({ read_callback, write_callback, close_callback, UNDEFINED, UNDEFINED, accept_callback, }); } void set_nonblocking (void|function(void|mixed,void|string:int) read, void|function(void|mixed:int) write, void|function(void|mixed:int) close, void|function(void|mixed:int) read_oob, void|function(void|mixed:int) write_oob, void|function(void|mixed:int) accept) //! Set the stream in nonblocking mode, installing the specified //! callbacks. The alert callback isn't touched. //! //! @note //! Prior to version 7.5.12, this function didn't set the accept //! callback. //! //! @bugs //! @[read_oob] and @[write_oob] are currently ignored. //! //! @seealso //! @[set_callbacks], @[query_callbacks], @[set_nonblocking_keep_callbacks], //! @[set_blocking] { SSL3_DEBUG_MSG ("SSL.sslfile->set_nonblocking (%O, %O, %O, %O, %O, %O)\n%s", read, write, close, read_oob, write_oob, accept, "" || describe_backtrace (backtrace())); ENTER (0, 0) { if (close_state > STREAM_OPEN) error ("Not open.\n"); nonblocking_mode = 1; accept_callback = accept; read_callback = read; write_callback = write; close_callback = close; if (stream) { stream->set_nonblocking_keep_callbacks(); // Has to restore here since a backend waiting in another thread // might be woken immediately when callbacks are registered. RESTORE; update_internal_state(); return; } } LEAVE; } void set_nonblocking_keep_callbacks() //! Set nonblocking mode like @[set_nonblocking], but don't alter any //! callbacks. //! //! @seealso //! @[set_nonblocking], @[set_blocking], @[set_blocking_keep_callbacks] { SSL3_DEBUG_MSG ("SSL.sslfile->set_nonblocking_keep_callbacks()\n"); ENTER (0, 0) { if (close_state > STREAM_OPEN) error ("Not open.\n"); nonblocking_mode = 1; if (stream) { stream->set_nonblocking_keep_callbacks(); // Has to restore here since a backend waiting in another thread // might be woken immediately when callbacks are registered. RESTORE; update_internal_state(); return; } } LEAVE; } void set_blocking() //! Set the stream in blocking mode. All but the alert callback are //! zapped. //! //! @note //! There might be some data still waiting to be written to the //! stream. That will be written in the next blocking call, regardless //! what it is. //! //! @note //! This function doesn't solve the case when the connection is used //! nonblocking in some backend thread and another thread switches it //! to blocking and starts using it. To solve that, put a call out in //! the backend from the other thread that switches it to blocking, //! and then wait until that call out has run. //! //! @note //! Prior to version 7.5.12, this function didn't clear the accept //! callback. //! //! @seealso //! @[set_nonblocking], @[set_blocking_keep_callbacks], //! @[set_nonblocking_keep_callbacks] { // Previously this function wrote the remaining write buffer to the // stream directly. But that can only be done safely if we implement // a lock here to wait for any nonblocking operations to complete, // and that could introduce a deadlock since there might be other // lock dependencies between the threads. SSL3_DEBUG_MSG ("SSL.sslfile->set_blocking()\n%s", "" || describe_backtrace (backtrace())); ENTER (0, 0) { if (close_state > STREAM_OPEN) error ("Not open.\n"); nonblocking_mode = 0; accept_callback = read_callback = write_callback = close_callback = 0; if (stream) { update_internal_state(); stream->set_blocking(); } } LEAVE; } void set_blocking_keep_callbacks() //! Set blocking mode like @[set_blocking], but don't alter any //! callbacks. //! //! @seealso //! @[set_blocking], @[set_nonblocking] { SSL3_DEBUG_MSG ("SSL.sslfile->set_blocking_keep_callbacks()\n"); ENTER (0, 0) { if (close_state > STREAM_OPEN) error ("Not open.\n"); nonblocking_mode = 0; if (stream) { update_internal_state(); stream->set_blocking(); } } LEAVE; } int errno() //! @returns //! Returns the current error number for the connection. //! Notable values are: //! @int //! @value 0 //! No error //! @value System.EPIPE //! Connection closed by other end. //! @endint { // We don't check threads for most other query functions, but // looking at errno while doing I/O in another thread can't be done // safely. CHECK (0, 0); return local_errno ? local_errno : stream && stream->errno(); } void set_alert_callback (function(object,int|object,string:void) alert) //! Install a function that will be called when an alert packet is about //! to be sent. It doesn't affect the callback mode - it's called both //! from backends and from within normal function calls like @[read] //! and @[write]. //! //! This callback can be used to implement fallback to other protocols //! when used on the server side together with @[shutdown()]. //! //! @note //! This object is part of a cyclic reference whenever this is set, //! just like setting any other callback. //! //! @note //! This callback is not cleared by @[set_blocking], or settable //! by @[set_callbacks] or @[set_nonblocking]. It is also not //! part of the set returned by @[query_callbacks]. //! //! @seealso //! @[query_alert_callback] { SSL3_DEBUG_MSG ("SSL.sslfile->set_alert_callback (%O)\n", alert); CHECK (0, 0); #ifdef DEBUG if (close_state == STREAM_UNINITIALIZED || !conn) error ("Doesn't have any connection.\n"); #endif conn->set_alert_callback ( alert && lambda (object packet, int|object seq_num, string alert_context) { SSL3_DEBUG_MSG ("Calling alert callback %O\n", alert); alert (packet, seq_num, alert_context); alert_cb_called = 1; }); } function(object,int|object,string:void) query_alert_callback() //! @returns //! Returns the current alert callback. //! //! @seealso //! @[set_alert_callback] { return conn && conn->alert_callback; } void set_accept_callback (function(void|object,void|mixed:int) accept) //! Install a function that will be called when the handshake is //! finished and the connection is ready for use. //! //! The callback function will be called with the sslfile object //! and the additional id arguments (set with @[set_id]). //! //! @note //! Like the read, write and close callbacks, installing this callback //! implies callback mode, even after the handshake is done. //! //! @seealso //! @[set_nonblocking], @[set_callbacks], //! @[query_accept_callback], @[query_callbacks] { SSL3_DEBUG_MSG ("SSL.sslfile->set_accept_callback (%O)\n", accept); ENTER (0, 0) { #ifdef DEBUG if (close_state == STREAM_UNINITIALIZED) error ("Doesn't have any connection.\n"); #endif accept_callback = accept; if (stream) update_internal_state(); } LEAVE; } function(void|object,void|mixed:int) query_accept_callback() //! @returns //! Returns the current accept callback. //! //! @seealso //! @[set_accept_callback] { return accept_callback; } void set_read_callback (function(void|mixed,void|string:int) read) //! Install a function to be called when data is available. //! //! @seealso //! @[query_read_callback], @[set_nonblocking], @[query_callbacks] { SSL3_DEBUG_MSG ("SSL.sslfile->set_read_callback (%O)\n", read); ENTER (0, 0) { #ifdef DEBUG if (close_state == STREAM_UNINITIALIZED) error ("Doesn't have any connection.\n"); #endif read_callback = read; if (stream) update_internal_state(); } LEAVE; } function(void|mixed,void|string:int) query_read_callback() //! @returns //! Returns the current read callback. //! //! @seealso //! @[set_read_callback], @[set_nonblocking], @[query_callbacks] { return read_callback; } void set_write_callback (function(void|mixed:int) write) //! Install a function to be called when data can be written. //! //! @seealso //! @[query_write_callback], @[set_nonblocking], @[query_callbacks] { SSL3_DEBUG_MSG ("SSL.sslfile->set_write_callback (%O)\n", write); ENTER (0, 0) { #ifdef DEBUG if (close_state == STREAM_UNINITIALIZED) error ("Doesn't have any connection.\n"); #endif write_callback = write; if (stream) update_internal_state(); } LEAVE; } function(void|mixed:int) query_write_callback() //! @returns //! Returns the current write callback. //! //! @seealso //! @[set_write_callback], @[set_nonblocking], @[query_callbacks] { return write_callback; } void set_close_callback (function(void|mixed:int) close) //! Install a function to be called when the connection is closed, //! either normally or due to an error (use @[errno] to retrieve it). //! //! @seealso //! @[query_close_callback], @[set_nonblocking], @[query_callbacks] { SSL3_DEBUG_MSG ("SSL.sslfile->set_close_callback (%O)\n", close); ENTER (0, 0) { #ifdef DEBUG if (close_state == STREAM_UNINITIALIZED) error ("Doesn't have any connection.\n"); #endif close_callback = close; if (stream) update_internal_state(); } LEAVE; } function(void|mixed:int) query_close_callback() //! @returns //! Returns the current close callback. //! //! @seealso //! @[set_close_callback], @[set_nonblocking], @[query_callbacks] { return close_callback; } void set_id (mixed id) //! Set the value to be sent as the first argument to the //! callbacks installed by @[set_callbacks]. //! //! @seealso //! @[query_id] { SSL3_DEBUG_MSG ("SSL.sslfile->set_id (%O)\n", id); CHECK (0, 0); callback_id = id; } mixed query_id() //! @returns //! Returns the currently set id. //! //! @seealso //! @[set_id] { return callback_id; } void set_backend (Pike.Backend backend) //! Set the backend used for the file callbacks. //! //! @seealso //! @[query_backend] { ENTER (0, 0) { if (close_state > STREAM_OPEN) error ("Not open.\n"); if (stream) { if (stream->query_backend() != local_backend) stream->set_backend (backend); if (got_extra_read_call_out > 0) { real_backend->remove_call_out (ssl_read_callback); backend->call_out (ssl_read_callback, 0, 1, 0); } } real_backend = backend; } LEAVE; } Pike.Backend query_backend() //! Return the backend used for the file callbacks. //! //! @seealso //! @[set_backend] { if (close_state > STREAM_OPEN) error ("Not open.\n"); return real_backend; } string query_address(int|void arg) //! @returns //! Returns the address and port of the connection. //! //! See @[Stdio.File.query_address] for details. //! //! @seealso //! @[Stdio.File.query_address] { if (close_state > STREAM_OPEN) error ("Not open.\n"); return stream->query_address(arg); } int is_open() //! @returns //! Returns nonzero if the stream currently is open, zero otherwise. //! //! This function does nonblocking I/O to check for a close packet in //! the input buffer. //! //! If a clean close has been requested in nonblocking mode, then 2 is //! returned until the close packet exchanged has been completed. //! //! @note //! In Pike 7.8 and earlier, this function returned zero in the case //! above where it now returns 2. { SSL3_DEBUG_MSG ("SSL.sslfile->is_open()\n"); ENTER (0, 0) { if ((close_state == STREAM_OPEN || close_state == CLEAN_CLOSE) && stream && stream->is_open()) { // When close_state == STREAM_OPEN, we have to check if there's // a close packet waiting to be read. This is common in // keep-alive situations since the remote end might have sent a // close packet and closed the connection a long time ago, and // in a typical blocking client no action has been taken causing // the close packet to be read. // // If close_state == CLEAN_CLOSE, we should return true as long // as close packets haven't been exchanged in both directions. // // Could avoid the whole local backend hoopla here by // essentially doing a peek and call ssl_read_callback directly, // but that'd lead to subtle code duplication. (Also, peek is // currently not implemented on NT.) if ((close_state == CLEAN_CLOSE ? conn->closing != 3 : !conn->closing)) RUN_MAYBE_BLOCKING ( action && (close_state == CLEAN_CLOSE ? conn->closing != 3 : !conn->closing), 1, 1, RETURN (!epipe_errnos[local_errno])); RETURN (conn && (close_state == CLEAN_CLOSE ? conn->closing != 3 && 2 : !conn->closing)); } } LEAVE; return 0; } Stdio.File query_stream() //! Return the underlying stream. //! //! @note //! Avoid any temptation to do //! @expr{destruct(sslfile_obj->query_stream())@}. That almost //! certainly creates more problems than it solves. //! //! You probably want to use @[shutdown]. //! //! @seealso //! @[shutdown] { SSL3_DEBUG_MSG ("SSL.sslfile->query_stream(): Called from %s:%d\n", backtrace()[-2][0], backtrace()[-2][1]); return stream; } SSL.connection query_connection() //! Return the SSL connection object. //! //! This returns the low-level @[SSL.connection] object. { SSL3_DEBUG_MSG ("SSL.sslfile->query_connection(): Called from %s:%d\n", backtrace()[-2][0], backtrace()[-2][1]); return conn; } SSL.context query_context() //! Return the SSL context object. { return conn && conn->context; } string _sprintf(int t) { return t=='O' && sprintf("SSL.sslfile(%O)", stream && stream->_fd); } protected void update_internal_state (void|int assume_real_backend) // Update the internal callbacks according to the current state. Does // nothing if the local backend is active, unless assume_real_backend // is set, in which case we're installing callbacks for the real // backend anyway (necessary to avoid races when we're about to switch // from the local to the real backend). { // When the local backend is used, callbacks are set explicitly // before it's started. if (assume_real_backend || stream->query_backend() != local_backend) { mixed install_read_cbs, install_write_cb; if (nonblocking_mode && (SSL_HANDSHAKING || close_state >= NORMAL_CLOSE)) { // Normally we never install our own callbacks if we aren't in // callback mode, but handling a nonblocking close is an // exception. install_read_cbs = SSL_INTERNAL_READING; install_write_cb = SSL_INTERNAL_WRITING; SSL3_DEBUG_MORE_MSG ("update_internal_state: " "%s [r:%O w:%O rcb:%O]\n", SSL_HANDSHAKING ? "Handshaking" : "After close", !!install_read_cbs, !!install_write_cb, got_extra_read_call_out); } // CALLBACK_MODE but slightly optimized below. else if (read_callback || write_callback || close_callback || accept_callback) { install_read_cbs = (read_callback || close_callback || accept_callback || SSL_INTERNAL_READING); install_write_cb = (write_callback || SSL_INTERNAL_WRITING); SSL3_DEBUG_MORE_MSG ("update_internal_state: " "Callback mode [r:%O w:%O rcb:%O]\n", !!install_read_cbs, !!install_write_cb, got_extra_read_call_out); } else { // Not in callback mode. Can't install callbacks even though we'd // "need" to - have to cope with the local backend in each // operation instead. SSL3_DEBUG_MORE_MSG ("update_internal_state: " "Not in callback mode [rcb:%O]\n", got_extra_read_call_out); } if (!got_extra_read_call_out && sizeof (read_buffer) && read_callback) // Got buffered read data and there's someone ready to receive it, // so schedule a call to ssl_read_callback to handle it. got_extra_read_call_out = -1; // If got_extra_read_call_out is set here then we wait with all // other callbacks so that the extra ssl_read_callback call is // carried out before anything else. if (install_read_cbs) { if (got_extra_read_call_out < 0) { real_backend->call_out (ssl_read_callback, 0, 1, 0); got_extra_read_call_out = 1; } else { stream->set_read_callback (ssl_read_callback); stream->set_close_callback (ssl_close_callback); } } else { stream->set_read_callback (0); // Installing a close callback without a read callback // currently doesn't work well in Stdio.File. stream->set_close_callback (0); if (got_extra_read_call_out > 0) { real_backend->remove_call_out (ssl_read_callback); got_extra_read_call_out = -1; } } stream->set_write_callback (install_write_cb && !got_extra_read_call_out && ssl_write_callback); #ifdef DEBUG if (!assume_real_backend && op_thread) // Check that we haven't installed callbacks that might start // executing in parallell in another thread. That's legitimate // in some cases (e.g. set_nonblocking) but in those cases we're // called after RESTORE or LEAVE, which has reset op_thread, so // we skip this check if op_thread is zero. CHECK_CB_MODE (THIS_THREAD()); #endif } else SSL3_DEBUG_MORE_MSG ("update_internal_state: " "In local backend - nothing done [rcb:%O]\n", got_extra_read_call_out); } protected int queue_write() // Return 0 if the connection is still alive, 1 if it was closed // politely, and -1 if it died unexpectedly (specifically, our side // has sent a fatal alert packet (not close notify) for some reason // and therefore nullified the connection). { int|string res = conn->to_write(); #ifdef SSL3_DEBUG_TRANSPORT werror ("queue_write: To write: %O\n", res); #endif if (stringp (res)) { if (res == "") SSL3_DEBUG_MSG ("queue_write: Got nothing to write (%d strings buffered)\n", sizeof (write_buffer)); else { int was_empty = !sizeof (write_buffer); write_buffer += ({res}); SSL3_DEBUG_MSG ("queue_write: Got %d bytes to write (%d strings buffered)\n", sizeof (res), sizeof (write_buffer)); if (was_empty && stream) update_internal_state(); } return 0; } SSL3_DEBUG_MSG ("queue_write: Connection closed %s\n", res == 1 ? "normally" : "abruptly"); return res; } protected int direct_write() // Do a write directly (and maybe also read if there's internal // reading to be done). Something to write is assumed to exist (either // in write_buffer or in the packet queue). Returns zero on error (as // opposed to queue_write). { if (!stream) { SSL3_DEBUG_MSG ("direct_write: " "Connection already closed - simulating System.EPIPE\n"); // If it was closed explicitly locally then close_state would be // set and we'd never get here, so we can report it as a remote // close. cleanup_on_error(); local_errno = System.EPIPE; return 0; } else { if (queue_write() < 0 || close_state == ABRUPT_CLOSE) { SSL3_DEBUG_MSG ("direct_write: " "Connection closed abruptly - simulating System.EPIPE\n"); cleanup_on_error(); local_errno = System.EPIPE; // Don't set close_state = ABRUPT_CLOSE here. It's either set // already, or there's an abrupt close due to an ALERT_fatal, // which we shouldn't mix up with ABRUPT_CLOSE. return 0; } if (SSL_INTERNAL_WRITING || SSL_INTERNAL_READING) RUN_MAYBE_BLOCKING (SSL_INTERNAL_WRITING || SSL_INTERNAL_READING, nonblocking_mode, SSL_INTERNAL_READING, SSL3_DEBUG_MORE_MSG ("direct_write: Got error\n"); return 0;); } SSL3_DEBUG_MORE_MSG ("direct_write: Ok\n"); return 1; } protected int ssl_read_callback (int called_from_real_backend, string input) { SSL3_DEBUG_MSG ("ssl_read_callback (%O, %s): " "nonblocking mode=%d, callback mode=%d%s%s\n", called_from_real_backend, input ? "string[" + sizeof (input) + "]" : "0 (queued extra call)", nonblocking_mode, !!(CALLBACK_MODE), conn && SSL_HANDSHAKING ? ", handshaking" : "", conn && SSL_CLOSING_OR_CLOSED ? ", closing (" + conn->closing + ", " + close_state + ", " + close_packet_send_state + ")" : ""); ENTER (1, called_from_real_backend) { int call_accept_cb; if (input) { int handshake_already_finished = conn->handshake_finished; string|int data = close_state == ABRUPT_CLOSE ? -1 : conn->got_data (input); #ifdef DEBUG if (got_extra_read_call_out) error ("Got to real read callback with queued extra read call out.\n"); #endif #ifdef SSL3_DEBUG_TRANSPORT werror ("ssl_read_callback: Got data: %O\n", data); #endif if (alert_cb_called) SSL3_DEBUG_MSG ("ssl_read_callback: After alert cb call\n"); else { int write_res; if (stringp(data) || (data > 0) || ((data < 0) && !conn->handshake_finished)) { #ifdef DEBUG if (!stream) error ("Got zapped stream in callback.\n"); #endif // got_data might have put more packets in the write queue if // we're handshaking. write_res = queue_write(); } cb_errno = 0; if (stringp (data)) { if (!handshake_already_finished && conn->handshake_finished) { SSL3_DEBUG_MSG ("ssl_read_callback: Handshake finished\n"); update_internal_state(); if (called_from_real_backend && accept_callback) { #ifdef DEBUG if (close_state >= NORMAL_CLOSE) error ("Didn't expect the connection to be " "explicitly closed already.\n"); #endif call_accept_cb = 1; } } SSL3_DEBUG_MSG ("ssl_read_callback: " "Got %d bytes of application data\n", sizeof (data)); read_buffer->add (data); } else if (data < 0 || write_res < 0) { SSL3_DEBUG_MSG ("ssl_read_callback: " "Got abrupt remote close - simulating System.EPIPE\n"); cleanup_on_error(); cb_errno = System.EPIPE; } // Don't use data > 0 here since we might have processed some // application data and a close in the same got_data call. if (conn->closing & 2) { if (close_packet_send_state == CLOSE_PACKET_MAYBE_IGNORED_WRITE_ERROR) { SSL3_DEBUG_MSG ("ssl_read_callback: Got close packet - " "ignoring failure to send one\n"); close_packet_send_state = CLOSE_PACKET_QUEUED_OR_DONE; } else SSL3_DEBUG_MSG ("ssl_read_callback: Got close packet\n"); } } } else { // This is another call that has been queued below through a // call out. That is necessary whenever we need to call several // user callbacks from the same invocation: We can't do anything // after calling a user callback, since the user is free to e.g. // remove the callbacks and "hand over" us to another thread and // do more I/O there while this one returns. We solve this // problem by queuing a call out to ourselves (with zero as // input) to continue. got_extra_read_call_out = 0; update_internal_state(); } // Figure out what we need to do. call_accept_cb is already set // from above. int(0..1) call_read_cb; int(0..1) do_close_stuff; if (alert_cb_called) { if (!conn) { SSL3_DEBUG_MSG ("ssl_read_callback: Shut down from alert callback\n"); RESTORE; return 0; } // Make sure the alert is queued for writing. queue_write(); } else { call_read_cb = called_from_real_backend && read_callback && sizeof (read_buffer) && // Shouldn't get here when close_state == ABRUPT_CLOSE. close_state < NORMAL_CLOSE; do_close_stuff = !!((conn->closing & 2) || cb_errno); } if (alert_cb_called || call_accept_cb + call_read_cb + do_close_stuff > 1) { // Need to do a call out to ourselves; see comment above. #ifdef DEBUG if (!alert_cb_called && !called_from_real_backend) error ("Internal confusion.\n"); if (called_from_real_backend && got_extra_read_call_out < 0) error ("Ended up in ssl_read_callback from real backend " "when no callbacks are supposed to be installed.\n"); #endif if (!got_extra_read_call_out) { got_extra_read_call_out = -1; SSL3_DEBUG_MSG ("ssl_read_callback: Too much to do (%O, %O, %O, %O) - " "scheduled another call\n", alert_cb_called, call_accept_cb, call_read_cb, do_close_stuff); update_internal_state(); } else SSL3_DEBUG_MSG ("ssl_read_callback: Too much to do (%O, %O, %O, %O) - " "another call already queued\n", alert_cb_called, call_accept_cb, call_read_cb, do_close_stuff); alert_cb_called = 0; } else if (got_extra_read_call_out) { // Don't know if this actually can happen, but it's symmetric. if (got_extra_read_call_out > 0) real_backend->remove_call_out (ssl_read_callback); got_extra_read_call_out = 0; update_internal_state(); } // Now actually do (a bit of) what should be done. if (call_accept_cb) { SSL3_DEBUG_MSG ("ssl_read_callback: Calling accept callback %O\n", accept_callback); RESTORE; return accept_callback (this, callback_id); } else if (call_read_cb) { string received = read_buffer->get(); SSL3_DEBUG_MSG ("call_read_callback: Calling read callback %O " "with %d bytes\n", read_callback, sizeof (received)); // Never called if there's an error - no need to propagate cb_errno. RESTORE; return read_callback (callback_id, received); } else if (do_close_stuff) { #ifdef DEBUG if (got_extra_read_call_out) error ("Shouldn't have more to do after close stuff.\n"); #endif if (conn->closing & 2 && close_packet_send_state == CLOSE_PACKET_NOT_SCHEDULED) { close_packet_send_state = CLOSE_PACKET_SCHEDULED; // Deinstall read side cbs to avoid reading more and install // the write cb to send the close packet. Can't use // update_internal_state here since we should force a // deinstall of the read side cbs even when we're still in // callback mode, to correctly imitate the behavior in // Stdio.File (it deinstalls read side cbs whenever the close // cb is called, but it's possible to reinstall the close cb // later and get another call to it). if (stream->query_backend() != local_backend) { stream->set_read_callback (0); stream->set_close_callback (0); stream->set_write_callback (ssl_write_callback); SSL3_DEBUG_MORE_MSG ("ssl_read_callback: Setting cbs for close [r:0 w:1]\n"); } } if (called_from_real_backend && close_callback) { // errno() should return the error in the close callback - need to // propagate it here. FIX_ERRNOS ( SSL3_DEBUG_MSG ("ssl_read_callback: Calling close callback %O (error %s)\n", close_callback, strerror (local_errno)), SSL3_DEBUG_MSG ("ssl_read_callback: Calling close callback %O (read eof)\n", close_callback) ); RESTORE; return close_callback (callback_id); } if (close_state >= NORMAL_CLOSE) { SSL3_DEBUG_MSG ("ssl_read_callback: " "In or after local close - shutting down\n"); shutdown(); } if (cb_errno) { SSL3_DEBUG_MSG ("ssl_read_callback: Returning with error\n"); // Make sure the local backend exits after this, so that the // error isn't clobbered by later I/O. RESTORE; return -1; } } } LEAVE; return 0; } protected int ssl_write_callback (int called_from_real_backend) { SSL3_DEBUG_MSG ("ssl_write_callback (%O): " "nonblocking mode=%d, callback mode=%d%s%s\n", called_from_real_backend, nonblocking_mode, !!(CALLBACK_MODE), conn && SSL_HANDSHAKING ? ", handshaking" : "", conn && SSL_CLOSING_OR_CLOSED ? ", closing (" + conn->closing + ", " + close_state + ", " + close_packet_send_state + ")" : ""); int ret = 0; ENTER (1, called_from_real_backend) { #ifdef DEBUG if (!stream) error ("Got zapped stream in callback.\n"); if (got_extra_read_call_out) error ("Got to write callback with queued extra read call out.\n"); #endif write_to_stream: do { if (sizeof (write_buffer)) { string output = write_buffer[0]; int written; #ifdef SIMULATE_CLOSE_PACKET_WRITE_FAILURE if (close_packet_send_state == CLOSE_PACKET_QUEUED_OR_DONE) written = -1; else #endif written = stream->write (output); if (written < 0 #if 0 #ifdef __NT__ // You don't want to know.. (Bug observed in Pike 0.6.132.) && stream->errno() != 1 #endif #endif ) { #ifdef SIMULATE_CLOSE_PACKET_WRITE_FAILURE if (close_packet_send_state == CLOSE_PACKET_QUEUED_OR_DONE) cb_errno = System.EPIPE; else #endif cb_errno = stream->errno(); cleanup_on_error(); SSL3_DEBUG_MSG ("ssl_write_callback: Write failed: %s\n", strerror (cb_errno)); // Make sure the local backend exits after this, so that the // error isn't clobbered by later I/O. ret = -1; if (close_packet_send_state == CLOSE_PACKET_QUEUED_OR_DONE && epipe_errnos[cb_errno]) { // See if it's an error from writing a close packet that // should be ignored. write_buffer = ({}); // No use trying to write the close again. if (close_state == CLEAN_CLOSE) { // Never accept a failure if a clean close is requested. close_packet_send_state = CLOSE_PACKET_WRITE_ERROR; update_internal_state(); } else { cb_errno = 0; if (conn->closing & 2) SSL3_DEBUG_MSG ("ssl_write_callback: Stream closed properly " "remotely - ignoring failure to send close packet\n"); else { SSL3_DEBUG_MSG ("ssl_write_callback: Stream closed remotely - " "checking input buffer for proper remote close\n"); // This causes the read/close callbacks to be // installed to handle the close packet that might be // sitting in the input buffer. close_packet_send_state = CLOSE_PACKET_MAYBE_IGNORED_WRITE_ERROR; update_internal_state(); if (called_from_real_backend) { // Shouldn't wait for a close packet that might // arrive later on, so we start a nonblocking local // backend to check for it. If we're already in a // local backend, this is handled by special cases // in RUN_MAYBE_BLOCKING. RUN_MAYBE_BLOCKING ( close_packet_send_state == CLOSE_PACKET_MAYBE_IGNORED_WRITE_ERROR, 1, 1, {}); } else { // Can't start a nested local backend - skip out to // the one we're called from. RESTORE; return ret; } } } } // Still try to call the write (or close) callback to // propagate the error to it. break write_to_stream; } if (written == sizeof (output)) write_buffer = write_buffer[1..]; else write_buffer[0] = output[written..]; SSL3_DEBUG_MSG ("ssl_write_callback: Wrote %d bytes (%d strings left)\n", written, sizeof (write_buffer)); if (sizeof (write_buffer)) { RESTORE; return ret; } update_internal_state(); } else // Ensure that the close is sent separately in case we should // ignore the error from it. if (close_packet_send_state == CLOSE_PACKET_SCHEDULED) { if (linger_time) { SSL3_DEBUG_MSG ("ssl_write_callback: Queuing close packet\n"); conn->send_close(); } else { SSL3_DEBUG_MSG ("ssl_write_callback: Skipping close packet\n"); } close_packet_send_state = CLOSE_PACKET_QUEUED_OR_DONE; } if (int err = queue_write()) { if (err > 0) { #ifdef DEBUG if (!conn->closing || close_packet_send_state < CLOSE_PACKET_QUEUED_OR_DONE) error ("Expected a close to be sent or received\n"); #endif if (sizeof (write_buffer)) SSL3_DEBUG_MSG ("ssl_write_callback: " "Close packet queued but not yet sent\n"); else { #ifdef DEBUG if (!(conn->closing & 1)) error ("Expected a close packet to be queued or sent.\n"); #endif if (conn->closing == 3 || close_state != CLEAN_CLOSE) { SSL3_DEBUG_MSG ("ssl_write_callback: %s\n", conn->closing == 3 ? "Close packets exchanged" : "Close packet sent"); break write_to_stream; } else { SSL3_DEBUG_MSG ("ssl_write_callback: " "Close packet sent - expecting response\n"); // Not SSL_INTERNAL_WRITING anymore. update_internal_state(); } } RESTORE; return ret; } else { SSL3_DEBUG_MSG ("ssl_write_callback: " "Connection closed abruptly remotely - " "simulating System.EPIPE\n"); cleanup_on_error(); cb_errno = System.EPIPE; ret = -1; break write_to_stream; } } } while (sizeof (write_buffer)); if (called_from_real_backend) { if (close_packet_send_state >= CLOSE_PACKET_SCHEDULED) { if (close_callback && cb_errno && close_state < NORMAL_CLOSE) { // Better signal errors writing the close packet to the // close callback. FIX_ERRNOS ( SSL3_DEBUG_MSG ("ssl_write_callback: Calling close callback %O " "(error %s)\n", close_callback, strerror (local_errno)), 0 ); RESTORE; return close_callback (callback_id); } } else { if (write_callback && !sizeof (write_buffer) && !SSL_HANDSHAKING && (close_state < NORMAL_CLOSE || cb_errno)) { // errno() should return the error in the write callback - need // to propagate it here. FIX_ERRNOS ( SSL3_DEBUG_MSG ("ssl_write_callback: Calling write callback %O " "(error %s)\n", write_callback, strerror (local_errno)), SSL3_DEBUG_MSG ("ssl_write_callback: Calling write callback %O\n", write_callback) ); RESTORE; return write_callback (callback_id); } } } if (close_state >= NORMAL_CLOSE && (close_packet_send_state >= CLOSE_PACKET_QUEUED_OR_DONE || cb_errno)) { #ifdef DEBUG if (close_packet_send_state == CLOSE_PACKET_MAYBE_IGNORED_WRITE_ERROR) error ("Unexpected close_packet_send_state\n"); #endif SSL3_DEBUG_MSG ("ssl_write_callback: " "In or after local close - shutting down\n"); shutdown(); } } LEAVE; return ret; } protected int ssl_close_callback (int called_from_real_backend) { SSL3_DEBUG_MSG ("ssl_close_callback (%O): " "nonblocking mode=%d, callback mode=%d%s%s\n", called_from_real_backend, nonblocking_mode, !!(CALLBACK_MODE), conn && SSL_HANDSHAKING ? ", handshaking" : "", conn && SSL_CLOSING_OR_CLOSED ? ", closing (" + conn->closing + ", " + close_state + ", " + close_packet_send_state + ")" : ""); ENTER (1, called_from_real_backend) { #ifdef DEBUG if (!stream) error ("Got zapped stream in callback.\n"); if (got_extra_read_call_out) error ("Got to close callback with queued extra read call out.\n"); #endif // If we've arrived here due to an error, let it override any // older errno from an earlier callback. if (int new_errno = stream->errno()) { SSL3_DEBUG_MSG ("ssl_close_callback: Got error %s\n", strerror (new_errno)); cleanup_on_error(); cb_errno = new_errno; } #ifdef SSL3_DEBUG else if (cb_errno) SSL3_DEBUG_MSG ("ssl_close_callback: Propagating errno from another callback\n"); #endif if (!cb_errno) { if (conn->closing & 2) SSL3_DEBUG_MSG ("ssl_close_callback: After clean close\n"); else { // The remote end has closed the connection without sending a // close packet. Treat that as an error so that the caller can // detect truncation attacks. if (close_packet_send_state == CLOSE_PACKET_MAYBE_IGNORED_WRITE_ERROR) { SSL3_DEBUG_MSG ("ssl_close_callback: Did not get a remote close - " "signalling delayed error from writing close message\n"); close_packet_send_state = CLOSE_PACKET_WRITE_ERROR; } else SSL3_DEBUG_MSG ("ssl_close_callback: Abrupt close - " "simulating System.EPIPE\n"); cleanup_on_error(); cb_errno = System.EPIPE; close_state = ABRUPT_CLOSE; } } if (called_from_real_backend && close_callback) { // errno() should return the error in the close callback - need to // propagate it here. FIX_ERRNOS ( SSL3_DEBUG_MSG ("ssl_close_callback: Calling close callback %O (error %s)\n", close_callback, strerror (local_errno)), SSL3_DEBUG_MSG ("ssl_close_callback: Calling close callback %O (read eof)\n", close_callback) ); RESTORE; // Note that the callback should call close() (or free things // so that we get destructed) - there's no need for us to // schedule a shutdown after it. return close_callback (callback_id); } if (close_state >= NORMAL_CLOSE) { SSL3_DEBUG_MSG ("ssl_close_callback: " "In or after local close - shutting down\n"); shutdown(); } } LEAVE; // Make sure the local backend exits after this, so that the error // isn't clobbered by later I/O. return -1; } //! The next protocol chosen by the client during next protocol //! negotiation. string `->next_protocol() { return conn->next_protocol; } #else // constant(SSL.Cipher.CipherAlgorithm) constant this_program_does_not_exist = 1; #endif