+ {
+ enum {
+ error_proto, /* Wrong protocol, not even HTTP */
+ error_connect, /* CONNECT failed */
+ success
+ } foundit = error_connect;
+ BIO *fbio = BIO_new(BIO_f_buffer());
+
+ BIO_push(fbio, sbio);
+ BIO_printf(fbio, "CONNECT %s HTTP/1.0\r\n\r\n", connectstr);
+ (void)BIO_flush(fbio);
+ /*
+ * The first line is the HTTP response. According to RFC 7230,
+ * it's formated exactly like this:
+ *
+ * HTTP/d.d ddd Reason text\r\n
+ */
+ mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
+ if (mbuf_len < (int)strlen("HTTP/1.0 200")) {
+ BIO_printf(bio_err,
+ "%s: HTTP CONNECT failed, insufficient response "
+ "from proxy (got %d octets)\n", prog, mbuf_len);
+ (void)BIO_flush(fbio);
+ BIO_pop(fbio);
+ BIO_free(fbio);
+ goto shut;
+ }
+ if (mbuf[8] != ' ') {
+ BIO_printf(bio_err,
+ "%s: HTTP CONNECT failed, incorrect response "
+ "from proxy\n", prog);
+ foundit = error_proto;
+ } else if (mbuf[9] != '2') {
+ BIO_printf(bio_err, "%s: HTTP CONNECT failed: %s ", prog,
+ &mbuf[9]);
+ } else {
+ foundit = success;
+ }
+ if (foundit != error_proto) {
+ /* Read past all following headers */
+ do {
+ mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
+ } while (mbuf_len > 2);
+ }
+ (void)BIO_flush(fbio);
+ BIO_pop(fbio);
+ BIO_free(fbio);
+ if (foundit != success) {
+ goto shut;
+ }
+ }
+ break;
+ case PROTO_IRC:
+ {
+ int numeric;
+ BIO *fbio = BIO_new(BIO_f_buffer());
+
+ BIO_push(fbio, sbio);
+ BIO_printf(fbio, "STARTTLS\r\n");
+ (void)BIO_flush(fbio);
+ width = SSL_get_fd(con) + 1;
+
+ do {
+ numeric = 0;
+
+ FD_ZERO(&readfds);
+ openssl_fdset(SSL_get_fd(con), &readfds);
+ timeout.tv_sec = S_CLIENT_IRC_READ_TIMEOUT;
+ timeout.tv_usec = 0;
+ /*
+ * If the IRCd doesn't respond within
+ * S_CLIENT_IRC_READ_TIMEOUT seconds, assume
+ * it doesn't support STARTTLS. Many IRCds
+ * will not give _any_ sort of response to a
+ * STARTTLS command when it's not supported.
+ */
+ if (!BIO_get_buffer_num_lines(fbio)
+ && !BIO_pending(fbio)
+ && !BIO_pending(sbio)
+ && select(width, (void *)&readfds, NULL, NULL,
+ &timeout) < 1) {
+ BIO_printf(bio_err,
+ "Timeout waiting for response (%d seconds).\n",
+ S_CLIENT_IRC_READ_TIMEOUT);
+ break;
+ }
+
+ mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ);
+ if (mbuf_len < 1 || sscanf(mbuf, "%*s %d", &numeric) != 1)
+ break;
+ /* :example.net 451 STARTTLS :You have not registered */
+ /* :example.net 421 STARTTLS :Unknown command */
+ if ((numeric == 451 || numeric == 421)
+ && strstr(mbuf, "STARTTLS") != NULL) {
+ BIO_printf(bio_err, "STARTTLS not supported: %s", mbuf);
+ break;
+ }
+ if (numeric == 691) {
+ BIO_printf(bio_err, "STARTTLS negotiation failed: ");
+ ERR_print_errors(bio_err);
+ break;
+ }
+ } while (numeric != 670);
+
+ (void)BIO_flush(fbio);
+ BIO_pop(fbio);
+ BIO_free(fbio);
+ if (numeric != 670) {
+ BIO_printf(bio_err, "Server does not support STARTTLS.\n");
+ ret = 1;
+ goto shut;
+ }
+ }
+ break;
+ case PROTO_MYSQL:
+ {
+ /* SSL request packet */
+ static const unsigned char ssl_req[] = {
+ /* payload_length, sequence_id */
+ 0x20, 0x00, 0x00, 0x01,
+ /* payload */
+ /* capability flags, CLIENT_SSL always set */
+ 0x85, 0xae, 0x7f, 0x00,
+ /* max-packet size */
+ 0x00, 0x00, 0x00, 0x01,
+ /* character set */
+ 0x21,
+ /* string[23] reserved (all [0]) */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ int bytes = 0;
+ int ssl_flg = 0x800;
+ int pos;
+ const unsigned char *packet = (const unsigned char *)sbuf;
+
+ /* Receiving Initial Handshake packet. */
+ bytes = BIO_read(sbio, (void *)packet, BUFSIZZ);
+ if (bytes < 0) {
+ BIO_printf(bio_err, "BIO_read failed\n");
+ goto shut;
+ /* Packet length[3], Packet number[1] + minimum payload[17] */
+ } else if (bytes < 21) {
+ BIO_printf(bio_err, "MySQL packet too short.\n");
+ goto shut;
+ } else if (bytes != (4 + packet[0] +
+ (packet[1] << 8) +
+ (packet[2] << 16))) {
+ BIO_printf(bio_err, "MySQL packet length does not match.\n");
+ goto shut;
+ /* protocol version[1] */
+ } else if (packet[4] != 0xA) {
+ BIO_printf(bio_err,
+ "Only MySQL protocol version 10 is supported.\n");
+ goto shut;
+ }
+
+ pos = 5;
+ /* server version[string+NULL] */
+ for (;;) {
+ if (pos >= bytes) {
+ BIO_printf(bio_err, "Cannot confirm server version. ");
+ goto shut;
+ } else if (packet[pos++] == '\0') {
+ break;
+ }
+ pos++;
+ }
+
+ /* make sure we have more 15 bytes left in the packet */
+ if (pos + 15 > bytes) {
+ BIO_printf(bio_err,
+ "MySQL server handshake packet is broken.\n");
+ goto shut;
+ }
+
+ pos += 12; /* skip over conn id[4] + SALT[8] */
+ if (packet[pos++] != '\0') { /* verify filler */
+ BIO_printf(bio_err,
+ "MySQL packet is broken.\n");
+ goto shut;
+ }
+
+ /* capability flags[2] */
+ if (!((packet[pos] + (packet[pos + 1] << 8)) & ssl_flg)) {
+ BIO_printf(bio_err, "MySQL server does not support SSL.\n");
+ goto shut;
+ }
+
+ /* Sending SSL Handshake packet. */
+ BIO_write(sbio, ssl_req, sizeof(ssl_req));
+ (void)BIO_flush(sbio);
+ }
+ break;
+ case PROTO_POSTGRES:
+ {
+ static const unsigned char ssl_request[] = {
+ /* Length SSLRequest */
+ 0, 0, 0, 8, 4, 210, 22, 47
+ };
+ int bytes;
+
+ /* Send SSLRequest packet */
+ BIO_write(sbio, ssl_request, 8);
+ (void)BIO_flush(sbio);
+
+ /* Reply will be a single S if SSL is enabled */
+ bytes = BIO_read(sbio, sbuf, BUFSIZZ);
+ if (bytes != 1 || sbuf[0] != 'S')
+ goto shut;
+ }
+ break;
+ case PROTO_NNTP: