OpenSSL code works on XP but hangs forever in Vista and up

后端 未结 1 1778
情书的邮戳
情书的邮戳 2021-01-24 17:52

This code starts a minimal SSL server:

WSAStartup(MakeWord(1,1), WData);
SSL_library_init;
SSL_load_error_strings;
ctx := SSL_CTX_new(SSLv23_server_method);
SSL_         


        
1条回答
  •  无人共我
    2021-01-24 18:11

    It probably has to do with the double BIO_do_accept() calls.

    I took OpenSSL's S_SERVER.C code, made some changes, and crunched it down to the simpler version below. It works on Vista and Windows 7! It uses a completely different set of BIO calls from the problem code above, non-blocking works, and (unlike S_SERVER.C and most server examples on the Net) Google Chrome POSTs are properly grabbed via timeouts per question 7054471.

    program s_server;
    
    uses sysutils, winsock, windows;
    
    const
      SSLEAY32DLL = 'ssleay32.dll';
      SSL_FILETYPE_PEM  = 1;
      SSL_SENT_SHUTDOWN = 1;
      SSL_RECEIVED_SHUTDOWN = 2;
    
    function BIO_f_ssl: pointer; cdecl; external SSLEAY32DLL;
    function SSL_CTX_check_private_key(ctx: pointer): BOOL; cdecl; external SSLEAY32DLL;
    function SSL_CTX_ctrl(ctx: pointer; cmd, i: integer; p: pointer): integer; cdecl; external SSLEAY32DLL;
    procedure SSL_CTX_free(ctx: pointer); cdecl; external SSLEAY32DLL;
    function SSL_CTX_new(meth: pointer): pointer; cdecl; external SSLEAY32DLL;
    procedure SSL_CTX_set_quiet_shutdown(ctx: pointer; mode: integer); cdecl; external SSLEAY32DLL;
    function SSL_CTX_use_certificate_chain_file(ctx: pointer; fname: pchar): integer; cdecl; external SSLEAY32DLL;
    function SSL_CTX_use_PrivateKey_file(ctx: pointer; fname: pchar; itype: integer): integer; cdecl; external SSLEAY32DLL;
    procedure SSL_library_init; cdecl; external SSLEAY32DLL;
    procedure SSL_load_error_strings; cdecl; external SSLEAY32DLL;
    function SSL_new(ctx: pointer): pointer; cdecl; external SSLEAY32DLL;
    procedure SSL_set_accept_state(b: pointer); cdecl; external SSLEAY32DLL;
    procedure SSL_set_bio(ssl, readbio, writebio: pointer); cdecl; external SSLEAY32DLL;
    procedure SSL_set_shutdown(ssl: pointer; mode: integer); cdecl; external SSLEAY32DLL;
    function SSLv23_server_method: pointer; cdecl; external SSLEAY32DLL;
    
    const
      LIBEAY32DLL = 'libeay32.dll';
      BIO_NOCLOSE = $00;
      BIO_CLOSE = $01;
    
    function BIO_ctrl(bp: pointer; cmd: integer; larg: integer; parg: pointer): integer; cdecl; external LIBEAY32DLL;
    function BIO_f_buffer: pointer; cdecl; external LIBEAY32DLL;
    procedure BIO_free_all(bp: pointer); cdecl; external LIBEAY32DLL;
    function BIO_gets(b: pointer; buf: pchar; size: integer): integer; cdecl; external LIBEAY32DLL;
    function BIO_int_ctrl(bp: pointer; cmd: integer; i1, i2: integer): integer; cdecl; external LIBEAY32DLL;
    function BIO_new(t: pointer): pointer; cdecl; external LIBEAY32DLL;
    function BIO_new_socket(sock, flag: integer): pointer; cdecl; external LIBEAY32DLL;
    function BIO_push(b: pointer; append: pointer): pointer; cdecl; external LIBEAY32DLL;
    function BIO_socket_ioctl(sock: integer; ctl: cardinal; p: pointer): integer; cdecl; external LIBEAY32DLL;
    function BIO_test_flags(bp: pointer; flags: integer): integer; cdecl; external LIBEAY32DLL;
    function BIO_write(bp, buffer: pointer; size: integer): integer; cdecl; external LIBEAY32DLL;
    procedure OPENSSL_load_builtin_modules; cdecl; external LIBEAY32DLL;
    
    function BIO_flush(b: pointer): integer; const BIO_CTRL_FLUSH = 11; begin result := BIO_ctrl(b, BIO_CTRL_FLUSH, 0, nil); end;
    function BIO_set_ssl(b, ssl: pointer; c: integer): integer; const BIO_C_SET_SSL = 109; begin result := BIO_ctrl(b, BIO_C_SET_SSL, c, ssl); end;
    function BIO_should_retry(b: pointer): boolean; const BIO_FLAGS_SHOULD_RETRY = 8; begin result := BIO_test_flags(b, BIO_FLAGS_SHOULD_RETRY) <> 0; end;
    function SSL_CTX_set_options(ctx: pointer; op: integer): integer; const SSL_CTRL_OPTIONS = 32; begin result := SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, op, nil); end;
    
    procedure confirm(b: boolean); begin {$WARN SYMBOL_PLATFORM OFF} win32check(b); {$WARN SYMBOL_PLATFORM ON} end;
    
    const
      DEFAULTPORT = 443;
      MAXWAIT = 500; // 500ms max for read
    
    function getresponse(const ip, request: string): string;
    var body: string;
    begin // ignore request and just announce ip
    body := 'Hello!

    Your IP is...

    ' + ip + ''; result := 'HTTP/1.0 200 OK'#13#10'Connection: Close'#13#10'Content-Type: text/HTML'#13#10'Content-Length: ' + IntToStr(length(body)) + #13#10#13#10 + body; end; const BUFSIZE = 16*1024; // used in openssl s_server.c var buf: packed array[0..BUFSIZE-1] of char; request, response: string; ctx: pointer; procedure read_and_respond(const ip: string; sock: integer); var i, j, k: integer; start: cardinal; con, io, ssl_bio, sbio: pointer; label err, endlabel, write_error; begin io := BIO_new(BIO_f_buffer); ssl_bio := BIO_new(BIO_f_ssl); if (io = nil) or (ssl_bio = nil) then goto err; // enable non-blocking i := 1; if BIO_socket_ioctl(sock, FIONBIO, @i) < 0 then writeln('Can''t unblock!'); con := SSL_new(ctx); if con = nil then goto err; sbio := BIO_new_socket(sock, BIO_NOCLOSE); SSL_set_bio(con, sbio, sbio); SSL_set_accept_state(con); BIO_set_ssl(ssl_bio, con, BIO_CLOSE); BIO_push(io, ssl_bio); request := ''; start := gettickcount; repeat i := BIO_gets(io, @buf, bufsize-1); if i < 0 then // error if not BIO_should_retry(io) then goto err else continue else if i > 0 then begin buf[i] := #0; request := request + buf; if length(request) > BUFSIZE then break; // stop malicious request end; until (gettickcount - start) > MAXWAIT; // could also stop if post body == content-length response := getresponse(ip, request); i := 1; j := length(response); while i <= j do begin k := BIO_write(io, @response[i], j-i+1); if k <= 0 then if BIO_should_retry(io) then continue else break else inc(i, k); end; while BIO_flush(io) <= 0 do if not BIO_should_retry(io) then break; SSL_set_shutdown(con, SSL_SENT_SHUTDOWN or SSL_RECEIVED_SHUTDOWN); // re-use session err: if io <> nil then BIO_free_all(io); // including ssl_bio end; var listen_socket: integer; procedure do_server; var server, client: TSockAddr; sock, clientlen: integer; begin listen_socket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); confirm(listen_socket <> INVALID_SOCKET); fillchar(server, sizeof(server), 0); server.sin_family := AF_INET; server.sin_port := htons(DEFAULTPORT); server.sin_addr.s_addr := INADDR_ANY; confirm(bind(listen_socket, server, sizeof(server)) <> INVALID_SOCKET); confirm(listen(listen_socket, 128) <> INVALID_SOCKET); while TRUE do begin fillchar(client, sizeof(client), 0); clientlen := sizeof(client); sock := accept(listen_socket, @client, @clientlen); if sock <> INVALID_SOCKET then begin read_and_respond(inet_ntoa(client.sin_addr), sock); shutdown(sock, SD_BOTH); closesocket(sock); end; end; end; procedure shutdownserver; begin closesocket(listen_socket); SSL_CTX_free(ctx); WSAcleanup; end; function consolehandler(signal: DWORD): BOOL; stdcall; begin // handle some console events case signal of CTRL_C_EVENT, CTRL_BREAK_EVENT: result := TRUE; // handle these by ignoring them CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT: begin shutdownserver; result := FALSE; // avoid popup end else result := FALSE; // not handling end; // case end; procedure main; var WData: TWSAData; begin allocconsole; confirm(SetConsoleCtrlHandler(@ConsoleHandler, TRUE)); if WSAStartup(MakeWord(2, 2), WData) <> 0 then confirm(WSAStartup(MakeWord(1,1), WData) = 0); SSL_library_init; SSL_load_error_strings; OPENSSL_load_builtin_modules; ctx := SSL_CTX_new(SSLv23_server_method); confirm(ctx <> nil); confirm(SSL_CTX_use_certificate_chain_file(ctx, 'cert.pem') > 0); confirm(SSL_CTX_use_PrivateKey_file(ctx, 'key.pem', SSL_FILETYPE_PEM) > 0); confirm(SSL_CTX_check_private_key(ctx)); SSL_CTX_set_quiet_shutdown(ctx, 1); SSL_CTX_set_options(ctx, 0); repeat try do_server except on E: Exception do writeln(E.Message); end; until FALSE; end; begin main end.

    0 讨论(0)
提交回复
热议问题