Client/Server TCP TLS communication problem

Just starting out? Need help? Post your questions and find answers here.
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Client/Server TCP TLS communication problem

Post by tatanas »

Hi,

I've developed a client/server TCP software. The clients run on Windows and the server application on Debian.
With the release of Purebasic 6.20 and its built-in TLS functionality, I wanted to secure the communications of this software.

I generated a private key and a certificate that I tested with the example source code provided in the Purebasic documentation. It works well.
However, when I modify my client and server code, it doesn't work. The connection is made, the client send the datas but the server can't interprete them correctly. And the SendNetworkstring() used by the client is hanging and never returns, so the client is blocked.

On the client side, I added:
UseNetworkTLS()
and #PB_Network_TLSv1_2 in OpenNetworkConnection()

On the server side, I added:
UseNetworkTLS(PrivateKey$,Cert$)
and #PB_Network_TLSv1_2 in CreateNetworkServer()
The variables PrivateKey$ and Cert$ do contain the necessary data.

A detail that probably has its importance: The server does not use the ReceiveNetworkData() procedure but instead uses recv_().
Is my communication problem coming from this ? At what level is the encryption/decryption of sent/received data handled?

Thank you in advance.
Windows 10 Pro x64
PureBasic 6.20 x64
Fred
Administrator
Administrator
Posts: 18178
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Client/Server TCP TLS communication problem

Post by Fred »

Yes you can't use recv_() but you need to use the libressl equivalent (tls_recv() IIRC)
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Client/Server TCP TLS communication problem

Post by tatanas »

Do you know the name of a library where I could find this function? Google isn't showing me anything interesting, as if this function no longer exists.

EDit : It seems that tls_read() replaces it.
Windows 10 Pro x64
PureBasic 6.20 x64
User avatar
idle
Always Here
Always Here
Posts: 5884
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Client/Server TCP TLS communication problem

Post by idle »

here's my static lib import for 6.20, so you can use regular PB network functions

Code: Select all

;V 1.0.2
;author Hexor,infratec,idle
;* $OpenBSD: tls.h,v 1.58 2020/01/22 06:44:02 beck Exp $ */
;*
;* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
;*
;* Permission To use, copy, modify, And 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.
;*
; Original from idle => https://www.purebasic.fr/english/viewtopic.php?p=593079#p593079

