/* * yosys -- Yosys Open SYnthesis Suite * * Copyright (C) 2012 Clifford Wolf * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include "libparse.h" #include #include #include #include #include #ifndef FILTERLIB #include "kernel/log.h" #endif using namespace Yosys; std::set LibertyAst::blacklist; std::set LibertyAst::whitelist; LibertyAst::~LibertyAst() { for (auto child : children) delete child; children.clear(); } LibertyAst *LibertyAst::find(std::string name) { if (this == NULL) return NULL; for (auto child : children) if (child->id == name) return child; return NULL; } void LibertyAst::dump(FILE *f, std::string indent, std::string path, bool path_ok) { if (whitelist.count(path + "/*") > 0) path_ok = true; path += "/" + id; if (blacklist.count(id) > 0 || blacklist.count(path) > 0) return; if (whitelist.size() > 0 && whitelist.count(id) == 0 && whitelist.count(path) == 0 && !path_ok) { fprintf(stderr, "Automatically added to blacklist: %s\n", path.c_str()); blacklist.insert(id); return; } fprintf(f, "%s%s", indent.c_str(), id.c_str()); if (!args.empty() || !children.empty()) { fprintf(f, "("); for (size_t i = 0; i < args.size(); i++) fprintf(f, "%s%s", i > 0 ? ", " : "", args[i].c_str()); fprintf(f, ")"); } if (!value.empty()) fprintf(f, " : %s", value.c_str()); if (!children.empty()) { fprintf(f, " {\n"); for (size_t i = 0; i < children.size(); i++) children[i]->dump(f, indent + " ", path, path_ok); fprintf(f, "%s}\n", indent.c_str()); } else fprintf(f, " ;\n"); } int LibertyParser::lexer(std::string &str) { int c; do { c = f.get(); } while (c == ' ' || c == '\t' || c == '\r'); if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') { str = c; while (1) { c = f.get(); if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_' || c == '-' || c == '+' || c == '.') str += c; else break; } f.unget(); // fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str()); return 'v'; } if (c == '"') { str = c; while (1) { c = f.get(); if (c == '\n') line++; str += c; if (c == '"') break; } // fprintf(stderr, "LEX: string >>%s<<\n", str.c_str()); return 'v'; } if (c == '/') { c = f.get(); if (c == '*') { int last_c = 0; while (c > 0 && (last_c != '*' || c != '/')) { last_c = c; c = f.get(); if (c == '\n') line++; } return lexer(str); } else if (c == '/') { while (c > 0 && c != '\n') c = f.get(); line++; return lexer(str); } f.unget(); // fprintf(stderr, "LEX: char >>/<<\n"); return '/'; } if (c == '\\') { c = f.get(); if (c == '\r') c = f.get(); if (c == '\n') return lexer(str); f.unget(); return '\\'; } if (c == '\n') { line++; return ';'; } // if (c >= 32 && c < 255) // fprintf(stderr, "LEX: char >>%c<<\n", c); // else // fprintf(stderr, "LEX: char %d\n", c); return c; } LibertyAst *LibertyParser::parse() { std::string str; int tok = lexer(str); while (tok == ';') tok = lexer(str); if (tok == '}' || tok < 0) return NULL; if (tok != 'v') error(); LibertyAst *ast = new LibertyAst; ast->id = str; while (1) { tok = lexer(str); if (tok == ';') break; if (tok == ':' && ast->value.empty()) { tok = lexer(ast->value); if (tok != 'v') error(); continue; } if (tok == '(') { while (1) { std::string arg; tok = lexer(arg); if (tok == ',') continue; if (tok == ')') break; if (tok != 'v') error(); ast->args.push_back(arg); } continue; } if (tok == '{') { while (1) { LibertyAst *child = parse(); if (child == NULL) break; ast->children.push_back(child); } break; } error(); } return ast; } #ifndef FILTERLIB void LibertyParser::error() { log_error("Syntax error in line %d.\n", line); } #else void LibertyParser::error() { fprintf(stderr, "Syntax error in line %d.\n", line); exit(1); } /**** BEGIN: http://svn.clifford.at/tools/trunk/examples/check.h ****/ // This is to not confuse the VIM syntax highlighting #define CHECK_VAL_OPEN ( #define CHECK_VAL_CLOSE ) #define CHECK(result, check) \ CHECK_VAL_OPEN{ \ auto _R = (result); \ if (!(_R check)) { \ fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \ #result, (long int)_R, #check, __FILE__, __LINE__); \ abort(); \ } \ _R; \ }CHECK_VAL_CLOSE #define CHECK_NV(result, check) \ do { \ auto _R = (result); \ if (!(_R check)) { \ fprintf(stderr, "Error from '%s' (%ld %s) in %s:%d.\n", \ #result, (long int)_R, #check, __FILE__, __LINE__); \ abort(); \ } \ } while(0) #define CHECK_COND(result) \ do { \ if (!(result)) { \ fprintf(stderr, "Error from '%s' in %s:%d.\n", \ #result, __FILE__, __LINE__); \ abort(); \ } \ } while(0) /**** END: http://svn.clifford.at/tools/trunk/examples/check.h ****/ std::string func2vl(std::string str) { for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) { char c_left = pos > 0 ? str[pos-1] : ' '; char c_right = pos+1 < str.size() ? str[pos+1] : ' '; if (std::string("\" \t*+").find(c_left) != std::string::npos) str.erase(pos, 1); else if (std::string("\" \t*+").find(c_right) != std::string::npos) str.erase(pos, 1); else str[pos] = '*'; } std::vector group_start; for (size_t pos = 0; pos < str.size(); pos++) { if (str[pos] == '(') group_start.push_back(pos); if (str[pos] == ')' && group_start.size() > 0) { if (pos+1 < str.size() && str[pos+1] == '\'') { std::string group = str.substr(group_start.back(), pos-group_start.back()+1); str[group_start.back()] = '~'; str.replace(group_start.back()+1, group.size(), group); pos++; } group_start.pop_back(); } if (str[pos] == '\'' && pos > 0) { size_t start = str.find_last_of("()'*+^&| ", pos-1)+1; std::string group = str.substr(start, pos-start); str[start] = '~'; str.replace(start+1, group.size(), group); } if (str[pos] == '*') str[pos] = '&'; if (str[pos] == '+') str[pos] = '|'; } return str; } void event2vl(LibertyAst *ast, std::string &edge, std::string &expr) { edge.clear(); expr.clear(); if (ast != NULL) { expr = func2vl(ast->value); if (expr.size() > 0 && expr[0] == '~') edge = "negedge " + expr.substr(1); else edge = "posedge " + expr; } } void clear_preset_var(std::string var, std::string type) { if (type.find('L') != std::string::npos) { printf(" %s <= 0;\n", var.c_str()); return; } if (type.find('H') != std::string::npos) { printf(" %s <= 1;\n", var.c_str()); return; } if (type.find('T') != std::string::npos) { printf(" %s <= ~%s;\n", var.c_str(), var.c_str()); return; } if (type.find('X') != std::string::npos) { printf(" %s <= 'bx;\n", var.c_str()); return; } } void gen_verilogsim_cell(LibertyAst *ast) { if (ast->find("statetable") != NULL) return; CHECK_NV(ast->args.size(), == 1); printf("module %s (", ast->args[0].c_str()); bool first = true; for (auto child : ast->children) { if (child->id != "pin") continue; CHECK_NV(child->args.size(), == 1); printf("%s%s", first ? "" : ", ", child->args[0].c_str()); first = false; } printf(");\n"); for (auto child : ast->children) { if (child->id != "ff" && child->id != "latch") continue; printf(" reg "); first = true; for (auto arg : child->args) { printf("%s%s", first ? "" : ", ", arg.c_str()); first = false; } printf(";\n"); } for (auto child : ast->children) { if (child->id != "pin") continue; CHECK_NV(child->args.size(), == 1); LibertyAst *dir = CHECK(child->find("direction"), != NULL); LibertyAst *func = child->find("function"); printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str()); if (func != NULL) printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str()); } for (auto child : ast->children) { if (child->id != "ff" || child->args.size() != 2) continue; std::string iq_var = child->args[0]; std::string iqn_var = child->args[1]; std::string clock_edge, clock_expr; event2vl(child->find("clocked_on"), clock_edge, clock_expr); std::string clear_edge, clear_expr; event2vl(child->find("clear"), clear_edge, clear_expr); std::string preset_edge, preset_expr; event2vl(child->find("preset"), preset_edge, preset_expr); std::string edge = ""; if (!clock_edge.empty()) edge += (edge.empty() ? "" : ", ") + clock_edge; if (!clear_edge.empty()) edge += (edge.empty() ? "" : ", ") + clear_edge; if (!preset_edge.empty()) edge += (edge.empty() ? "" : ", ") + preset_edge; if (edge.empty()) continue; printf(" always @(%s) begin\n", edge.c_str()); const char *else_prefix = ""; if (!clear_expr.empty() && !preset_expr.empty()) { printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str()); clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value); clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value); printf(" end\n"); else_prefix = "else "; } if (!clear_expr.empty()) { printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str()); printf(" %s <= 0;\n", iq_var.c_str()); printf(" %s <= 1;\n", iqn_var.c_str()); printf(" end\n"); else_prefix = "else "; } if (!preset_expr.empty()) { printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str()); printf(" %s <= 1;\n", iq_var.c_str()); printf(" %s <= 0;\n", iqn_var.c_str()); printf(" end\n"); else_prefix = "else "; } if (*else_prefix) printf(" %sbegin\n", else_prefix); std::string expr = CHECK(child->find("next_state"), != NULL)->value; printf(" // %s\n", expr.c_str()); printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str()); printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str()); if (*else_prefix) printf(" end\n"); printf(" end\n"); } for (auto child : ast->children) { if (child->id != "latch" || child->args.size() != 2) continue; std::string iq_var = child->args[0]; std::string iqn_var = child->args[1]; std::string enable_edge, enable_expr; event2vl(child->find("enable"), enable_edge, enable_expr); std::string clear_edge, clear_expr; event2vl(child->find("clear"), clear_edge, clear_expr); std::string preset_edge, preset_expr; event2vl(child->find("preset"), preset_edge, preset_expr); printf(" always @* begin\n"); const char *else_prefix = ""; if (!clear_expr.empty() && !preset_expr.empty()) { printf(" %sif ((%s) && (%s)) begin\n", else_prefix, clear_expr.c_str(), preset_expr.c_str()); clear_preset_var(iq_var, CHECK(child->find("clear_preset_var1"), != NULL)->value); clear_preset_var(iqn_var, CHECK(child->find("clear_preset_var2"), != NULL)->value); printf(" end\n"); else_prefix = "else "; } if (!clear_expr.empty()) { printf(" %sif (%s) begin\n", else_prefix, clear_expr.c_str()); printf(" %s <= 0;\n", iq_var.c_str()); printf(" %s <= 1;\n", iqn_var.c_str()); printf(" end\n"); else_prefix = "else "; } if (!preset_expr.empty()) { printf(" %sif (%s) begin\n", else_prefix, preset_expr.c_str()); printf(" %s <= 1;\n", iq_var.c_str()); printf(" %s <= 0;\n", iqn_var.c_str()); printf(" end\n"); else_prefix = "else "; } if (!enable_expr.empty()) { printf(" %sif (%s) begin\n", else_prefix, enable_expr.c_str()); std::string expr = CHECK(child->find("data_in"), != NULL)->value; printf(" %s <= %s;\n", iq_var.c_str(), func2vl(expr).c_str()); printf(" %s <= ~(%s);\n", iqn_var.c_str(), func2vl(expr).c_str()); printf(" end\n"); else_prefix = "else "; } printf(" end\n"); } printf("endmodule\n"); } void gen_verilogsim(LibertyAst *ast) { CHECK_COND(ast->id == "library"); for (auto child : ast->children) if (child->id == "cell" && !child->find("dont_use")) gen_verilogsim_cell(child); } void usage() { fprintf(stderr, "Usage: filterlib [rules-file [liberty-file]]\n"); fprintf(stderr, " or: filterlib -verilogsim [liberty-file]\n"); exit(1); } int main(int argc, char **argv) { bool flag_verilogsim = false; if (argc > 3) usage(); if (argc > 1) { if (!strcmp(argv[1], "-verilogsim")) flag_verilogsim = true; if (!strcmp(argv[1], "-") || !strcmp(argv[1], "-verilogsim")) { LibertyAst::whitelist.insert("/library"); LibertyAst::whitelist.insert("/library/cell"); LibertyAst::whitelist.insert("/library/cell/area"); LibertyAst::whitelist.insert("/library/cell/cell_footprint"); LibertyAst::whitelist.insert("/library/cell/dont_touch"); LibertyAst::whitelist.insert("/library/cell/dont_use"); LibertyAst::whitelist.insert("/library/cell/ff"); LibertyAst::whitelist.insert("/library/cell/ff/*"); LibertyAst::whitelist.insert("/library/cell/latch"); LibertyAst::whitelist.insert("/library/cell/latch/*"); LibertyAst::whitelist.insert("/library/cell/pin"); LibertyAst::whitelist.insert("/library/cell/pin/clock"); LibertyAst::whitelist.insert("/library/cell/pin/direction"); LibertyAst::whitelist.insert("/library/cell/pin/driver_type"); LibertyAst::whitelist.insert("/library/cell/pin/function"); LibertyAst::whitelist.insert("/library/cell/pin_opposite"); LibertyAst::whitelist.insert("/library/cell/pin/state_function"); LibertyAst::whitelist.insert("/library/cell/pin/three_state"); LibertyAst::whitelist.insert("/library/cell/statetable"); LibertyAst::whitelist.insert("/library/cell/statetable/*"); } else { FILE *f = fopen(argv[1], "r"); if (f == NULL) { fprintf(stderr, "Can't open rules file `%s'.\n", argv[1]); usage(); } char buffer[1024]; while (fgets(buffer, 1024, f) != NULL) { char mode = 0; std::string id; for (char *p = buffer; *p; p++) { if (*p == '-' || *p == '+') { if (mode != 0) goto syntax_error; mode = *p; continue; } if (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' || *p == '#') { if (!id.empty()) { if (mode == '-') LibertyAst::blacklist.insert(id); else if (mode == '+') LibertyAst::whitelist.insert(id); else goto syntax_error; } id.clear(); if (*p == '#') break; continue; } id += *p; continue; syntax_error: fprintf(stderr, "Syntax error in rules file:\n%s", buffer); exit(1); } } } } std::istream *f = &std::cin; if (argc == 3) { std::ifstream *ff = new std::ifstream; ff->open(argv[2]); if (ff->fail()) { delete ff; fprintf(stderr, "Can't open liberty file `%s'.\n", argv[2]); usage(); } f = ff; } LibertyParser parser(*f); if (parser.ast) { if (flag_verilogsim) gen_verilogsim(parser.ast); else parser.ast->dump(stdout); } if (argc == 3) delete f; return 0; } #endif ='#n425'>425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
import cStringIO
import Queue
import time
import socket
import random
import os
import threading
import mock

