+ *errbuf = 0; // Initialize errbuf
+
+ //
+ // Peek into the socket to determine whether the client sent us
+ // a TLS handshake message or a non-TLS rpcapd message.
+ //
+ // The first byte of an rpcapd request is the version number;
+ // the first byte of a TLS handshake message is 22. The
+ // first request to an rpcapd server must be an authentication
+ // request or a close request, and must have a version number
+ // of 0, so it will be possible to distinguish between an
+ // initial plaintext request to a server and an initial TLS
+ // handshake message.
+ //
+ nrecv = sock_recv(sockctrl, NULL, (char *)&first_octet, 1,
+ SOCK_EOF_ISNT_ERROR|SOCK_MSG_PEEK, errbuf, PCAP_ERRBUF_SIZE);
+ if (nrecv == -1)
+ {
+ // Fatal error.
+ rpcapd_log(LOGPRIO_ERROR, "Peek from client failed: %s", errbuf);
+ goto end;
+ }
+ if (nrecv == 0)
+ {
+ // Client closed the connection.
+ goto end;
+ }
+
+#ifdef HAVE_OPENSSL
+ //
+ // We have to upgrade to TLS as soon as possible, so that the
+ // whole protocol goes through the encrypted tunnel, including
+ // early error messages.
+ //
+ // Even in active mode, the other end has to initiate the TLS
+ // handshake as we still are the server as far as TLS is concerned,
+ // so we don't check isactive.
+ //
+ if (uses_ssl)
+ {
+ //
+ // We're expecting a TLS handshake message. If this
+ // isn't one, assume it's a non-TLS rpcapd message.
+ //
+ // The first octet of a TLS handshake is
+ // TLS_RECORD_TYPE_HANDSHAKE.
+ //
+ if (first_octet != TLS_RECORD_TYPE_HANDSHAKE)
+ {
+ //
+ // We assume this is a non-TLS rpcapd message.
+ //
+ // Read the message header from the client.
+ //
+ nrecv = rpcapd_recv_msg_header(sockctrl, NULL, &header);
+ if (nrecv == -1)
+ {
+ // Fatal error.
+ goto end;
+ }
+ if (nrecv == -2)
+ {
+ // Client closed the connection.
+ goto end;
+ }
+ plen = header.plen;
+
+ // Discard the rest of the message.
+ if (rpcapd_discard(sockctrl, NULL, plen) == -1)
+ {
+ // Network error.
+ goto end;
+ }
+
+ //
+ // Send an authentication error, indicating
+ // that we require TLS.
+ //
+ if (rpcap_senderror(sockctrl, NULL, header.ver,
+ PCAP_ERR_TLS_REQUIRED,
+ "TLS is required by this server", errbuf) == -1)
+ {
+ // That failed; log a message and give up.
+ rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
+ goto end;
+ }
+
+ // Shut the session down.
+ goto end;
+ }
+ ssl = ssl_promotion(1, sockctrl, errbuf, PCAP_ERRBUF_SIZE);
+ if (! ssl)
+ {
+ rpcapd_log(LOGPRIO_ERROR, "TLS handshake on control connection failed: %s",
+ errbuf);
+ goto end;
+ }
+ }
+ else
+#endif
+ {
+ //
+ // We're expecting a non-TLS rpcapd message. If this
+ // looks, instead, like a TLS handshake message, send
+ // a TLS handshake_failed alert.
+ //
+ // The first octet of a TLS handshake is
+ // TLS_RECORD_TYPE_HANDSHAKE.
+ //
+ if (first_octet == TLS_RECORD_TYPE_HANDSHAKE)
+ {
+ //
+ // TLS handshake.
+ // Read the record header.
+ //
+ nrecv = sock_recv(sockctrl, ssl, (char *) &tls_header,
+ sizeof tls_header, SOCK_RECEIVEALL_YES|SOCK_EOF_ISNT_ERROR,
+ errbuf, PCAP_ERRBUF_SIZE);
+ if (nrecv == -1)
+ {
+ // Network error.
+ rpcapd_log(LOGPRIO_ERROR, "Read from client failed: %s", errbuf);
+ goto end;
+ }
+ if (nrecv == 0)
+ {
+ // Immediate EOF
+ goto end;
+ }
+ plen = (tls_header.length_hi << 8U) | tls_header.length_lo;
+
+ // Discard the rest of the message.
+ if (rpcapd_discard(sockctrl, NULL, plen) == -1)
+ {
+ // Network error.
+ goto end;
+ }
+
+ //
+ // Send a TLS handshake failure alert.
+ // Use the same version the client sent us.
+ //
+ tls_header.type = TLS_RECORD_TYPE_ALERT;
+ tls_header.length_hi = 0;
+ tls_header.length_lo = TLS_ALERT_LEN;
+
+ if (sock_send(sockctrl, NULL, (char *) &tls_header,
+ TLS_RECORD_HEADER_LEN, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ {
+ // That failed; log a message and give up.
+ rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
+ goto end;
+ }
+
+ tls_alert.alert_level = TLS_ALERT_LEVEL_FATAL;
+ tls_alert.alert_description = TLS_ALERT_HANDSHAKE_FAILURE;
+ if (sock_send(sockctrl, NULL, (char *) &tls_alert,
+ TLS_ALERT_LEN, errbuf, PCAP_ERRBUF_SIZE) == -1)
+ {
+ // That failed; log a message and give up.
+ rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
+ goto end;
+ }
+ //
+ // Give up anyway.
+ //
+ goto end;
+ }
+ }
+