;/=============
;|
;| Usage:
;| - Include that file here on top of your (existing?!) network application.
;| - make sure a libtls-xx.dll or libtls-xx.a is in the same directory as your app
;|                                               (or set a global variable LIBTLSFILE$ to path and filename of that library BEFORE including this source)
;|   you will find compiled libraries in the dropbox account from idle, see here:
;|   https://www.purebasic.fr/english/viewtopic.php?p=593079#p593079
;|   or just compile newer versions yourself
;| - That's it!
;|   If you now want to initiate a TLS connection, use all known PB commands as usual, but:
;|   Add one of the PB_Network_TLSv1_xx constants to:
;|   OpenNetworkConnection(ServerName$, Port [, Mode  <= here
;|   e.g. OpenNetworkConnection("my.domain.com", 1234, #PB_Network_TCP | #PB_Network_IPv4 | #PB_Network_TLS_DEFAULT, ...)
;|
;|   OR
;|
;|   CreateNetworkServer(Server, Port [, Mode, <== here
;|   e.g. CreateNetworkServer(#PB_Any, 1234, #PB_Network_TCP | #PB_Network_IPv4 | #PB_Network_TLS_DEFAULT, ...)
;|   In case of a server, you would need to call Init_TLS() also most likely.
;|   Because with that procedure you can set the needed TLS certificates (cert and key file typically)
;|
;|   ;/============

#TLS_API = 20200120

#TLS_PROTOCOL_TLSv1_0 = (1 << 1)
#TLS_PROTOCOL_TLSv1_1 = (1 << 2)
#TLS_PROTOCOL_TLSv1_2 = (1 << 3)
#TLS_PROTOCOL_TLSv1_3 = (1 << 4)

#TLS_PROTOCOL_TLSv1 = #TLS_PROTOCOL_TLSv1_0 | #TLS_PROTOCOL_TLSv1_1 | #TLS_PROTOCOL_TLSv1_2 | #TLS_PROTOCOL_TLSv1_3

#TLS_PROTOCOLS_ALL     = #TLS_PROTOCOL_TLSv1
#TLS_PROTOCOLS_DEFAULT = (#TLS_PROTOCOL_TLSv1_2 | #TLS_PROTOCOL_TLSv1_3)

#TLS_WANT_POLLIN  = -2
#TLS_WANT_POLLOUT = -3

;/* RFC 6960 Section 2.3 */
#TLS_OCSP_RESPONSE_SUCCESSFUL    = 0
#TLS_OCSP_RESPONSE_MALFORMED     = 1
#TLS_OCSP_RESPONSE_INTERNALERROR = 2
#TLS_OCSP_RESPONSE_TRYLATER      = 3
#TLS_OCSP_RESPONSE_SIGREQUIRED   = 4
#TLS_OCSP_RESPONSE_UNAUTHORIZED  = 5

;/* RFC 6960 Section 2.2 */
#TLS_OCSP_CERT_GOOD    = 0
#TLS_OCSP_CERT_REVOKED = 1
#TLS_OCSP_CERT_UNKNOWN = 2

;/* RFC 5280 Section 5.3.1 */
#TLS_CRL_REASON_UNSPECIFIED            = 0
#TLS_CRL_REASON_KEY_COMPROMISE         = 1
#TLS_CRL_REASON_CA_COMPROMISE          = 2
#TLS_CRL_REASON_AFFILIATION_CHANGED    = 3
#TLS_CRL_REASON_SUPERSEDED             = 4
#TLS_CRL_REASON_CESSATION_OF_OPERATION = 5
#TLS_CRL_REASON_CERTIFICATE_HOLD       = 6
#TLS_CRL_REASON_REMOVE_FROM_CRL        = 8
#TLS_CRL_REASON_PRIVILEGE_WITHDRAWN    = 9
#TLS_CRL_REASON_AA_COMPROMISE          = 10

#TLS_MAX_SESSION_ID_LENGTH = 32
#TLS_TICKET_KEY_SIZE       = 48

CompilerIf Defined(PB_Network_TLSv1, #PB_Constant) = 0
  #PB_Network_TLSv1_0     = $10
  #PB_Network_TLSv1_1     = $20
  #PB_Network_TLSv1_2     = $40
  #PB_Network_TLSv1_3     = $80
    
  #PB_Network_TLSv1       = #PB_Network_TLSv1_0 | #PB_Network_TLSv1_1 | #PB_Network_TLSv1_2 | #PB_Network_TLSv1_3
CompilerEndIf     
  #PB_Network_TLS_DEFAULT = #PB_Network_TLSv1_2 | #PB_Network_TLSv1_3
  #PB_Network_KeepAlive   = $1000
  #PB_Network_Extra       = #PB_Network_TLSv1 | #PB_Network_KeepAlive
;CompilerEndIf

CompilerIf Defined(TLS_AUTOINIT, #PB_Constant) = #False
  #TLS_AUTOINIT = #True
CompilerEndIf

PrototypeC tls_read_cb(ctx,*buf,_buflen,*cb_arg);
PrototypeC tls_write_cb(ctx,*buf,_buflen,*cb_arg);

ImportC "pbtls.lib" ;C:\Purebasic\PureLibraries\Windows\Libraries\pbtls.lib" 
  tls_init()                                                                                                   
  tls_config_error(config)                                                                            
  tls_error(ctx)                                                                                      
  tls_config_new()                                                                                             
  tls_config_free(config)                                                                             
  tls_default_ca_cert_file()                                                                                   
  tls_config_add_keypair_file(config,cert_file.p-utf8,key_file.p-utf8)                              
  tls_config_add_keypair_mem(config,cert.p-utf8,certlen.i,key.p-utf8,key_len.i)        
  tls_config_add_keypair_ocsp_file(config,cert_file.p-utf8,key_file.p-utf8,ocsp_staple_file.p-utf8)
  tls_config_add_keypair_ocsp_mem(config,cert.p-utf8,cert_len.i,key.p-utf8,key_len.i,staple.p-utf8,staple_len.i)
  tls_config_set_alpn(config,alpn.p-utf8)                                                                                           
  tls_config_set_ca_file(config,ca_file.p-utf8)                                                                                     
  tls_config_set_ca_path(config,ca_path.p-utf8)                                                                                     
  tls_config_set_ca_mem(config,*ca,len.i)                                                                               
  tls_config_set_cert_file(config,cert_file.p-utf8)                                                                                 
  tls_config_set_cert_mem(config,*cert,len.i)                                                                           
  tls_config_set_ciphers(config,ciphers.p-utf8)                                                                                     
  tls_config_set_crl_file(config,crl_file.p-utf8)                                                                                   
  tls_config_set_crl_mem(config,crl.p-utf8,len.i)                                                                             
  tls_config_set_dheparams(config,params.p-utf8)                                                                                    
  tls_config_set_ecdhecurve(config,curve.p-utf8)                                                                                    
  tls_config_set_ecdhecurves(config,curves.p-utf8)                                                                                  
  tls_config_set_key_file(config,key_file.p-utf8)                                                                                   
  tls_config_set_key_mem(config,*key,len.i)                                                                             
  tls_config_set_keypair_file(config,cert_file.p-utf8,key_file.p-utf8)                                                             
  tls_config_set_keypair_mem(config,cert.p-utf8,cert_len.i,key.p-utf8,key_len.i)                                       
  tls_config_set_keypair_ocsp_file(config,cert_file.p-utf8,key_file.p-utf8,staple_file.p-utf8)                                    
  tls_config_set_keypair_ocsp_mem(config,cert.p-utf8,cert_len.i,key.p-utf8,key_len.i,staple.p-utf8,staple_len.i) 
  tls_config_set_ocsp_staple_mem(config,staple.p-utf8,len.i)                                                                  
  tls_config_set_ocsp_staple_file(config,staple_file.p-utf8)                                                                        
  tls_config_set_protocols(config,protocols.l)                                                                               
  tls_config_set_session_fd(config,session_fd.l)                                                                                  
  tls_config_set_verify_depth(config,verify_depth.l)                                                                              
  tls_config_prefer_ciphers_client(config)                                                                                           
  tls_config_prefer_ciphers_server(config)                                                                                           
  tls_config_insecure_noverifycert(config)                                                                                           
  tls_config_insecure_noverifyname(config)                                                                                           
  tls_config_insecure_noverifytime(config)                                                                                           
  tls_config_verify(config)                                                                                                          
  tls_config_ocsp_require_stapling(config)                                                                                           
  tls_config_verify_client(config)                                                                                                   
  tls_config_verify_client_optional(config)                                                                                          
  tls_config_clear_keys(config)                                                                                                      
  tls_config_parse_protocols(*protocols,protostr.p-utf8)                                                                           
  tls_config_set_session_id(config,session_id.p-utf8,len.i)                                                                   
  tls_config_set_session_lifetime(config,lifetime.l)                                                                              
  tls_config_add_ticket_key(config,keyrev.i,*key,keylen.i)                                                   
  tls_client()                                                                                                                                
  tls_server()                                                                                                                                
  tls_configure(ctx,config)                                                                                                 
  tls_reset(ctx)                                                                                                                     
  tls_free(ctx)                                                                                                                      
  tls_accept_fds(ctx,*cctx,fd_read.l,fd_write.l)                                                                       
  tls_accept_socket(ctx,*cctx,socket.l)                                                                                   
  tls_accept_cbs(ctx,*cctx,*read_cb.tls_read_cb,*write_cb.tls_write_cb,*cb_arg)                                      
  tls_connect(ctx,host.p-utf8,port.p-utf8)                                                                                         
  tls_connect_fds(ctx,fd_read.l,fd_write.l,servername.p-utf8)                                                                 
  tls_connect_servername(ctx,host.p-utf8,port.p-utf8,servername.p-utf8)                                                           
  tls_connect_socket(ctx,s.l,servername.p-utf8)                                                                                  
  tls_connect_cbs(ctx,*read_cb.tls_read_cb,*write_cb.tls_write_cb,*cb_arg,servername.p-utf8)                                
  tls_handshake(ctx)                                                                                                                 
  tls_read(ctx,*buf,buflen.i)                                                                                            
  tls_write(ctx,*buf,buflen.i)                                                                                          
  tls_close(ctx)                                                                                                                     
  tls_peer_cert_provided(ctx)                                                                                                        
  tls_peer_cert_contains_name(ctx,name.p-utf8)                                                                                      
  tls_peer_cert_hash(ctx)                                                                                                            
  tls_peer_cert_issuer(ctx)                                                                                                          
  tls_peer_cert_subject(ctx)                                                                                                         
  tls_peer_cert_notbefore(ctx)                                                                                                       
  tls_peer_cert_notafter(ctx)                                                                                                        
  tls_peer_cert_chain_pem(ctx,*len)                                                                                          
  tls_conn_alpn_selected(ctx)                                                                                                        
  tls_conn_cipher(ctx)                                                                                                               
  tls_conn_cipher_strength(ctx)                                                                                                      
  tls_conn_servername(ctx)                                                                                                           
  tls_conn_session_resumed(ctx)                                                                                                      
  tls_conn_version(ctx)                                                                                                              
  tls_load_file(file.p-utf8,*len,*password)                                                                                    
  tls_unload_file(*buf,len.i)                                                                                                   
  tls_ocsp_process_response(ctx,response.p-utf8,size.i)                                                                       
  tls_peer_ocsp_cert_status(ctx)                                                                                                     
  tls_peer_ocsp_crl_reason(ctx)                                                                                                      
  tls_peer_ocsp_next_update(ctx)                                                                                                     
  tls_peer_ocsp_response_status(ctx)                                                                                                 
  tls_peer_ocsp_result(ctx)                                                                                                          
  tls_peer_ocsp_revocation_time(ctx)                                                                                                 
  tls_peer_ocsp_this_update(ctx)                                                                                                     
  tls_peer_ocsp_url(ctx)                                                                                                             
EndImport

Structure TLS_Connections
  *ctx
EndStructure

Structure TLS_Certs 
  Path$
  CertFile$
  KeyFile$
  CaCertFile$
  domain$
EndStructure 

Structure TLS_Globals
  CertFile$
  KeyFile$
  CaCertFile$
  List certs.TLS_Certs()
  domain$
  LastError.i
  DLL.i
  muxClient.i
  muxSever.i
  Map Clients.TLS_Connections()
  Map Servers.TLS_Connections()
EndStructure

Global TLSG.TLS_Globals

Procedure __MyInit()
  TLSG\DLL=1
  TLSG\muxClient = CreateMutex()
  TLSG\muxSever = CreateMutex() 
    
EndProcedure

Enumeration TLS_Errors 
  #TLS_Error_InitFailed
  #TLS_Error_None
  #TLS_Error_NewConfig_Failed
  #TLS_Error_CantLoad_CertFile
  #TLS_Error_CantLoad_KeyFile
  #TLS_Error_CantLoad_RootCert
  #TLS_Error_Unsupported_Protocol
  #TLS_Error_Cant_Start_Client
  #TLS_Error_Configure_Error
  #TLS_Error_Cant_Start_Server
  #TLS_Error_Cant_Connect_Socket
EndEnumeration

Global TLSG\LastError = #TLS_Error_None

Procedure TLS_GetLastError()
  ProcedureReturn TLSG\LastError
EndProcedure

Procedure TLS_CreateNetworkServer(Server, Port, Mode, BindedIP.s)
  Protected TLSMode, ServerID, *ctx, *cfg, *Error, SockOpt.l, SockOptLen.l
  
  If Mode & #PB_Network_TLSv1
    TLSMode = #PB_Network_TLSv1
    TLSG\LastError = #TLS_Error_None 
    *ctx = tls_server()
    If *ctx 
      *cfg  = tls_config_new()
      If *cfg = 0
        TLSG\LastError = #TLS_Error_NewConfig_Failed
      Else
        FirstElement(TLSG\certs())
        
        tls_config_set_ca_path(*cfg,TLSG\certs()\Path$)
        
        If tls_config_set_keypair_file(*cfg,tlsg\certs()\CertFile$,tlsg\certs()\KeyFile$) = -1
          TLSG\LastError = #TLS_Error_CantLoad_CertFile
        EndIf 
        
        While NextElement(TLSG\certs()) 
          If tls_config_add_keypair_file(*cfg,tlsg\certs()\CertFile$,tlsg\certs()\KeyFile$) = -1
            TLSG\LastError = #TLS_Error_CantLoad_CertFile
          EndIf   
        Wend   
                
        If tls_config_set_protocols(*cfg, TLSMode) = -1
          TLSG\LastError = #TLS_Error_Unsupported_Protocol
        EndIf
        
        If tls_configure(*ctx, *cfg) = -1
          TLSG\LastError = #TLS_Error_Configure_Error 
          *Error = tls_config_error(*cfg)
          If *Error
            PrintN("TLS Config error: " + PeekS(*Error, - 1, #PB_UTF8))
          EndIf
        EndIf     
        tls_config_free(*cfg)
      EndIf 
      
      If *ctx 
        Mode = Mode & ($FFFFFFFF - #PB_Network_Extra)
        ServerID = CreateNetworkServer(Server, Port, mode, BindedIP)
        If ServerID
          Protected iocontext   
          If tls_accept_socket(*ctx,@iocontext,ServerID(ServerID)) = -1
            TLSG\LastError = #TLS_Error_Cant_Connect_Socket
            PrintN("#TLS_Error_Cant_Connect_Socket") 
            *error = tls_error(*ctx) 
            PrintN("error " + PeekS(*Error, - 1, #PB_UTF8)) 
          Else
            LockMutex(TLSG\muxSever) 
            AddMapElement(TLSG\Servers(), Str(ServerID(ServerID)))
            TLSG\Servers()\ctx = *ctx
            UnlockMutex(TLSG\muxSever) 
          EndIf
        EndIf
      EndIf
    Else 
      TLSG\LastError = #TLS_Error_Cant_Start_Server
    EndIf
  Else
    mode = #PB_Network_IPv4 | #PB_Network_TCP
    ServerID = CreateNetworkServer(Server, Port, Mode, BindedIP)
  EndIf
  
  ProcedureReturn ServerID
EndProcedure

Procedure TLS_NetworkServerEvent(ServerID)
  Protected Result, Server, ClientID, ctx,*server.TLS_Connections
  Protected  *client.TLS_Connections,key.s 
  
  Result = NetworkServerEvent(ServerID)
  Select Result
    Case #PB_NetworkEvent_Connect
      
      Server = EventServer()
      Server = ServerID(Server)
      LockMutex(TLSG\muxSever)
      *server = FindMapElement(TLSG\Servers(), Str(Server))
      
      If *server 
        ;TLS!
        ClientID = EventClient()
        If tls_accept_socket(*server\ctx, @ctx, ConnectionID(ClientID)) = -1
          CloseNetworkConnection(ClientID)
          Result = #PB_NetworkEvent_None
        Else
          LockMutex(TLSG\muxClient) 
          
          AddMapElement(TLSG\Clients(),Str(ClientID))
          TLSG\Clients()\ctx = ctx
          If tls_handshake(ctx) = -1 
           CloseNetworkConnection(ClientID) 
           DeleteMapElement(TLSG\Clients(),Str(ClientID))
           Result = #PB_NetworkEvent_None
          EndIf 
          
          UnlockMutex(TLSG\muxClient)  
        EndIf
      EndIf
       UnlockMutex(TLSG\muxSever)
    Case #PB_NetworkEvent_Disconnect
      
      ClientID = EventClient()
      key = Str(ClientID)
      LockMutex(TLSG\muxClient) 
      *client = FindMapElement(TLSG\Clients(),key) 
      If *client 
        tls_close(*client\ctx)
        tls_free(*client\ctx)
        DeleteMapElement(TLSG\Clients(),key)
      EndIf
      UnlockMutex(TLSG\muxClient)
      
  EndSelect
  
  ProcedureReturn Result
EndProcedure

Procedure TLS_CloseNetworkServer(Server)
  Protected *client.TLS_Connections 
  Protected key.s = Str(Server)
  
  LockMutex(TLSG\muxClient) 
  
  *client = FindMapElement(TLSG\Clients(),key) 
  
  If *client 
    ;is TLS connection!
    CloseNetworkServer(Server)
    tls_close(*Client\ctx)
    tls_free(*client\ctx)
    DeleteMapElement(TLSG\clients(),key)
  Else
    CloseNetworkServer(Server)
  EndIf
  
  UnlockMutex(TLSG\muxClient) 
  
EndProcedure

Procedure TLS_OpenNetworkConnection(ServerName$, Port, Mode, TimeOut, LocaleIP$, LocalePort)
  Protected TLSMode, ClientID, *ctx, *cfg, *Error, SockOpt.l, SockOptLen.l
  
  If Mode & #PB_Network_TLSv1
    TLSMode = (Mode & #PB_Network_TLSv1) >> 3
    Mode    = Mode & ($FFFFFFFF - #PB_Network_Extra)
  EndIf
  
  If TLSG\DLL And TLSMode
    *cfg = tls_config_new()
    If *cfg = 0
      TLSG\LastError = #TLS_Error_NewConfig_Failed
    Else
      If TLSG\CertFile$ = "" And TLSG\KeyFile$ = "" And TLSG\CaCertFile$ = ""
        tls_config_insecure_noverifycert(*cfg)
        tls_config_insecure_noverifyname(*cfg)
      Else
        If TLSG\CertFile$
          If tls_config_set_cert_file(*cfg, TLSG\CertFile$) = -1
            TLSG\LastError = #TLS_Error_CantLoad_CertFile
          EndIf
        EndIf
        If TLSG\KeyFile$
          If tls_config_set_key_file(*cfg, TLSG\KeyFile$) = -1
            TLSG\LastError = #TLS_Error_CantLoad_KeyFile
          EndIf
        EndIf
        If TLSG\CaCertFile$
          If tls_config_set_ca_file(*cfg, TLSG\CaCertFile$) = -1
            TLSG\LastError = #TLS_Error_CantLoad_RootCert
          EndIf
        EndIf
      EndIf
      If tls_config_set_protocols(*cfg, TLSMode) = -1
        TLSG\LastError = #TLS_Error_Unsupported_Protocol
      EndIf
      *ctx = tls_client()
      If *ctx = 0
        TLSG\LastError = #TLS_Error_Cant_Start_Client
      ElseIf tls_configure(*ctx, *cfg) = -1
        TLSG\LastError = #TLS_Error_Configure_Error
      Else
        *Error = tls_config_error(*cfg)
        If *Error
          Debug "TLS Config error: " + PeekS(*Error, - 1, #PB_UTF8)
        EndIf
        tls_config_free(*cfg)
        ClientID = OpenNetworkConnection(ServerName$, Port, Mode, TimeOut, LocaleIP$, LocalePort)
        If ClientID
          tls_connect_socket(*ctx, ConnectionID(ClientID), ServerName$)
          LockMutex(TLSG\muxClient) 
          AddMapElement(TLSG\Clients(), Str(ClientID))
          TLSG\Clients()\ctx = *ctx 
          UnlockMutex(TLSG\muxClient) 
        EndIf
      EndIf
    EndIf
  Else
    ClientID = OpenNetworkConnection(ServerName$, Port, Mode, TimeOut, LocaleIP$, LocalePort)
  EndIf
  
  ProcedureReturn ClientID
EndProcedure

Procedure TLS_ReceiveNetworkData(ClientID, Buffer, Length)
  Protected Result,*client.TLS_Connections 
  
  
  LockMutex(TLSG\muxClient) 
  
  *client = FindMapElement(TLSG\Clients(), Str(ClientID))
    
  If *client 
    Result = tls_read(*client\ctx, Buffer, Length)
  Else
    Result = ReceiveNetworkData(ClientID, Buffer, Length)
  EndIf
  
  UnlockMutex(TLSG\muxClient)
   
  ProcedureReturn Result
  
EndProcedure

Procedure TLS_SendNetworkData(ClientID, Buffer, Length)
  Protected Result,*client.TLS_Connections
  
  LockMutex(TLSG\muxClient) 
  
  *client = FindMapElement(TLSG\Clients(), Str(ClientID))
  
  If *client   
    Result = tls_write(*client\ctx, Buffer, Length)
  Else
    Result = SendNetworkData(ClientID, Buffer, Length)
  EndIf
  
  UnlockMutex(TLSG\muxClient)
  
  ProcedureReturn Result
EndProcedure

Procedure TLS_SendNetworkString(ClientID, String$, Format)
  Protected Result, *Buffer,*client.TLS_Connections
  
  LockMutex(TLSG\muxClient) 
  
  *client = FindMapElement(TLSG\Clients(), Str(ClientID))
  
  If *client 
    Select Format
      Case #PB_Ascii
        *Buffer = Ascii(String$)
        Result  = tls_write(*client\ctx, *Buffer, MemorySize(*Buffer) - 1)
        FreeMemory(*Buffer)
      Case #PB_Unicode
        Result = tls_write(*client\ctx, @String$, StringByteLength(String$))
      Case #PB_UTF8
        *Buffer = UTF8(String$)
        Result  = tls_write(*client\ctx, *Buffer, MemorySize(*Buffer) - 1)
        FreeMemory(*Buffer)
    EndSelect
  Else
    Result = SendNetworkString(ClientID, String$, Format)
  EndIf
  
  UnlockMutex(TLSG\muxClient)
  
  ProcedureReturn Result
EndProcedure

Procedure TLS_CloseNetworkConnection(ClientID)
  Protected *client.TLS_Connections,key.s 
  key =  Str(ClientID)
  
  LockMutex(TLSG\muxClient) 
  
  *client = FindMapElement(TLSG\Clients(), key)
  If *client   
    
    If *client\ctx 
      tls_close(*client\ctx)
      tls_free(*client\ctx)
    EndIf
    CloseNetworkConnection(ClientID)
    
    DeleteMapElement(TLSG\Clients(),key)
  Else
    CloseNetworkConnection(ClientID)
  EndIf
  
  UnlockMutex(TLSG\muxClient)
  
EndProcedure

Procedure Init_TLS(Domain$="",CertFile$ = "", KeyFile$ = "", CaCertFile$ = "",caPath$="")
  
  Protected Result = #TLS_Error_InitFailed
  
    If tls_init() = 0
      TLSG\DLL = 1 
      AddElement(TLSG\certs())
      tlsg\certs()\Path$ = caPath$
      TLSG\certs()\domain$ = Domain$ 
      TLSG\certs()\CertFile$ = CertFile$ 
      TLSG\certs()\KeyFile$ = KeyFile$
      TLSG\certs()\CaCertFile$ = CaCertFile$ 
      If TLSG\CertFile$ <> "" 
        TLSG\CertFile$ = CertFile$  
        TLSG\KeyFile$ = KeyFile$ 
        TLSG\CaCertFile$ = CaCertFile$ 
        TLSG\domain$ = Domain$ 
      EndIf   
      Result  = #TLS_Error_None
    EndIf
   
  ProcedureReturn Result
EndProcedure

Macro OpenNetworkConnection(ServerName, Port, Mode = #PB_Network_TCP | #PB_Network_IPv4, TimeOut = 0, LocaleIP = "", LocalePort = 0)
  TLS_OpenNetworkConnection(ServerName, Port, Mode, TimeOut, LocaleIP, LocalePort)
EndMacro

Macro CloseNetworkConnection(ClientID)
  TLS_CloseNetworkConnection(ClientID)
EndMacro

Macro ReceiveNetworkData(ClientID, Buffer, Length)
  TLS_ReceiveNetworkData(ClientID, Buffer, Length)
EndMacro

Macro SendNetworkData(ClientID, Buffer, Length)
  TLS_SendNetworkData(ClientID, Buffer, Length)
EndMacro

Macro SendNetworkString(ClientID, String, Format = #PB_UTF8)
  TLS_SendNetworkString(ClientID, String, Format)
EndMacro

Macro CreateNetworkServer(Server, Port, Mode = #PB_Network_TCP | #PB_Network_IPv4, BindedIP = "")
  TLS_CreateNetworkServer(Server, Port, Mode, BindedIP)
EndMacro

Macro CloseNetworkServer(Server)
  TLS_CloseNetworkServer(Server)
EndMacro

Macro NetworkServerEvent(ServerID = -1)
  TLS_NetworkServerEvent(ServerID)
EndMacro

CompilerIf #TLS_AUTOINIT
  __MyInit()
CompilerEndIf

tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Client/Server TCP TLS communication problem

Post by tatanas »

Before you respond to this post idle, I tryed to replace the recv_() function by tls_read() this way :

Code: Select all

PrototypeC Proto_tls_read(ctx, *buf, buflen.i)
OpenLibrary(0, "/usr/lib/x86_64-linux-gnu/libtls.so.26.0.1")
tls_read.Proto_tls_read = GetFunction(0, "tls_read")


...


Repeat

	#FIONREAD = $541B
	Result = ioctl_(SocketHandle, #FIONREAD, @Length) ; Length max = 65536 - https://flylib.com/books/en/1.397.1.75/1/

	If Result < 0 ; error
		AmountRead  = -1
		ErrorNumber = PeekL(errno_location())
		Break
		
	ElseIf Length > 0
		
		If Length > MemLength
			Length = MemLength
		EndIf
		
		; ---------------------------------
		;   loop until we got all data
		; ---------------------------------
		While Length > 0
         	Result = tls_read(SocketHandle,*MemoryID + AmountRead, Length)
; 			Result = recv_(SocketHandle,*MemoryID + AmountRead, Length, 0)
		
			If Result > 0
				AmountRead = AmountRead + Result
				Length     = Length     - Result
			Else
				AmountRead  = -1
				Break
			EndIf
		Wend
		Break 

	Else ; nothing to read
		Delay(SocketTimeout)
		AttemptCount = AttemptCount + 1
		If AttemptCount > MaxReadTrys
			Break
		EndIf
	EndIf
	
ForEver

; ----------------------------
;   All Data received
; ----------------------------

Select AmountRead
	Case 0
		; nothing to read
		
	Case 1 To 65535
	
	...
But it doesn't work. I guess it's not that simple.
Windows 10 Pro x64
PureBasic 6.20 x64
Fred
Administrator
Administrator
Posts: 18178
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Client/Server TCP TLS communication problem

Post by Fred »

you can't import another lib like that, it's not the same one that PB use. Just use the ImportC section from idle and put nothing in parameter:

Code: Select all

ImportC ""
  tls_read(ctx,*buf,buflen.i) 
EndImport
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Client/Server TCP TLS communication problem

Post by tatanas »

Unfortunately, the server continues to crash as soon as it receives data (call to tls_read()). With or without "libpbtls.a" as an ImportC parameter, the result is the same. The debugger doesn't indicate an explicit error message.
All the TCP functions I use are those of PureBasic except for recv_() because I wanted to be able to handle client disconnections in a precise and reactive way.
Windows 10 Pro x64
PureBasic 6.20 x64
User avatar
idle
Always Here
Always Here
Posts: 5884
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Client/Server TCP TLS communication problem

Post by idle »

a bit off topic but I think I still have problems with disconnections on my server.
the documentation for ReceiveNetworkData
Returns the number of bytes received. If 'Result' is equal to DataBufferLength then more data is available to be read. If an error occurred on the connection (link broken, connection close by the server etc...) 'Result' will be -1.


Though a socket will report, bytes read, 0 on drop , or -1 on error and it's the 0 part that seems to be missing and it might be cast as -1
but out of the socket errors these are the errors that are recoverable and that you should continue processing, I'm not sure if these error constants are the same on linux.

Code: Select all

  #WSA_IO_INCOMPLETE = 996
  #WSA_IO_PENDING = 997
  #WSAEINTR = 10004
  #WSAEMFILE = 10024
  #WSAEWOULDBLOCK = 10035
  #WSAEINPROGRESS = 10036
  #WSAEALREADY = 10037
  #TLS_WANT_POLLIN  = -2    ;libre TLS socket errors 
  #TLS_WANT_POLLOUT = -3  
 


the main issue is detecting disconnections.
infratec
Always Here
Always Here
Posts: 7604
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Client/Server TCP TLS communication problem

Post by infratec »

Btw. today I built a kind of slapd server on Linux with the PB inbuild TLS procedures.
It works without problems. Also the disconnect is always detected.
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Client/Server TCP TLS communication problem

Post by tatanas »

Thanks for the feedback.
I guess I would have to rework all the internal working of the server and only use the inbuild Network procedures.

EDIT : What means "-2" as a return result of ReceiveNetworkdata() ?
Windows 10 Pro x64
PureBasic 6.20 x64
User avatar
idle
Always Here
Always Here
Posts: 5884
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Client/Server TCP TLS communication problem

Post by idle »

Tls has errors want pollin and pollout these are recoverable as far as I understand. So add a delay and try again.
tatanas
Enthusiast
Enthusiast
Posts: 260
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: Client/Server TCP TLS communication problem

Post by tatanas »

Sorry but where should I put a delay ? When receiving on the server in the #PB_NetworkEvent_Data Event part ?
Windows 10 Pro x64
PureBasic 6.20 x64
User avatar
idle
Always Here
Always Here
Posts: 5884
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Client/Server TCP TLS communication problem

Post by idle »

This is more or less how I handle it

Code: Select all

Procedure NetworkErrorContinue(ID) 
  Protected ret,option.l,oplen.l=4 
  
  #WSA_IO_INCOMPLETE = 996
  #WSA_IO_PENDING = 997
  #WSAEINTR = 10004
  #WSAEMFILE = 10024
  #WSAEWOULDBLOCK = 10035
  #WSAEINPROGRESS = 10036
  #WSAEALREADY = 10037
  
  #_WANT_POLLIN  = -2
  #_WANT_POLLOUT = -3
    
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows 
    option = WSAGetLastError_()
  CompilerElse 
    CompilerIf #PB_Compiler_Backend = #PB_Backend_C
      !#include "errno.h"
      !extern int errno;
      !v_option=errno;
    CompilerElse
      option = PeekL(__errno_location()) 
    CompilerEndIf 
  CompilerEndIf
    
  Select option 
    Case 0 
      ret = 1
    Case  #WSAEWOULDBLOCK  
      ret = 1 
      Debug "#WSAEWOULDBLOCK"
    Case  #WSAEINPROGRESS   
      Debug "#WSAEINPROGRESS"
      ret = 1  
    Case  #WSAEALREADY  
      Debug "#WSAEALREADY"
      ret = 1  
    Case  #WSA_IO_INCOMPLETE
      Debug "#WSA_IO_INCOMPLETE"
      ret =1   
    Case  #WSA_IO_PENDING
      Debug "#WSA_IO_PENDING"
      ret = 1  
    Case  #WSAEMFILE 
      ret =1 
      Debug "#WSAEMFILE"
    Case #_WANT_POLLIN
      ret =1 
       Debug "#TLS_WANT_POLLIN"
    Case #_WANT_POLLOUT 
      ret = 1 
      Debug "#TLS_WANT_POLLOUT"
    Default 
      Debug option   
  EndSelect   
  
  ProcedureReturn ret 
  
EndProcedure  

;in your #PB_NetworkEvent_Data 

Repeat 
  again=0
  *buffer = ReAllocateMemory(*buffer, MaxRequest + BufferSize )  
  Result = ReceiveNetworkData(CLientid,*Buffer+MaxRequest,BufferSize)
  If result > 0   
    MaxRequest + result
    timeout = ElapsedMilliseconds() + 15000 
  ElseIf ElapsedMilliseconds() > timeout  
    Break 
  ElseIf NetworkErrorContinue(clientid)    
    Delay(1)
    again =1 
  Else 
    Break 
  EndIf
Until (result <> BufferSize And again=0) 

User avatar
HeX0R
Addict
Addict
Posts: 1198
Joined: Mon Sep 20, 2004 7:12 am
Location: Hell

Re: Client/Server TCP TLS communication problem

Post by HeX0R »

It's really time for a native GetNetworkError() function.
Quin
Addict
Addict
Posts: 1132
Joined: Thu Mar 31, 2022 7:03 pm
Location: Colorado, United States
Contact:

Re: Client/Server TCP TLS communication problem

Post by Quin »

HeX0R wrote: Tue Mar 18, 2025 2:20 pm It's really time for a native GetNetworkError() function.
Fully agreed, PB's networking library is honestly a bit of a joke without it.
Post Reply