from OpenSSL import SSL
import OpenSSL

from netlib import tcp, certutils
from . import tutils, tservers


class EchoHandler(tcp.BaseHandler):
    sni = None

    def handle_sni(self, connection):
        self.sni = connection.get_servername()

    def handle(self):
        v = self.rfile.readline()
        self.wfile.write(v)
        self.wfile.flush()


class ClientCipherListHandler(tcp.BaseHandler):
    sni = None

    def handle(self):
        self.wfile.write("%s" % self.connection.get_cipher_list())
        self.wfile.flush()


class HangHandler(tcp.BaseHandler):

    def handle(self):
        while True:
            time.sleep(1)


class ALPNHandler(tcp.BaseHandler):
    sni = None

    def handle(self):
        alp = self.get_alpn_proto_negotiated()
        if alp:
            self.wfile.write("%s" % alp)
        else:
            self.wfile.write("NONE")
        self.wfile.flush()


class TestServer(tservers.ServerTestBase):
    handler = EchoHandler

    def test_echo(self):
        testval = "echo!\n"
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        c.wfile.write(testval)
        c.wfile.flush()
        assert c.rfile.readline() == testval

    def test_thread_start_error(self):
        with mock.patch.object(threading.Thread, "start", side_effect=threading.ThreadError("nonewthread")) as m:
            c = tcp.TCPClient(("127.0.0.1", self.port))
            c.connect()
            assert not c.rfile.read(1)
            assert m.called
            assert "nonewthread" in self.q.get_nowait()
        self.test_echo()


class TestServerBind(tservers.ServerTestBase):

    class handler(tcp.BaseHandler):

        def handle(self):
            self.wfile.write(str(self.connection.getpeername()))
            self.wfile.flush()

    def test_bind(self):
        """ Test to bind to a given random port. Try again if the random port turned out to be blocked. """
        for i in range(20):
            random_port = random.randrange(1024, 65535)
            try:
                c = tcp.TCPClient(
                    ("127.0.0.1", self.port), source_address=(
                        "127.0.0.1", random_port))
                c.connect()
                assert c.rfile.readline() == str(("127.0.0.1", random_port))
                return
            except tcp.NetLibError:  # port probably already in use
                pass


class TestServerIPv6(tservers.ServerTestBase):
    handler = EchoHandler
    addr = tcp.Address(("localhost", 0), use_ipv6=True)

    def test_echo(self):
        testval = "echo!\n"
        c = tcp.TCPClient(tcp.Address(("::1", self.port), use_ipv6=True))
        c.connect()
        c.wfile.write(testval)
        c.wfile.flush()
        assert c.rfile.readline() == testval


class TestEcho(tservers.ServerTestBase):
    handler = EchoHandler

    def test_echo(self):
        testval = "echo!\n"
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        c.wfile.write(testval)
        c.wfile.flush()
        assert c.rfile.readline() == testval


class HardDisconnectHandler(tcp.BaseHandler):

    def handle(self):
        self.connection.close()


class TestFinishFail(tservers.ServerTestBase):

    """
        This tests a difficult-to-trigger exception in the .finish() method of
        the handler.
    """
    handler = EchoHandler

    def test_disconnect_in_finish(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        c.wfile.write("foo\n")
        c.wfile.flush = mock.Mock(side_effect=tcp.NetLibDisconnect)
        c.finish()


class TestServerSSL(tservers.ServerTestBase):
    handler = EchoHandler
    ssl = dict(
        cipher_list="AES256-SHA",
        chain_file=tutils.test_data.path("data/server.crt")
    )

    def test_echo(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        c.convert_to_ssl(sni="foo.com", options=SSL.OP_ALL)
        testval = "echo!\n"
        c.wfile.write(testval)
        c.wfile.flush()
        assert c.rfile.readline() == testval

    def test_get_current_cipher(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        assert not c.get_current_cipher()
        c.convert_to_ssl(sni="foo.com")
        ret = c.get_current_cipher()
        assert ret
        assert "AES" in ret[0]


class TestSSLv3Only(tservers.ServerTestBase):
    handler = EchoHandler
    ssl = dict(
        request_client_cert=False,
        v3_only=True
    )

    def test_failure(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        tutils.raises(tcp.NetLibError, c.convert_to_ssl, sni="foo.com")


class TestSSLUpstreamCertVerificationWBadServerCert(tservers.ServerTestBase):
    handler = EchoHandler

    ssl = dict(
        cert=tutils.test_data.path("data/verificationcerts/untrusted.crt"),
        key=tutils.test_data.path("data/verificationcerts/verification-server.key"))

    def test_mode_default_should_pass(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()

        c.convert_to_ssl()

        # Verification errors should be saved even if connection isn't aborted
        # aborted
        assert c.ssl_verification_error is not None

        testval = "echo!\n"
        c.wfile.write(testval)
        c.wfile.flush()
        assert c.rfile.readline() == testval

    def test_mode_none_should_pass(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()

        c.convert_to_ssl(verify_options=SSL.VERIFY_NONE)

        # Verification errors should be saved even if connection isn't aborted
        assert c.ssl_verification_error is not None

        testval = "echo!\n"
        c.wfile.write(testval)
        c.wfile.flush()
        assert c.rfile.readline() == testval

    def test_mode_strict_should_fail(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()

        tutils.raises(
            tcp.NetLibError,
            c.convert_to_ssl,
            verify_options=SSL.VERIFY_PEER,
            ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted.pem"))

        assert c.ssl_verification_error is not None

        # Unknown issuing certificate authority for first certificate
        assert c.ssl_verification_error['errno'] == 20
        assert c.ssl_verification_error['depth'] == 0


class TestSSLUpstreamCertVerificationWBadCertChain(tservers.ServerTestBase):
    handler = EchoHandler

    ssl = dict(
        cert=tutils.test_data.path("data/verificationcerts/untrusted-chain.crt"),
        key=tutils.test_data.path("data/verificationcerts/verification-server.key"))

    def test_mode_strict_should_fail(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()

        tutils.raises(
            "certificate verify failed",
            c.convert_to_ssl,
            verify_options=SSL.VERIFY_PEER,
            ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted.pem"))

        assert c.ssl_verification_error is not None

        # Untrusted self-signed certificate at second position in certificate
        # chain
        assert c.ssl_verification_error['errno'] == 19
        assert c.ssl_verification_error['depth'] == 1


class TestSSLUpstreamCertVerificationWValidCertChain(tservers.ServerTestBase):
    handler = EchoHandler

    ssl = dict(
        cert=tutils.test_data.path("data/verificationcerts/trusted-chain.crt"),
        key=tutils.test_data.path("data/verificationcerts/verification-server.key"))

    def test_mode_strict_w_pemfile_should_pass(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()

        c.convert_to_ssl(
            verify_options=SSL.VERIFY_PEER,
            ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted.pem"))

        assert c.ssl_verification_error is None

        testval = "echo!\n"
        c.wfile.write(testval)
        c.wfile.flush()
        assert c.rfile.readline() == testval

    def test_mode_strict_w_cadir_should_pass(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()

        c.convert_to_ssl(
            verify_options=SSL.VERIFY_PEER,
            ca_path=tutils.test_data.path("data/verificationcerts/"))

        assert c.ssl_verification_error is None

        testval = "echo!\n"
        c.wfile.write(testval)
        c.wfile.flush()
        assert c.rfile.readline() == testval


class TestSSLClientCert(tservers.ServerTestBase):

    class handler(tcp.BaseHandler):
        sni = None

        def handle_sni(self, connection):
            self.sni = connection.get_servername()

        def handle(self):
            self.wfile.write("%s\n" % self.clientcert.serial)
            self.wfile.flush()

    ssl = dict(
        request_client_cert=True,
        v3_only=False
    )

    def test_clientcert(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        c.convert_to_ssl(
            cert=tutils.test_data.path("data/clientcert/client.pem"))
        assert c.rfile.readline().strip() == "1"

    def test_clientcert_err(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        tutils.raises(
            tcp.NetLibError,
            c.convert_to_ssl,
            cert=tutils.test_data.path("data/clientcert/make")
        )


class TestSNI(tservers.ServerTestBase):

    class handler(tcp.BaseHandler):
        sni = None

        def handle_sni(self, connection):
            self.sni = connection.get_servername()

        def handle(self):
            self.wfile.write(self.sni)
            self.wfile.flush()

    ssl = True

    def test_echo(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        c.convert_to_ssl(sni="foo.com")
        assert c.sni == "foo.com"
        assert c.rfile.readline() == "foo.com"


class TestServerCipherList(tservers.ServerTestBase):
    handler = ClientCipherListHandler
    ssl = dict(
        cipher_list='RC4-SHA'
    )

    def test_echo(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        c.convert_to_ssl(sni="foo.com")
        assert c.rfile.readline() == "['RC4-SHA']"


class TestServerCurrentCipher(tservers.ServerTestBase):

    class handler(tcp.BaseHandler):
        sni = None

        def handle(self):
            self.wfile.write("%s" % str(self.get_current_cipher()))
            self.wfile.flush()

    ssl = dict(
        cipher_list='RC4-SHA'
    )

    def test_echo(self):
        c = tcp.TCPClient(("127.0.0.1", self.port))
        c.connect()
        c.convert_to_ssl(sni="foo.com")
        assert "RC4-SHA" in c.rfile.readline()


class TestServerCipherListError(tservers.ServerTestBase):