aboutsummaryrefslogblamecommitdiffstats
path: root/erts/preloaded/src/socket.erl
blob: 68b7d3f4b06801cba42316aa62b78105528479b2 (plain) (tree)





















                                                                           

                                     


                                                

                          



                                
                        
                              

                            
 
                                
                                      


                                                                                          
                                        
                                                        

                      

                 
                    
 
                  



                    







                         
                            


                            



                             




                             

                          









                                   
                                  
 
                        
                            
                        
                               
                            

                                 
                          
                              
                                     
                                 
                             
                             

 



                             
 



                       

                






                                 



                                           
                                    
                                                         

                                             



                                                                           
 

                                



                                                        

                                  
 








                      

          
  


                                                             


                                                                    
           
 









                                                                         










                                                                       
 

                                                       






                                                          
 

                                                










                                                         


                                                       

                                       










                                                                 
                                                          










                                                          
 
                                                


                                                
 























                                                                                 
 

                                                                
                                                       
                                      

                                                                     
                                                                                 
                                                  

                                 
                                                             









                                                       
                                                  











                                              
                                 







                                    
                                   
                                   
                                   

                                      

                                   





                                      

                                   

                                    
 


                     

                                  
                                      
                         


                                                   
                                      





























                                                    
                  
                        


                    
                         















                           

                    
                               








                                          
                                    
                                    
                                       

                                         
                                      





                                           
 

                                  

































                                

                                    







                                             
                                           
                                            
 

                                      
                              






                                           




                                








                                                                      

                                    


                                
                            
 

                                                  





















                                                                            
 














                                                   

                                  


                                          
                                                 


                                        



                                        
 

                                                 
                                           
                                                                     

                                          



                                          
 

                                               
 






                                            
 


                                            
 
                                            
                                                         
                                            
                                            
                                               
                                            

                                            
                                               

                                            
                                               
                                            
                                               





                                               
                                            
                                            
                                            
                                            
                                               
                                                         

                                               
                                            
                                            
                                            
                                            
 
                                                 

                                                 
                                                              
                                                 
                                                 
                                                 
                                                 
                                                 
                                                 
                                                 
                                                 
                                                 
                                                 
                                                 
                                                 
                                                 
                                                              
                                                 


                                                           
                                                 
                                                 
                                                 
                                                 
                                                 


                                                    
                                                 


                                                    
                                               
                                               
                                                           

                                                            
                                               
                                               


                                                            
                                               
                                               
                                               


                                                            
                                               
                                               
                                               
                                               
                                               

                                                            
                                               

                                                                     
                                               
                                               
                                                            
                                               
                                                            
                                               

                                           
                                           










                                              
 


                                                       
                                                    








                                                       
                                                    
                                                       



                                                       
                                                    

                                                       
                                                    






                                                       
                                                    


                                                       
 


                                           

 
 



























                                                                              

                                                                                    















                                                                              

                                                                              
                                              
  




















                                                                     








                                                                      








                                                                       
                                                                               


























                                                                  
 





                                                               





























                                                                                
                                                    












                                                                          
                                                                        








                                                                                 

                                                                              
                                 
  
 



                                                     
 
                                  
                                                     








                                                           
                                                                 

             
                                                    





                      
 


                                                                              















                                                                    
                                            


                                                                              


                                                         






                                            














                                   
































                                                                              
                                              
  
 

                                                            
                             
                         
 

                                        
 

                                                                     
                             

                            
 


                                                                         
                                    

                                                      

                                                                             

                                                             
                                          







                                                       













                                                                                

                                                    
                                                      






                                    


                                                                              
                                              
  
 
                                                 
                          




                                                   




                                                          
                                       

                                                      


 
 

                                                                              
                                                    
  
 
                                                            
                          



                          
                                                   
 





                                                                     
                                       
                                                                     
                     
                                        
                                                           
                                 
 
                               


                                        
                        









                                                                                   
                                            
                         
 
                          


                                                                   
                                                   
                   
                                                          
                                                                   



                                              
                               

                                                         
                                    




                                                                                  

        
 
 

                                                                              
                                                     
  
 




                                                     
                     
                                                                                 
 










                                                                       
                                                
                                                            


                                                            









                                                                     

                                                  
                                   
                                            



                                          

                                                    
               
                        
                                                   



                                                                              
                                                  

                                                           
                                                  




                                                       

                                                       
                                                        
                                                  



                                                           
                                                  




                                                       
                            
                                                       
                                                        
                                                  






                             

 
                                                                              
  
 






                                             


                                                             






                                                                    

                                                                      
 
                                                                             

                          
                                   
                              

                           
 
                                                                
                                      

                                                          
                              
                              
                             

                                                             

                                                                             
                              
                                                                              
                             
                                                             
                                   
                                                    
 
                                                  

                                 
                                                            
             


                          




                                                                              
                                                          

                                                           
                                                          




                                                         
                            
                                                         


                                                        
 
                          

                                                           
                                                           

                                                        
                                                         






                                                        
 

 

                                                                              

                                                                    
                               




                                  
































































                                                                              
                                                            
  


















                                                                           




                                                       


                    





                                                               




                                       












                                                                                











                                                                               
 
                                                    



                                                             
                                                               
 



                                                                              
                                                       















                                                                           
                                     
                                   









                                                               
                                             
                                                     
                                





                                                       










                                                          
                                             
                                                     
                                                       





                                                       




                                                        
 

                                                                      
                      
 





                                                                            
                                             
                                           
                                




                                                       





                                                        








                                          







                                                    
                                                                 

                                                                             

                                       
              
                                                                                    
                            
                                                                

                     

 

                                                                              










                                                                      
 





                                                                     
                   

                        






                                                                            




                                           























                                                                 







                                                                 

                                                                 



                                     
                                          








                                                             
 



                                                         
                                         








                                                                            




                                                           





                                                           
                                   


                 
 
 
 


                                                                              
                                                                      
                            
                            
                                



                          






                                                                              

                                                                              
                                  
  




                                                
                                


                      


















                                                               


 

                                                                              

























                                                                              






                                                                             

          
                                                               


                                                   
 
                                                                                  
                         

                       
                                                                                       
                         

                       
                                                                                      
                         

                       
                                                                                          
                         

                       
                                                                                        
                         

                       
                                                                                        
                         

                       
                                                                                          
                         


                       
                                                    

             


                                                           



                                                                               








                                           
 
 

                                                                              






                                                                  



                                                                                
 
                                                                                    
                         

                       
                                                                                         
                         

                       
                                                                                        
                         

                       
                                                                                            
                         

                       
                                                                                          
                         

                       
                                                                                          
                         

                       
                                                                                            
                         
                       





                                                                                 
                                                  

                          
 
                                             

             


                                               
                                                           
                                                                          
                                                                   
                                                                    
                                                                 

                       
                             

                                                                   












                                           





























                                                                           
 






























                                                                              


                                                                              






















                                                                              

                                                                   






                                                       




                                                                  

 





                                                       










                                                             


                                                         
                                                       















                                                            







                                                               
                        
                                  
                           
                                     
                       
                                 
                         
                                   
                        
                                  
                        
                                  

                                   
                                                                    

                                         
               

 
                            

                                                                                 










                                                                                  



                                                                  
 

                                                              

                                                            

                                                                        


                                                

                                                                        

                                                                        

                                                                    

                                                                        

                                                                        




                                                                        

                                                                        

                                                                       

                                                                       

                                                                     

                                                                       


                                                                                

                                                                        

                                                                        

                                                                     

                                                                       


                                                                                

                                                                        


                                                   
                                                       
                                                                        
                                                      

                                                                       














                                                                                
                                                                         
                                                      

                                                                       






                                                                                 

                                                                   

                                                                  

                                                                 











                                                                   






                                                 


                                                  


                                                                  


                                                   


                                                           


                                             


                                            


                                                


                                            


                                           


                                             


                                                    


                                            


                                            


                                            



                                                 

                                 
                                 
                                 

                       


                                                


                                        






                                                                         


                                               

                                                         




                                                                          





                                                                      


                                                                  




                                                                           


                                                                  


                                                                  


                                                                  

                                                                






                                                   





                                                           


                                                   


                                                     


                                              



                                                                


                                                  

                                                                  
      





                                                           

                                                                   


                                                 
                                              



                             




                                          




                                           


                                                
                                        
                                                                 
      


                                                 














                                                                   
                                               
                                                             
      


                                                       



















                                                                            

                      










                                                                   


                                                             
                                             
                                            
      












                                                       
 


                                                 
                                      

                                                                    


      
 


                              


                            







                                                             



                                                                        
 


                                            


 







                                                                       
                             
                                       





                                                                       
                             
                                   





                                                                       
                             
                                      





                                                                       
                             
                                        





                                                                       
                             
                                       





                                                                       
                             
                                       





                                                                       
                             
                                        





                                                                       
                       
                             









                                                                       









                                          



                                                        

                                


                                                                          
                            
                                                     
                                             
                                        

                                                                      

                                                                      

                                                                 

                                                               

                                                                 

                                                              

                                                                   






                                                                   

                                                            

                                                                   

                                                                

                                                                          
                                                                      
                            

                                                             

                                                                   

                                                           

                                                                   
                         

                                                                  

                                                                  
                                                                   
                               
                                                               
                                             
                               



                                                                    
                                                                
                            

                                                                   
                           

                                                                  

                                                                  

                                                                   

                                                              

                                                            

                            

                                                                          



                                                                                 

                                                                             

                                                            

                                                                           

                                                                                  
              

                                                              

                                                                   

                                                                  

                                                                    

                                                               

                                                                  

                                                                   

                                                                  

                                                                    

                                                                   

                                                                    

                                                           

                                                                     

                                                                                   

                                                             


                                                        

                                                                                 

                                                                     

                                                        

                                                                                

                                                                                

                                                                   




                                                         

                                                                 

                                                    

                                                                          

                                                        

                      

                                                                      

                                                                       
                                                            
                                            
                             



                                                                

                                                                        
                                                           
                                          
                             



                                                                       
                                                            
                                          
                             
                                                       

                                          
                                                            
                                            
                             





                                                                  

                                                           

                                                                    

                                                                      
                                                                 
                                          
                                  

                                                                      

                                                               

                                                                

                                                               



                                                                

                                                                

                                                                                    
                                                          
                                            
                           

                                                            

                                                                    

                                                                 

                                                              
                                                          
                             

                     



                                                            

                                                         





                                                              

                                                        

                                                           

                                                         







                                                                              

                                                         

                     

                                                      

                                                         

                      

                                                                    

                                                                 









                                                                     

                                                            





                                                                         

                                                                         

                                                                

                                                                    





                                                                         

                                                               



                                                                           

                                                              

                                                              

                                                          









                                                                            

                                                               

                                                                           

                                                                  

                                                                      

                                                          
 

                                                   

                                                   


                                                                            

                                                               
        





                                                               









                                    





                                                                              






                                                                        



















                                                                              








                                         























                                                                                  












                                     
                                      
            
                            












                                                  

                
 

                           
 



                                                             


 











                                                                              


                  


                           














                                                                              
                             


                                       

                         
                                










                                 
                                              

                         
                                                    

                         
                                             

                         
                                                 

                         
                               




                         


                            


                            
                                             

                         
                                       
                         
 





                         
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2018-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%

-module(socket).

-compile({no_auto_import,[error/1]}).

%% Administrative and "global" utility functions
-export([
	 on_load/0, on_load/1, on_load/2,
	 info/0,
         ensure_sockaddr/1
        ]).

-export([
         open/2, open/3, open/4,
         bind/2, bind/3,
         connect/2, connect/3,
         listen/1, listen/2,
         accept/1, accept/2,

         send/2, send/3, send/4,
         sendto/3, sendto/4, sendto/5,
         %% sendmsg/4,
         %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv)

         recv/1, recv/2, recv/3, recv/4,
         recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4,
         %% recvmsg/4,
         %% readv/3,

         close/1,
         shutdown/2,

         setopt/4,
         getopt/3,

         sockname/1,
         peername/1
        ]).

-export_type([
              domain/0,
              type/0,
              protocol/0,
              socket/0,

              port_number/0,
              ip_address/0,
              ip4_address/0,
              ip6_address/0,
              sockaddr/0,
              sockaddr_in4/0,
              sockaddr_in6/0,
              sockaddr_un/0,

              accept_flags/0,
              accept_flag/0,

              send_flags/0,
              send_flag/0,

              shutdown_how/0,

              sockopt_level/0,
              otp_socket_option/0,
              socket_option/0,
              ip_socket_option/0,
              ipv6_socket_option/0,
              tcp_socket_option/0,
              udp_socket_option/0,
              sctp_socket_option/0,
              raw_socket_option/0,

              timeval/0,
              ip_tos_flag/0,
              ip_mreq/0,
              ip_mreq_source/0,
              ip_pmtudisc/0,
              ip_msfilter_mode/0,
              ip_msfilter/0,
              ipv6_mreq/0,
              ipv6_pmtudisc/0,
              sctp_event_subscribe/0,
              sctp_assocparams/0,
              sctp_initmsg/0,
              sctp_rtoinfo/0,


              msghdr_flag/0,
              msghdr_flags/0,
              msghdr/0,
              cmsghdr/0,

              uint8/0,
              uint16/0,
              uint20/0,
              uint32/0
             ]).


-type uint8()  :: 0..16#FF.
-type uint16() :: 0..16#FFFF.
-type uint20() :: 0..16#FFFFF.
-type uint32() :: 0..16#FFFFFFFF.


%% We support only a subset of all domains.
-type domain() :: local | inet | inet6.

%% We support only a subset of all types.
%% RDM - Reliably Delivered Messages
-type type()   :: stream | dgram | raw | rdm | seqpacket.

%% We support only a subset of all protocols:
%% Note that the '{raw, integer()}' construct is intended
%% to be used with type = raw.
%% Note also that only the "superuser" can create a raw socket.
-type protocol() :: ip | tcp | udp | sctp | icmp | igmp | {raw, integer()}.

-type port_number() :: 0..65535.

-type ip_address() :: ip4_address() | ip6_address().

-type ip4_address() :: {0..255, 0..255, 0..255, 0..255}.

-type in6_flow_info() :: uint20().
-type in6_scope_id()  :: uint32().

-type ip6_address() ::
           {0..65535,
            0..65535,
            0..65535,
            0..65535,
            0..65535,
            0..65535,
            0..65535,
            0..65535}.

%% <KOLLA>
%%
%% Should we do these as maps instead?
%% If we do we may need to include the family (domain) in the
%% map (as the native type do. See struct sockaddr_in6).
%%
%% What about default values? Such as for port (=0) and addr (=any)?
%%
%% </KOLLA>

-type timeval() :: #{sec  := integer(),
                     usec := integer()}.

%% If the integer value is used its up to the caller to ensure its valid!
-type ip_tos_flag() :: lowdeley |
                       throughput |
                       reliability |
                       mincost |
                       integer().

%% This type is used when requesting to become member of a multicast
%% group with a call to setopt. Example: 
%%
%%     socket:setopt(Socket, ip, add_membership, #{multiaddr => Addr,
%%                                                 interface => any}).
%%
%% Its also used when removing from a multicast group. Example:
%%
%%     socket:setopt(Socket, ip, drop_membership, #{multiaddr => Addr,
%%                                                  interface => any}).
%%

-type ip_mreq() :: #{multiaddr := ip4_address(),
                     interface := any | ip4_address()}.
%% -type ip_mreqn() :: #{multiaddr := ip4_address(),
%%                       address   := any | ip4_address(),
%%                       ifindex   := integer()}.

-type ip_mreq_source() :: #{multiaddr  := ip4_address(),
                            interface  := ip4_address(),
                            sourceaddr := ip4_address()}.

-type ip_pmtudisc() :: want | dont | do | probe.

%% multiaddr: Multicast group address
%% interface: Address of local interface
%% mode:      Filter mode
%% slist:     List of source addresses
-type ip_msfilter_mode() :: include | exclude.

-type ip_msfilter() :: #{multiaddr => ip4_address(),
                         interface => ip4_address(),
                         mode      => ip_msfilter_mode(),
                         slist     => [ip4_address()]}.

-type ipv6_mreq() :: #{multiaddr := ip6_address(),
                       interface := non_neg_integer()}.

-type ipv6_pmtudisc() :: ip_pmtudisc().

-type sctp_event_subscribe() :: #{data_in          := boolean(),
                                  association      := boolean(),
                                  address          := boolean(),
                                  send_failure     := boolean(),
                                  peer_error       := boolean(),
                                  shutdown         := boolean(),
                                  partial_delivery := boolean(),
                                  adaptation_layer := boolean(),
                                  authentication   := boolean(),
                                  sender_dry       := boolean()}.

-type sctp_assocparams() :: #{assoc_id       := integer(),
                              max_rxt        := uint16(),
                              num_peer_dests := uint16(),
                              peer_rwnd      := uint32(),
                              local_rwnd     := uint32(),
                              cookie_life    := uint32()}.

-type sctp_initmsg() :: #{num_outstreams := uint16(),
                          max_instreams  := uint16(),
                          max_attempts   := uint16(),
                          max_init_timeo := uint16()
                         }.

-type sctp_rtoinfo() :: #{assoc_id := integer(),
                          initial  := uint32(),
                          max      := uint32(),
                          min      := uint32()}.

-type sockaddr_un()  :: #{family := local,
                          path   := binary() | string()}.
-type sockaddr_in4() :: #{family := inet,
                          port   := port_number(),
                          addr   := any | loopback | ip4_address()}.
-type sockaddr_in6() :: #{family   := inet6,
                          port     := port_number(),
                          addr     := any | loopback | ip6_address(),
                          flowinfo := in6_flow_info(),
                          scope_id := in6_scope_id()}.
-type sockaddr() :: sockaddr_in4() |
                    sockaddr_in6() |
                    sockaddr_un().

-define(SOCKADDR_IN4_DEFAULTS(A), #{port => 0,
                                    addr => A}).
-define(SOCKADDR_IN4_DEFAULTS,    ?SOCKADDR_IN4_DEFAULTS(any)).
-define(SOCKADDR_IN4_DEFAULT(A),  (?SOCKADDR_IN4_DEFAULTS(A))#{family => inet}).
-define(SOCKADDR_IN6_DEFAULTS(A), #{port     => 0,
                                    addr     => A,
                                    flowinfo => 0, 
                                    scope_id => 0}).
-define(SOCKADDR_IN6_DEFAULTS,    ?SOCKADDR_IN6_DEFAULTS(any)).
-define(SOCKADDR_IN6_DEFAULT(A),  (?SOCKADDR_IN6_DEFAULTS(A))#{family => inet6}).

%% otp    - The option is internal to our (OTP) imeplementation.
%% socket - The socket layer (SOL_SOCKET).
%% ip     - The IP layer (SOL_IP or is it IPPROTO_IP?).
%% ipv6   - The IPv6 layer (SOL_IPV6).
%% tcp    - The TCP (Transport Control Protocol) layer (IPPROTO_TCP).
%% udp    - The UDP (User Datagram Protocol) layer (IPPROTO_UDP).
%% sctp   - The SCTP (Stream Control Transmission Protocol) layer (IPPROTO_SCTP).
%% Int    - Raw level, sent down and used "as is".
-type sockopt_level() :: otp |
                         socket |
                         ip | ipv6 | tcp | udp | sctp | raw |
                         non_neg_integer().

%% There are some options that are 'read-only'.
%% Should those be included here or in a special list?
%% Should we just document it and leave it to the user?
%% Or catch it in the encode functions?
%% A setopt for a readonly option leads to einval?

-type otp_socket_option() :: debug |
                             iow |
                             controlling_process |
                             rcvbuf |
                             sndbuf.
%% Shall we have special treatment of linger??
%% read-only options:
%% domain | protocol | type.
%% FreeBSD (only?): acceptfilter
-type socket_option() :: acceptconn |
                         acceptfilter |
                         bindtodevice |
                         broadcast |
                         busy_poll |
                         debug |
                         domain |
                         dontroute |
                         error |
                         keepalive |
                         linger |
                         mark |
                         oobinline |
                         passcred |
                         peek_off |
                         peekcred |
                         priority |
                         protocol |
                         rcvbuf |
                         rcvbufforce |
                         rcvlowat |
                         rcvtimeo |
                         reuseaddr |
                         reuseport |
                         rxq_ovfl |
                         setfib |
                         sndbuf |
                         sndbufforce |
                         sndlowat |
                         sndtimeo |
                         timestamp |
                         type.

%% Read-only options:
%% mtu
%%
%% Options only valid on FreeBSD?:
%% dontfrag
%% Options only valid for RAW sockets:
%% nodefrag (linux only?)
-type ip_socket_option() :: add_membership |
                            add_source_membership |
                            block_source |
                            dontfrag |
                            drop_membership |
                            drop_source_membership |
                            freebind |
                            hdrincl |
                            minttl |
                            msfilter |
                            mtu |
                            mtu_discover |
                            multicast_all |
                            multicast_if |
                            multicast_loop |
                            multicast_ttl |
                            nodefrag |
                            options |
                            pktinfo |
                            recverr |
                            recvif |
                            recvdstaddr |
                            recvopts |
                            recvorigdstaddr |
                            recvtos |
                            recvttl |
                            retopts |
                            router_alert |
                            sndsrcaddr |
                            tos |
                            transparent |
                            ttl |
                            unblock_source.
-type ipv6_socket_option() ::
        addrform |
        add_membership |
        authhdr |
        auth_level |
        checksum |
        drop_membership |
        dstopts |
        esp_trans_level |
        esp_network_level |
        faith |
        flowinfo |
        hoplimit |
        hopopts |
        ipcomp_level |
        join_group |
        leave_group |
        mtu |
        mtu_discover |
        multicast_hops |
        multicast_if |
        multicast_loop |
        portrange |
        pktoptions |
        recverr |
        recvpktinfo | pktinfo |
        recvtclass |
        router_alert |
        rthdr |
        tclass |
        unicast_hops |
        use_min_mtu |
        v6only.

-type tcp_socket_option()  :: congestion |
                              cork |
                              info |
                              keepcnt |
                              keepidle |
                              keepintvl |
                              maxseg |
                              md5sig |
                              nodelay |
                              noopt |
                              nopush |
                              syncnt |
                              user_timeout.

-type udp_socket_option() :: cork.

-type sctp_socket_option() ::
        adaption_layer |
        associnfo |
        auth_active_key |
        auth_asconf |
        auth_chunk |
        auth_key |
        auth_delete_key |
        autoclose |
        context |
        default_send_params |
        delayed_ack_time |
        disable_fragments |
        hmac_ident |
        events |
        explicit_eor |
        fragment_interleave |
        get_peer_addr_info |
        initmsg |
        i_want_mapped_v4_addr |
        local_auth_chunks |
        maxseg |
        maxburst |
        nodelay |
        partial_delivery_point |
        peer_addr_params |
        peer_auth_chunks |
        primary_addr |
        reset_streams |
        rtoinfo |
        set_peer_primary_addr |
        status |
        use_ext_recvinfo.

-type raw_socket_option() :: filter.

%% -type plain_socket_option()  :: integer().
%% -type sockopt() :: otp_socket_option() |
%%                    socket_option() |
%%                    ip_socket_option() |
%%                    ipv6_socket_option() |
%%                    tcp_socket_option() |
%%                    udp_socket_option() |
%%                    sctp_socket_option() |
%%                    raw_socket_option() |
%%                    plain_socket_option().

-record(socket, {ref :: reference()}).

-opaque socket() :: #socket{}.

-type accept_flags() :: [accept_flag()].
-type accept_flag()  :: nonblock | cloexec.

-type send_flags() :: [send_flag()].
-type send_flag()  :: confirm |
                      dontroute |
                      eor |
                      more |
                      nosignal |
                      oob.

%% Extend with OWN flags for other usage:
%%   - adapt-buffer-sz:
%%     This will have the effect that the nif recvfrom will use
%%     MSG_PEEK to ensure no part of the message is lost, but if
%%     necessary adapt (increase) the buffer size until all of
%%     it fits.
%%
%% Note that not all of these flags is useful for every recv function!
%%
-type recv_flags() :: [recv_flag()].
-type recv_flag()  :: cmsg_cloexec |
                      errqueue |
                      oob |
                      peek |
                      trunc.

-type shutdown_how() :: read | write | read_write.

%% These are just place-holder(s) - used by the sendmsg/recvmsg functions...
-type msghdr_flag()  :: eor | trunc | ctrunc | oob | errqueue.
-type msghdr_flags() :: [msghdr_flag()].
-type msghdr() :: #{
                    %% *Optional* target address
                    %% *If* this field is specified for an unconnected
                    %% socket, then it will be used as destination for the 
                    %% datagram.
                    target => sockaddr(),
                    
                    iov    => [binary()],
                    
                    ctrl   => cmsghdr(),

                    %% Only valid with recvmsg
                    flags  => msghdr_flags()
                   }.
-type cmsghdr() :: #{
                     level => protocol(),
                     type  => integer(),
                     data  => binary()
                    }.

-define(SOCKET_DOMAIN_LOCAL, 1).
-define(SOCKET_DOMAIN_UNIX,  ?SOCKET_DOMAIN_LOCAL).
-define(SOCKET_DOMAIN_INET,  2).
-define(SOCKET_DOMAIN_INET6, 3).

-define(SOCKET_TYPE_STREAM,    1).
-define(SOCKET_TYPE_DGRAM,     2).
-define(SOCKET_TYPE_RAW,       3).
%% -define(SOCKET_TYPE_RDM,       4).
-define(SOCKET_TYPE_SEQPACKET, 5).

-define(SOCKET_PROTOCOL_IP,    1).
-define(SOCKET_PROTOCOL_TCP,   2).
-define(SOCKET_PROTOCOL_UDP,   3).
-define(SOCKET_PROTOCOL_SCTP,  4).
-define(SOCKET_PROTOCOL_ICMP,  5).
-define(SOCKET_PROTOCOL_IGMP,  6).

-define(SOCKET_LISTEN_BACKLOG_DEFAULT, 5).

-define(SOCKET_ACCEPT_TIMEOUT_DEFAULT, infinity).

-define(SOCKET_SEND_FLAG_CONFIRM,    0).
-define(SOCKET_SEND_FLAG_DONTROUTE,  1).
-define(SOCKET_SEND_FLAG_EOR,        2).
-define(SOCKET_SEND_FLAG_MORE,       3).
-define(SOCKET_SEND_FLAG_NOSIGNAL,   4).
-define(SOCKET_SEND_FLAG_OOB,        5).

-define(SOCKET_SEND_FLAGS_DEFAULT,     []).
-define(SOCKET_SEND_TIMEOUT_DEFAULT,   infinity).
-define(SOCKET_SENDTO_FLAGS_DEFAULT,   []).
-define(SOCKET_SENDTO_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT).

-define(SOCKET_RECV_FLAG_CMSG_CLOEXEC, 0).
-define(SOCKET_RECV_FLAG_ERRQUEUE,     1).
-define(SOCKET_RECV_FLAG_OOB,          2).
-define(SOCKET_RECV_FLAG_PEEK,         3).
-define(SOCKET_RECV_FLAG_TRUNC,        4).

-define(SOCKET_RECV_FLAGS_DEFAULT,   []).
-define(SOCKET_RECV_TIMEOUT_DEFAULT, infinity).

-define(SOCKET_OPT_LEVEL_OTP,            0).
-define(SOCKET_OPT_LEVEL_SOCKET,         1).
-define(SOCKET_OPT_LEVEL_IP,             2).
-define(SOCKET_OPT_LEVEL_IPV6,           3).
-define(SOCKET_OPT_LEVEL_TCP,            4).
-define(SOCKET_OPT_LEVEL_UDP,            5).
-define(SOCKET_OPT_LEVEL_SCTP,           6).

-define(SOCKET_OPT_OTP_DEBUG,            1).
-define(SOCKET_OPT_OTP_IOW,              2).
-define(SOCKET_OPT_OTP_CTRL_PROC,        3).

-define(SOCKET_OPT_SOCK_ACCEPTCONN,      1).
%% -define(SOCKET_OPT_SOCK_ACCEPTFILTER,    2). % FreeBSD
-define(SOCKET_OPT_SOCK_BINDTODEVICE,    3).
-define(SOCKET_OPT_SOCK_BROADCAST,       4).
%% -define(SOCKET_OPT_SOCK_BUSY_POLL,       5).
-define(SOCKET_OPT_SOCK_DEBUG,           6).
-define(SOCKET_OPT_SOCK_DOMAIN,          7).
-define(SOCKET_OPT_SOCK_DONTROUTE,       8).
%% -define(SOCKET_OPT_SOCK_ERROR,           9).
-define(SOCKET_OPT_SOCK_KEEPALIVE,      10).
-define(SOCKET_OPT_SOCK_LINGER,         11).
%% -define(SOCKET_OPT_SOCK_MARK,           12).
-define(SOCKET_OPT_SOCK_OOBINLINE,      13).
%% -define(SOCKET_OPT_SOCK_PASSCRED,       14).
-define(SOCKET_OPT_SOCK_PEEK_OFF,       15).
%% -define(SOCKET_OPT_SOCK_PEEKCRED,       16).
-define(SOCKET_OPT_SOCK_PRIORITY,       17).
-define(SOCKET_OPT_SOCK_PROTOCOL,       18).
-define(SOCKET_OPT_SOCK_RCVBUF,         19).
%% -define(SOCKET_OPT_SOCK_RCVBUFFORCE,    20).
-define(SOCKET_OPT_SOCK_RCVLOWAT,       21).
-define(SOCKET_OPT_SOCK_RCVTIMEO,       22).
-define(SOCKET_OPT_SOCK_REUSEADDR,      23).
-define(SOCKET_OPT_SOCK_REUSEPORT,      24).
%% -define(SOCKET_OPT_SOCK_RXQ_OVFL,       25).
%% -define(SOCKET_OPT_SOCK_SETFIB,         26). % FreeBSD
-define(SOCKET_OPT_SOCK_SNDBUF,         27).
%% -define(SOCKET_OPT_SOCK_SNDBUFFORCE,    28).
-define(SOCKET_OPT_SOCK_SNDLOWAT,       29).
-define(SOCKET_OPT_SOCK_SNDTIMEO,       30).
-define(SOCKET_OPT_SOCK_TIMESTAMP,      31).
-define(SOCKET_OPT_SOCK_TYPE,           32).

-define(SOCKET_OPT_IP_ADD_MEMBERSHIP,         1).
-define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP,  2).
-define(SOCKET_OPT_IP_BLOCK_SOURCE,           3).
%% -define(SOCKET_OPT_IP_DONTFRAG,               4). % FreeBSD
-define(SOCKET_OPT_IP_DROP_MEMBERSHIP,        5).
-define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6).
-define(SOCKET_OPT_IP_FREEBIND,               7).
-define(SOCKET_OPT_IP_HDRINCL,                8).
-define(SOCKET_OPT_IP_MINTTL,                 9).
-define(SOCKET_OPT_IP_MSFILTER,              10).
-define(SOCKET_OPT_IP_MTU,                   11).
-define(SOCKET_OPT_IP_MTU_DISCOVER,          12).
-define(SOCKET_OPT_IP_MULTICAST_ALL,         13).
-define(SOCKET_OPT_IP_MULTICAST_IF,          14).
-define(SOCKET_OPT_IP_MULTICAST_LOOP,        15).
-define(SOCKET_OPT_IP_MULTICAST_TTL,         16).
-define(SOCKET_OPT_IP_NODEFRAG,              17).
%% -define(SOCKET_OPT_IP_OPTIONS,               18). % FreeBSD
-define(SOCKET_OPT_IP_PKTINFO,               19).
-define(SOCKET_OPT_IP_RECVDSTADDR,           20). % FreeBSD
-define(SOCKET_OPT_IP_RECVERR,               21).
-define(SOCKET_OPT_IP_RECVIF,                22).
-define(SOCKET_OPT_IP_RECVOPTS,              23).
-define(SOCKET_OPT_IP_RECVORIGDSTADDR,       24).
-define(SOCKET_OPT_IP_RECVTOS,               25).
-define(SOCKET_OPT_IP_RECVTTL,               26).
-define(SOCKET_OPT_IP_RETOPTS,               27).
-define(SOCKET_OPT_IP_ROUTER_ALERT,          28).
%% -define(SOCKET_OPT_IP_SNDSRCADDR,            29).
-define(SOCKET_OPT_IP_TOS,                   30).
-define(SOCKET_OPT_IP_TRANSPARENT,           31).
-define(SOCKET_OPT_IP_TTL,                   32).
-define(SOCKET_OPT_IP_UNBLOCK_SOURCE,           33).

-define(SOCKET_OPT_IPV6_ADDRFORM,           1).
-define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP,     2).
-define(SOCKET_OPT_IPV6_AUTHHDR,            3). % Obsolete?
%% -define(SOCKET_OPT_IPV6_AUTH_LEVEL,         4). % FreeBSD
%% -define(SOCKET_OPT_IPV6_CHECKSUM,           5). % FreeBSD
-define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP,    6).
-define(SOCKET_OPT_IPV6_DSTOPTS,            7).
%% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL,    8). % FreeBSD
%% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL,  9). % FreeBSD
%% -define(SOCKET_OPT_IPV6_FAITH,             10). % FreeBSD
-define(SOCKET_OPT_IPV6_FLOWINFO,          11).
-define(SOCKET_OPT_IPV6_HOPLIMIT,          12).
-define(SOCKET_OPT_IPV6_HOPOPTS,           13).
%% -define(SOCKET_OPT_IPV6_IPCOMP_LEVEL,      14). % FreeBSD
%% -define(SOCKET_OPT_IPV6_JOIN_GROUP,        15). % FreeBSD
%% -define(SOCKET_OPT_IPV6_LEAVE_GROUP,       16). % FreeBSD
-define(SOCKET_OPT_IPV6_MTU,               17).
-define(SOCKET_OPT_IPV6_MTU_DISCOVER,      18).
-define(SOCKET_OPT_IPV6_MULTICAST_HOPS,    19).
-define(SOCKET_OPT_IPV6_MULTICAST_IF,      20).
-define(SOCKET_OPT_IPV6_MULTICAST_LOOP,    21).
%% -define(SOCKET_OPT_IPV6_PORTRANGE,         22). % FreeBSD
%% -define(SOCKET_OPT_IPV6_PKTOPTIONS,        23). % FreeBSD
-define(SOCKET_OPT_IPV6_RECVERR,           24).
-define(SOCKET_OPT_IPV6_RECVPKTINFO,       25). % On FreeBSD: PKTINFO
%% -define(SOCKET_OPT_IPV6_RECVTCLASS,        26).
-define(SOCKET_OPT_IPV6_ROUTER_ALERT,      27).
-define(SOCKET_OPT_IPV6_RTHDR,             28).
%% -define(SOCKET_OPT_IPV6_TCLASS,            29). % FreeBSD
-define(SOCKET_OPT_IPV6_UNICAST_HOPS,      30).
%% -define(SOCKET_OPT_IPV6_USE_MIN_MTU,       31). % FreeBSD
-define(SOCKET_OPT_IPV6_V6ONLY,            32).

-define(SOCKET_OPT_TCP_CONGESTION,      1).
-define(SOCKET_OPT_TCP_CORK,            2).
%% -define(SOCKET_OPT_TCP_INFO,            3).
%% -define(SOCKET_OPT_TCP_KEEPCNT,         4).
%% -define(SOCKET_OPT_TCP_KEEPIDLE,        5).
%% -define(SOCKET_OPT_TCP_KEEPINTVL,       6).
-define(SOCKET_OPT_TCP_MAXSEG,          7).
%% -define(SOCKET_OPT_TCP_MD5SIG,          8).
-define(SOCKET_OPT_TCP_NODELAY,         9).
%% -define(SOCKET_OPT_TCP_NOOPT,          10).
%% -define(SOCKET_OPT_TCP_NOPUSH,         11).
%% -define(SOCKET_OPT_TCP_SYNCNT,         12).
%% -define(SOCKET_OPT_TCP_USER_TIMEOUT,   13).

-define(SOCKET_OPT_UDP_CORK,            1).

%% -define(SOCKET_OPT_SCTP_ADAPTION_LAYER,          1).
-define(SOCKET_OPT_SCTP_ASSOCINFO,               2).
%% -define(SOCKET_OPT_SCTP_AUTH_ACTIVE_KEY,         3).
%% -define(SOCKET_OPT_SCTP_AUTH_ASCONF,             4).
%% -define(SOCKET_OPT_SCTP_AUTH_CHUNK,              5).
%% -define(SOCKET_OPT_SCTP_AUTH_KEY,                6).
%% -define(SOCKET_OPT_SCTP_AUTH_DELETE_KEY,         7).
-define(SOCKET_OPT_SCTP_AUTOCLOSE,               8).
%% -define(SOCKET_OPT_SCTP_CONTEXT,                 9).
%% -define(SOCKET_OPT_SCTP_DEFAULT_SEND_PARAMS,    10).
%% -define(SOCKET_OPT_SCTP_DELAYED_ACK_TIME,       11).
-define(SOCKET_OPT_SCTP_DISABLE_FRAGMENTS,      12).
%% -define(SOCKET_OPT_SCTP_HMAC_IDENT,             13).
-define(SOCKET_OPT_SCTP_EVENTS,                 14).
%% -define(SOCKET_OPT_SCTP_EXPLICIT_EOR,           15).
%% -define(SOCKET_OPT_SCTP_FRAGMENT_INTERLEAVE,    16).
%% -define(SOCKET_OPT_SCTP_GET_PEER_ADDR_INFO,     17).
-define(SOCKET_OPT_SCTP_INITMSG,                18).
%% -define(SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR,  19).
%% -define(SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS,      20).
-define(SOCKET_OPT_SCTP_MAXSEG,                 21).
%% -define(SOCKET_OPT_SCTP_MAXBURST,               22).
-define(SOCKET_OPT_SCTP_NODELAY,                23).
%% -define(SOCKET_OPT_SCTP_PARTIAL_DELIVERY_POINT, 24).
%% -define(SOCKET_OPT_SCTP_PEER_ADDR_PARAMS,       25).
%% -define(SOCKET_OPT_SCTP_PEER_AUTH_CHUNKS,       26).
%% -define(SOCKET_OPT_SCTP_PRIMARY_ADDR,           27).
%% -define(SOCKET_OPT_SCTP_RESET_STREAMS,          28).
-define(SOCKET_OPT_SCTP_RTOINFO,                29).
%% -define(SOCKET_OPT_SCTP_SET_PEER_PRIMARY_ADDR,  30).
%% -define(SOCKET_OPT_SCTP_STATUS,                 31).
%% -define(SOCKET_OPT_SCTP_USE_EXT_RECVINFO,       32).

-define(SOCKET_SHUTDOWN_HOW_READ,       0).
-define(SOCKET_SHUTDOWN_HOW_WRITE,      1).
-define(SOCKET_SHUTDOWN_HOW_READ_WRITE, 2).



%% ===========================================================================
%%
%% Administrative and utility API
%%
%% ===========================================================================

-spec on_load() -> ok.

%% Should we require that the Extra arg is a map?
on_load() ->
    on_load(#{}).

-spec on_load(Extra) -> ok when
      Extra :: maps:map().

on_load(Extra) when is_map(Extra) ->
    on_load(atom_to_list(?MODULE), Extra).

-spec on_load(Path, Extra) -> ok when
      Path  :: string(),
      Extra :: maps:map().

on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) ->
    on_load(nif_is_loaded(), Path, Extra).

on_load(true, _Path, _Extra) ->
    ok;
on_load(false, Path, Extra) ->
    %% ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)).
    ok = erlang:load_nif(Path, Extra).



-spec info() -> list().

info() ->
    nif_info().



%% ===========================================================================
%%
%% The proper socket API
%%
%% ===========================================================================

%% ===========================================================================
%%
%% open - create an endpoint for communication
%%
%% Extra: netns
%%
%% <KOLLA>
%%
%% How do we handle the case when an fd has beem created (somehow)
%% and we shall create a socket "from it".
%% Can we figure out Domain, Type and Protocol from fd?
%% Yes we can: SO_DOMAIN, SO_PROTOCOL, SO_TYPE
%%
%% </KOLLA>
%%
%%
%% <KOLLA>
%%
%% Start a controller process here, *before* the nif_open call.
%% If that call is successful, update with owner process (controlling
%% process) and SockRef. If the open fails, kill the process.
%% "Register" the process on success:
%%
%%      nif_register(SockRef, self()).
%%
%% <ALSO>
%%
%% Maybe register the process under a name?
%% Something like: 
%%
%% list_to_atom(lists:flatten(io_lib:format("socket-~p", [SockRef]))).
%%
%% </ALSO>
%%
%% The nif sets up a monitor to this process, and if it dies the socket
%% is closed. It is also used if someone wants to monitor the socket.
%%
%% We therefor need monitor function(s): 
%%
%%               socket:monitor(Socket)
%%               socket:demonitor(Socket)
%%
%% These are basically used to monitor the controller process.
%% Should the socket record therefor contain the pid of the controller process?
%%
%% </KOLLA>
%%

%% -spec open(FD) -> {ok, Socket} | {error, Reason} when
%%       Socket   :: socket(),
%%       Reason   :: term().

%% open(FD) ->
%%     try
%%         begin
%%             case nif_open(FD) of
%%                 {ok, {SockRef, Domain, Type, Protocol}} ->
%%                     SocketInfo     = #{domain   => Domain,
%%                                        type     => Type,
%%                                        protocol => Protocol},
%%                     Socket         = #socket{info = SocketInfo,
%%                                              ref  = SockRef},
%%                     {ok, Socket};
%%                 {error, _} = ERROR ->
%%                     ERROR
%%             end
%%         end
%%     catch
%%         _:_ -> % This must be improved!!
%%             {error, einval}
%%     end.

-spec open(Domain, Type) -> {ok, Socket} | {error, Reason} when
      Domain   :: domain(),
      Type     :: type(),
      Socket   :: socket(),
      Reason   :: term().

open(Domain, Type) ->
    open(Domain, Type, null).

-spec open(Domain, Type, Protocol) -> {ok, Socket} | {error, Reason} when
      Domain   :: domain(),
      Type     :: type(),
      Protocol :: null | protocol(),
      Socket   :: socket(),
      Reason   :: term().

open(Domain, Type, Protocol) ->
    open(Domain, Type, Protocol, #{}).

-spec open(Domain, Type, Protocol, Extra) -> {ok, Socket} | {error, Reason} when
      Domain   :: domain(),
      Type     :: type(),
      Protocol :: null | protocol(),
      Extra    :: map(),
      Socket   :: socket(),
      Reason   :: term().

open(Domain, Type, Protocol0, Extra) when is_map(Extra) ->
    try
        begin
            Protocol  = default_protocol(Protocol0, Type),
            EDomain   = enc_domain(Domain),
            EType     = enc_type(Domain, Type),
            EProtocol = enc_protocol(Type, Protocol),
            case nif_open(EDomain, EType, EProtocol, Extra) of
                {ok, SockRef} ->
                    Socket = #socket{ref = SockRef},
                    {ok, Socket};
                {error, _} = ERROR ->
                    ERROR
            end
        end
    catch
        throw:T ->
            T;
        error:Reason ->
            {error, Reason}
    end.

%% Note that this is just a convenience function for when the protocol was
%% not specified. If its actually specified, then that will be selected.
%% Also, this only works for the some of the type's (stream, dgram and
%% seqpacket).
default_protocol(null, stream)    -> tcp;
default_protocol(null, dgram)     -> udp;
default_protocol(null, seqpacket) -> sctp;
default_protocol(null, Type)      -> throw({error, {no_default_protocol, Type}});
default_protocol(Protocol, _)     -> Protocol.


%% ===========================================================================
%%
%% bind - bind a name to a socket
%%

-spec bind(Socket, Addr) -> ok | {error, Reason} when
      Socket :: socket(),
      Addr   :: any | loopback | sockaddr(),
      Reason :: term().

bind(#socket{ref = SockRef}, Addr)
  when ((Addr =:= any) orelse (Addr =:= loopback)) ->
    try which_domain(SockRef) of
        inet ->
            nif_bind(SockRef, ?SOCKADDR_IN4_DEFAULT(Addr));
        inet6 ->
            nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr))
    catch
        throw:ERROR ->
            ERROR
    end;
bind(#socket{ref = SockRef} = _Socket, Addr) when is_map(Addr) ->
    try
        begin
            nif_bind(SockRef, ensure_sockaddr(Addr))
        end
    catch
        throw:ERROR ->
            ERROR
    end.



%% ===========================================================================
%%
%% bind - Add or remove a bind addresses on a socket
%%
%% Calling this function is only valid if the socket is: 
%%   type     = seqpacket
%%   protocol = sctp
%%
%% If the domain is inet, then all addresses *must* be IPv4.
%% If the domain is inet6, the addresses can be aither IPv4 or IPv6.
%%

-spec bind(Socket, Addrs, Action) -> ok | {error, Reason} when
      Socket :: socket(),
      Addrs  :: [sockaddr()],
      Action :: add | remove,
      Reason :: term().

bind(#socket{ref = SockRef}, Addrs, Action) 
  when is_list(Addrs) andalso ((Action =:= add) orelse (Action =:= remove)) ->
    try
        begin
            ensure_type(seqpacket, which_type(SockRef)),
            ensure_proto(sctp, which_protocol(SockRef)),
            validate_addrs(which_domain(SockRef), Addrs),
            nif_bind(SockRef, Addrs, Action)
        end
    catch
        throw:ERROR ->
            ERROR
    end.

ensure_type(SockRef, Type) ->
    case which_type(SockRef) of
        Type ->
            ok;
        _InvalidType ->
            einval()
    end.

ensure_proto(SockRef, Proto) ->
    case which_protocol(SockRef) of
        Proto ->
            ok;
        _InvalidProto ->
            einval()
    end.

validate_addrs(inet = _Domain, Addrs) ->
    validate_inet_addrs(Addrs);
validate_addrs(inet6 = _Domain, Addrs) ->
    validate_inet6_addrs(Addrs).

validate_inet_addrs(Addrs) ->
    Validator = fun(#{family := inet,
                      addrs  := Addr}) when is_tuple(Addr) andalso 
                                            (size(Addr) =:= 4) ->
                        ok;
                   (X) ->
                        throw({error, {invalid_address, X}})
                end,
    lists:foreach(Validator, Addrs).

validate_inet6_addrs(Addrs) ->
    Validator = fun(#{family := inet,
                      addrs  := Addr}) when is_tuple(Addr) andalso 
                                            (size(Addr) =:= 4) ->
                        ok;
                   (#{family := inet6,
                      addrs  := Addr}) when is_tuple(Addr) andalso 
                                            (size(Addr) =:= 8) ->
                        ok;
                   (X) ->
                        throw({error, {invalid_address, X}})
                end,
    lists:foreach(Validator, Addrs).


%% ===========================================================================
%%
%% connect - initiate a connection on a socket
%%

-spec connect(Socket, SockAddr) -> ok | {error, Reason} when
      Socket   :: socket(),
      SockAddr :: sockaddr(),
      Reason   :: term().

connect(Socket, SockAddr) ->
    connect(Socket, SockAddr, infinity).

-spec connect(Socket, SockAddr, Timeout) -> ok | {error, Reason} when
      Socket   :: socket(),
      SockAddr :: sockaddr(),
      Timeout  :: timeout(),
      Reason   :: term().

%% <KOLLA>
%% Is it possible to connect with family = local for the (dest) sockaddr?
%% </KOLLA>
connect(_Socket, _SockAddr, Timeout)
  when (is_integer(Timeout) andalso (Timeout =< 0)) ->
    {error, timeout};
connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout)
  when ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso
       ((Timeout =:= infinity) orelse is_integer(Timeout)) ->
    TS = timestamp(Timeout),
    case nif_connect(SockRef, SockAddr) of
        ok ->
            %% Connected!
            ok;
        {ok, Ref} ->
            %% Connecting...
	    NewTimeout = next_timeout(TS, Timeout),
	    receive
		{select, SockRef, Ref, ready_output} ->
                    %% <KOLLA>
                    %%
                    %% See open above!!
                    %%
                    %% * Here we should start and *register* the reader process
                    %%   (This will cause the nif code to create a monitor to 
                    %%   the process)
                    %% * The reader is basically used to implement the active-X 
                    %%   feature!
                    %% * If the reader dies for whatever reason, then the socket
                    %%   (resource) closes and the owner (controlling) process
                    %%   is informed (closed message).
                    %%
                    %% </KOLLA>
		    nif_finalize_connection(SockRef)
	    after NewTimeout ->
                    nif_cancel(SockRef, connect, Ref),
		    {error, timeout}
	    end;
	{error, _} = ERROR ->
	    ERROR
    end.



%% ===========================================================================
%%
%% listen - listen for connections on a socket
%%

-spec listen(Socket) -> ok | {error, Reason} when
      Socket  :: socket(),
      Reason  :: term().

listen(Socket) ->
    listen(Socket, ?SOCKET_LISTEN_BACKLOG_DEFAULT).

-spec listen(Socket, Backlog) -> ok | {error, Reason} when
      Socket  :: socket(),
      Backlog :: pos_integer(),
      Reason  :: term().

listen(#socket{ref = SockRef}, Backlog)
  when (is_integer(Backlog) andalso (Backlog >= 0)) ->
    nif_listen(SockRef, Backlog).




%% ===========================================================================
%%
%% accept, accept4 - accept a connection on a socket
%%

-spec accept(LSocket) -> {ok, Socket} | {error, Reason} when
      LSocket :: socket(),
      Socket  :: socket(),
      Reason  :: term().

accept(Socket) ->
    accept(Socket, ?SOCKET_ACCEPT_TIMEOUT_DEFAULT).

-spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when
      LSocket :: socket(),
      Timeout :: timeout(),
      Socket  :: socket(),
      Reason  :: term().

%% Do we really need this optimization?
accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) ->
    {error, timeout};
accept(#socket{ref = LSockRef}, Timeout)
  when is_integer(Timeout) orelse (Timeout =:= infinity) ->
    do_accept(LSockRef, Timeout).

do_accept(LSockRef, Timeout) ->
    TS     = timestamp(Timeout),
    AccRef = make_ref(),
    case nif_accept(LSockRef, AccRef) of
        {ok, SockRef} ->
            %% <KOLLA>
            %%
            %% * Here we should start and *register* the reader process
            %%   (This will cause the nif code to create a monitor to the process)
            %% * The reader is basically used to implement the active-X feature!
            %% * If the reader dies for whatever reason, then the socket (resource)
            %%   closes and the owner (controlling) process is informed (closed 
            %%   message).
            %%
            %% </KOLLA>
            Socket = #socket{ref = SockRef},
            {ok, Socket};

        {error, eagain} ->
            %% Each call is non-blocking, but even then it takes
            %% *some* time, so just to be sure, recalculate before 
            %% the receive.
	    NewTimeout = next_timeout(TS, Timeout),
            receive
                {select, LSockRef, AccRef, ready_input} ->
                    do_accept(LSockRef, next_timeout(TS, Timeout));

                {nif_abort, AccRef, Reason} ->
                    {error, Reason}

            after NewTimeout ->
                    nif_cancel(LSockRef, accept, AccRef),
                    flush_select_msgs(LSockRef, AccRef),
                    {error, timeout}
            end;

        {error, _} = ERROR ->
            nif_cancel(LSockRef, accept, AccRef), % Just to be on the safe side...
            ERROR
    end.



%% ===========================================================================
%%
%% send, sendto, sendmsg - send a message on a socket
%%

-spec send(Socket, Data) -> ok | {error, Reason} when
      Socket  :: socket(),
      Data    :: iodata(),
      Reason  :: term().

send(Socket, Data) ->
    send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT).

-spec send(Socket, Data, Flags) -> ok | {error, Reason} when
      Socket  :: socket(),
      Data    :: iodata(),
      Flags   :: send_flags(),
      Reason  :: term()
                 ; (Socket, Data, Timeout) -> ok | {error, Reason} when
      Socket  :: socket(),
      Data    :: iodata(),
      Timeout :: timeout(),
      Reason  :: term().

send(Socket, Data, Flags) when is_list(Flags) ->
    send(Socket, Data, Flags, ?SOCKET_SEND_TIMEOUT_DEFAULT);
send(Socket, Data, Timeout) ->
    send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, Timeout).

-spec send(Socket, Data, Flags, Timeout) -> ok | {error, Reason} when
      Socket  :: socket(),
      Data    :: iodata(),
      Flags   :: send_flags(),
      Timeout :: timeout(),
      Reason  :: term().

send(Socket, Data, Flags, Timeout) when is_list(Data) ->
    Bin = erlang:list_to_binary(Data),
    send(Socket, Bin, Flags, Timeout);
send(#socket{ref = SockRef}, Data, Flags, Timeout)
  when is_binary(Data) andalso is_list(Flags)  ->
    EFlags = enc_send_flags(Flags),
    do_send(SockRef, Data, EFlags, Timeout).

do_send(SockRef, Data, EFlags, Timeout) ->
    TS      = timestamp(Timeout),
    SendRef = make_ref(),
    case nif_send(SockRef, SendRef, Data, EFlags) of
        ok ->
            ok;
        {ok, Written} ->
	    NewTimeout = next_timeout(TS, Timeout),
	    %% We are partially done, wait for continuation
            receive
                {select, SockRef, SendRef, ready_output} when (Written > 0) ->
                    <<_:Written/binary, Rest/binary>> = Data,
                    do_send(SockRef, Rest, EFlags,
                            next_timeout(TS, Timeout));
                {select, SockRef, SendRef, ready_output} ->
                    do_send(SockRef, Data, EFlags,
                            next_timeout(TS, Timeout));

                {nif_abort, SendRef, Reason} ->
                    {error, Reason}

            after NewTimeout ->
                    nif_cancel(SockRef, send, SendRef),
                    flush_select_msgs(SockRef, SendRef),
                    {error, {timeout, size(Data)}}
            end;
        {error, eagain} ->
            receive
                {select, SockRef, SendRef, ready_output} ->
                    do_send(SockRef, Data, EFlags,
                            next_timeout(TS, Timeout));

                {nif_abort, SendRef, Reason} ->
                    {error, Reason}

            after Timeout ->
                    nif_cancel(SockRef, send, SendRef),
                    flush_select_msgs(SockRef, SendRef),
                    {error, {timeout, size(Data)}}
            end;

        {error, _} = ERROR ->
            ERROR
    end.




%% ---------------------------------------------------------------------------
%%

-spec sendto(Socket, Data, Dest) ->
                    ok | {error, Reason} when
      Socket :: socket(),
      Data   :: binary(),
      Dest   :: null | sockaddr(),
      Reason :: term().

sendto(Socket, Data, Dest) ->
    sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT).

-spec sendto(Socket, Data, Dest, Flags) -> ok | {error, Reason} when
      Socket :: socket(),
      Data   :: binary(),
      Dest   :: null | sockaddr(),
      Flags  :: send_flags(),
      Reason :: term().

sendto(Socket, Data, Dest, Flags) ->
    sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT).

-spec sendto(Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when
      Socket  :: socket(),
      Data    :: binary(),
      Dest    :: null | sockaddr(),
      Flags   :: send_flags(),
      Timeout :: timeout(),
      Reason  :: term().

sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) ->
    Bin = erlang:list_to_binary(Data),
    sendto(Socket, Bin, Dest, Flags, Timeout);
sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout)
  when is_binary(Data) andalso
       (Dest =:= null) andalso
       is_list(Flags) andalso
       (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
    EFlags = enc_send_flags(Flags),
    do_sendto(SockRef, Data, Dest, EFlags, Timeout);
sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout)
  when is_binary(Data) andalso
       ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso 
       is_list(Flags) andalso
       (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
    EFlags = enc_send_flags(Flags),
    do_sendto(SockRef, Data, Dest, EFlags, Timeout).

do_sendto(SockRef, Data, Dest, EFlags, Timeout) ->
    TS      = timestamp(Timeout),
    SendRef = make_ref(),
    case nif_sendto(SockRef, SendRef, Data, Dest, EFlags) of
        ok ->
            %% We are done
            ok;

        {ok, Written} ->
	    %% We are partially done, wait for continuation
            receive
                {select, SockRef, SendRef, ready_output} when (Written > 0) ->
                    <<_:Written/binary, Rest/binary>> = Data,
                    do_sendto(SockRef, Rest, Dest, EFlags,
                              next_timeout(TS, Timeout));
                {select, SockRef, SendRef, ready_output} ->
                    do_sendto(SockRef, Data, Dest, EFlags,
                              next_timeout(TS, Timeout));

                {nif_abort, SendRef, Reason} ->
                    {error, Reason}

            after Timeout ->
                    nif_cancel(SockRef, sendto, SendRef),
                    flush_select_msgs(SockRef, SendRef),
                    {error, timeout}
            end;

        {error, eagain} ->
            receive
                {select, SockRef, SendRef, ready_output} ->
                    do_sendto(SockRef, Data, Dest, EFlags, 
                              next_timeout(TS, Timeout))
            after Timeout ->
                    nif_cancel(SockRef, sendto, SendRef),
                    flush_select_msgs(SockRef, SendRef),
                    {error, timeout}
            end;

        {error, _} = ERROR ->
            ERROR
    end.



%% ---------------------------------------------------------------------------

%% -spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when
%%       Socket   :: socket(),
%%       MsgHdr   :: msg_hdr(),
%%       Flags    :: send_flags(),
%%       Reason   :: term().



%% ===========================================================================
%%
%% writev - write data into multiple buffers
%%

%% send(Socket, Data, Flags, Timeout)
%%   when (is_list(Data) orelse is_binary(Data)) andalso is_list(Flags)  ->
%%     IOVec  = erlang:iolist_to_iovec(Data),
%%     EFlags = enc_send_flags(Flags),
%%     send_iovec(Socket, IOVec, EFlags, Timeout).


%% %% Iterate over the IO-vector (list of binaries).

%% send_iovec(_Socket, [] = _IOVec, _EFlags, _Timeout) ->
%%     ok;
%% send_iovec({socket, _, SockRef} = Socket, [Bin|IOVec], EFlags, Timeout) ->
%%     case do_send(SockRef, make_ref(), Bin, EFlags, Timeout) of
%%         {ok, NewTimeout} ->
%%             send_iovec(Socket, IOVec, EFlags, NewTimeout);
%%         {error, _} = ERROR ->
%%             ERROR
%%     end.


%% do_send(SockRef, SendRef, Data, _EFlags, Timeout)
%%   when (Timeout < 0) ->
%%     nif_cancel(SockRef, SendRef),
%%     flush_select_msgs(SockRef, SendRef),
%%     {error, {timeout, size(Data)}};
%% do_send(SockRef, SendRef, Data, EFlags, Timeout) ->
%%     TS = timestamp(Timeout),
%%     case nif_send(SockRef, SendRef, Data, EFlags) of
%%         ok ->
%%             {ok, next_timeout(TS, Timeout)};
%%         {ok, Written} ->
%% 	    %% We are partially done, wait for continuation
%%             receive
%%                 {select, SockRef, SendRef, ready_output} ->
%%                     <<_:Written/binary, Rest/binary>> = Data,
%%                     do_send(SockRef, make_ref(), Rest, EFlags,
%%                             next_timeout(TS, Timeout))
%%             after Timeout ->
%%                     nif_cancel(SockRef, SendRef),
%%                     flush_select_msgs(SockRef, SendRef),
%%                     {error, timeout}
%%             end;
%%         {error, eagain} ->
%%             receive
%%                 {select, SockRef, SendRef, ready_output} ->
%%                     do_send(SockRef, SendRef, Data, EFlags,
%%                             next_timeout(TS, Timeout))
%%             after Timeout ->
%%                     nif_cancel(SockRef, SendRef),
%%                     flush_select_msgs(SockRef, SendRef),
%%                     {error, timeout}
%%             end;

%%         {error, _} = ERROR ->
%%             ERROR
%%     end.


%% ===========================================================================
%%
%% recv, recvfrom, recvmsg - receive a message from a socket
%%
%% Description:
%% There is a special case for the argument Length. If its set to zero (0),
%% it means "give me everything you have".
%%
%% Returns: {ok, Binary} | {error, Reason}
%% Binary  - The received data as a binary
%% Reason  - The error reason:
%%              timeout | {timeout, AccData} |
%%              posix() | {posix(), AccData} |
%%              atom()  | {atom(), AccData}
%% AccData - The data (as a binary) that we did manage to receive
%%           before the timeout.
%%
%% Arguments:
%% Socket  - The socket to read from.
%% Length  - The number of bytes to read.
%% Flags   - A list of "options" for the read.
%% Timeout - Time-out in milliseconds.

-spec recv(Socket) -> {ok, Data} | {error, Reason} when
      Socket :: socket(),
      Data   :: binary(),
      Reason :: term().
                          
recv(Socket) ->
    recv(Socket, 0).

-spec recv(Socket, Length) -> {ok, Data} | {error, Reason} when
      Socket :: socket(),
      Length :: non_neg_integer(),
      Data   :: binary(),
      Reason :: term().

recv(Socket, Length) ->
    recv(Socket, Length,
         ?SOCKET_RECV_FLAGS_DEFAULT,
         ?SOCKET_RECV_TIMEOUT_DEFAULT).

-spec recv(Socket, Length, Flags) -> {ok, Data} | {error, Reason} when
      Socket :: socket(),
      Length :: non_neg_integer(),
      Flags  :: recv_flags(),
      Data   :: binary(),
      Reason :: term()
                ; (Socket, Length, Timeout) -> {ok, Data} | {error, Reason} when
      Socket  :: socket(),
      Length  :: non_neg_integer(),
      Timeout :: timeout(),
      Data    :: binary(),
      Reason  :: term().

recv(Socket, Length, Flags) when is_list(Flags) ->
    recv(Socket, Length, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT);
recv(Socket, Length, Timeout) ->
    recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).

-spec recv(Socket, Length, Flags, Timeout) -> {ok, Data} | {error, Reason} when
      Socket  :: socket(),
      Length  :: non_neg_integer(),
      Flags   :: recv_flags(),
      Timeout :: timeout(),
      Data    :: binary(),
      Reason  :: term().

recv(#socket{ref = SockRef}, Length, Flags, Timeout)
  when (is_integer(Length) andalso (Length >= 0)) andalso
       is_list(Flags) andalso
       (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
    EFlags = enc_recv_flags(Flags),
    do_recv(SockRef, undefined, Length, EFlags, <<>>, Timeout).

%% We need to pass the "old recv ref" around because of the special case
%% with Length = 0. This case makes it neccessary to have a timeout function
%% clause since we may never wait for anything (no receive select), and so the
%% the only timeout check will be the function clause.
do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
  when (Timeout =:= infinity) orelse
       (is_integer(Timeout) andalso (Timeout > 0)) ->
    TS      = timestamp(Timeout),
    RecvRef = make_ref(),
    case nif_recv(SockRef, RecvRef, Length, EFlags) of
        {ok, true = _Complete, Bin} when (size(Acc) =:= 0) ->
            {ok, Bin};
        {ok, true = _Complete, Bin} ->
            {ok, <<Acc/binary, Bin/binary>>};

        %% It depends on the amount of bytes we tried to read:
        %%    0   - Read everything available
        %%          We got something, but there may be more - keep reading.
        %%    > 0 - We got a part of the message and we will be notified
        %%          when there is more to read (a select message)
        {ok, false = _Complete, Bin} when (Length =:= 0) ->
            do_recv(SockRef, RecvRef,
                    Length, EFlags,
                    <<Acc/binary, Bin/binary>>,
                    next_timeout(TS, Timeout));

        {ok, false = _Completed, Bin} when (size(Acc) =:= 0) ->
            %% We got the first chunk of it.
            %% We will be notified (select message) when there
            %% is more to read.
	    NewTimeout = next_timeout(TS, Timeout),
            receive
                {select, SockRef, RecvRef, ready_input} ->
                    do_recv(SockRef, RecvRef,
                            Length-size(Bin), EFlags,
                            Bin,
                            next_timeout(TS, Timeout));

                {nif_abort, RecvRef, Reason} ->
                    {error, Reason}


            after NewTimeout ->
                    nif_cancel(SockRef, recv, RecvRef),
                    flush_select_msgs(SockRef, RecvRef),
                    {error, {timeout, Acc}}
            end;

        {ok, false = _Completed, Bin} ->
            %% We got a chunk of it!
	    NewTimeout = next_timeout(TS, Timeout),
            receive
                {select, SockRef, RecvRef, ready_input} ->
                    do_recv(SockRef, RecvRef,
                            Length-size(Bin), EFlags,
                            <<Acc/binary, Bin/binary>>,
                            next_timeout(TS, Timeout));

                {nif_abort, RecvRef, Reason} ->
                    {error, Reason}


            after NewTimeout ->
                    nif_cancel(SockRef, recv, RecvRef),
                    flush_select_msgs(SockRef, RecvRef),
                    {error, {timeout, Acc}}
            end;

        %% We return with the accumulated binary (if its non-empty)
        {error, eagain} when (Length =:= 0) andalso (size(Acc) > 0) ->
            {ok, Acc};

        {error, eagain} ->
            %% There is nothing just now, but we will be notified when there
            %% is something to read (a select message).
            NewTimeout = next_timeout(TS, Timeout),
            receive
                {select, SockRef, RecvRef, ready_input} ->
                    do_recv(SockRef, RecvRef,
                            Length, EFlags,
                            Acc,
                            next_timeout(TS, Timeout));

                {nif_abort, RecvRef, Reason} ->
                    {error, Reason}

            after NewTimeout ->
                    nif_cancel(SockRef, recv, RecvRef),
                    flush_select_msgs(SockRef, RecvRef),
                    {error, timeout}
            end;

        {error, closed = Reason} ->
            do_close(SockRef),
            if
                (size(Acc) =:= 0) ->
                    {error, Reason};
                true ->
                    {error, {Reason, Acc}}
            end;

        {error, _} = ERROR when (size(Acc) =:= 0) ->
            ERROR;

        {error, Reason} ->
            {error, {Reason, Acc}}

    end;

do_recv(SockRef, RecvRef, 0 = _Length, _Eflags, Acc, _Timeout) ->
    %% The current recv operation is to be cancelled, so no need for a ref...
    %% The cancel will end our 'read everything you have' and "activate"
    %% any waiting reader.
    nif_cancel(SockRef, recv, RecvRef),
    {ok, Acc};
do_recv(_SockRef, _RecvRef, _Length, _EFlags, Acc, _Timeout) when (size(Acc) > 0) ->
    {error, {timeout, Acc}};
do_recv(_SockRef, _RecvRef, _Length, _EFlags, _Acc, _Timeout) ->
    {error, timeout}.



%% ---------------------------------------------------------------------------
%%
%% With recvfrom we get messages, which means that regardless of how
%% much we want to read, we return when we get a message.
%% The MaxSize argument basically defines the size of our receive
%% buffer. By setting the size to zero (0), we use the configured
%% size (see setopt).
%% It may be impossible to know what (buffer) size is appropriate
%% "in advance", and in those cases it may be convenient to use the
%% (recv) 'peek' flag. When this flag is provided the message is *not*
%% "consumed" from the underlying buffers, so another recvfrom call
%% is needed, possibly with a then adjusted buffer size.
%%

-spec recvfrom(Socket) -> {ok, {Source, Data}} | {error, Reason} when
      Socket    :: socket(),
      Source    :: sockaddr() | undefined,
      Data      :: binary(),
      Reason    :: term().

recvfrom(Socket) ->
    recvfrom(Socket, 0).

-spec recvfrom(Socket, BufSz) -> {ok, {Source, Data}} | {error, Reason} when
      Socket    :: socket(),
      BufSz     :: non_neg_integer(),
      Source    :: sockaddr() | undefined,
      Data      :: binary(),
      Reason    :: term().

recvfrom(Socket, BufSz) ->
    recvfrom(Socket, BufSz,
             ?SOCKET_RECV_FLAGS_DEFAULT,
             ?SOCKET_RECV_TIMEOUT_DEFAULT).

-spec recvfrom(Socket, Flags, Timeout) -> 
                      {ok, {Source, Data}} | {error, Reason} when
      Socket  :: socket(),
      Flags   :: recv_flags(),
      Timeout :: timeout(),
      Source  :: sockaddr() | undefined,
      Data    :: binary(),
      Reason  :: term()
                 ; (Socket, BufSz, Flags) -> 
                      {ok, {Source, Data}} | {error, Reason} when
      Socket :: socket(),
      BufSz  :: non_neg_integer(),
      Flags  :: recv_flags(),
      Source :: sockaddr() | undefined,
      Data   :: binary(),
      Reason :: term()
                 ; (Socket, BufSz, Timeout) -> 
                      {ok, {Source, Data}} | {error, Reason} when
      Socket  :: socket(),
      BufSz   :: non_neg_integer(),
      Timeout :: timeout(),
      Source  :: sockaddr() | undefined,
      Data    :: binary(),
      Reason  :: term().

recvfrom(Socket, Flags, Timeout) when is_list(Flags) ->
    recvfrom(Socket, 0, Flags, Timeout);
recvfrom(Socket, BufSz, Flags) when is_list(Flags) ->
    recvfrom(Socket, BufSz, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT);
recvfrom(Socket, BufSz, Timeout) ->
    recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).

-spec recvfrom(Socket, BufSz, Flags, Timeout) -> 
                      {ok, {Source, Data}} | {error, Reason} when
      Socket    :: socket(),
      BufSz     :: non_neg_integer(),
      Flags     :: recv_flags(),
      Timeout   :: timeout(),
      Source    :: sockaddr() | undefined,
      Data      :: binary(),
      Reason    :: term().

recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout)
  when (is_integer(BufSz) andalso (BufSz >= 0)) andalso
       is_list(Flags) andalso
       (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
    EFlags = enc_recv_flags(Flags),
    do_recvfrom(SockRef, BufSz, EFlags, Timeout).

do_recvfrom(SockRef, BufSz, EFlags, Timeout)  ->
    TS      = timestamp(Timeout),
    RecvRef = make_ref(),
    case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of
        {ok, {_Source, _NewData}} = OK ->
            OK;

        {error, eagain} ->
            %% There is nothing just now, but we will be notified when there
            %% is something to read (a select message).
            NewTimeout = next_timeout(TS, Timeout),
            receive
                {select, SockRef, RecvRef, ready_input} ->
                    do_recvfrom(SockRef, BufSz, EFlags,
                                next_timeout(TS, Timeout));

                {nif_abort, RecvRef, Reason} ->
                    {error, Reason}

            after NewTimeout ->
                    nif_cancel(SockRef, recvfrom, RecvRef),
                    flush_select_msgs(SockRef, RecvRef),
                    {error, timeout}
            end;

        {error, _Reason} = ERROR ->
            ERROR

    end.



%% ---------------------------------------------------------------------------
%%

%% -spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when
%%       Socket :: socket(),
%%       MsgHdr :: msghdr(),
%%       Flags  :: recv_flags(),
%%       Reason :: term().



%% ===========================================================================
%%
%% readv - read data into multiple buffers
%%



%% ===========================================================================
%%
%% close - close a file descriptor
%%

-spec close(Socket) -> ok | {error, Reason} when
      Socket :: socket(),
      Reason :: term().

close(#socket{ref = SockRef}) ->
    do_close(SockRef).

do_close(SockRef) ->
    case nif_close(SockRef) of
        ok ->
            nif_finalize_close(SockRef);
        {ok, CloseRef} ->
            %% We must wait
            receive
		{close, CloseRef} ->
                    %% <KOLLA>
                    %%
                    %% WHAT HAPPENS IF THIS PROCESS IS KILLED
                    %% BEFORE WE CAN EXECUTE THE FINAL CLOSE???
                    %%
                    %% </KOLLA>
                    nif_finalize_close(SockRef)
            end;
        {error, _} = ERROR ->
            ERROR
    end.




%% ===========================================================================
%%
%% shutdown - shut down part of a full-duplex connection
%%

-spec shutdown(Socket, How) -> ok | {error, Reason} when
      Socket :: socket(),
      How    :: shutdown_how(),
      Reason :: term().

shutdown(#socket{ref = SockRef}, How) ->
    try
        begin
            EHow = enc_shutdown_how(How),
            nif_shutdown(SockRef, EHow)
        end
    catch
        throw:T ->
            T;
        error:Reason ->
            {error, Reason}
    end.




%% ===========================================================================
%%
%% setopt - manipulate individual properties of a socket
%%
%% What properties are valid depend on what kind of socket it is
%% (domain, type and protocol)
%% If its an "invalid" option (or value), we should not crash but return some
%% useful error...
%%
%% <KOLLA>
%%
%% WE NEED TO MAKE SURE THAT THE USER DOES NOT MAKE US BLOCKING
%% AS MUCH OF THE CODE EXPECTS TO BE NON-BLOCKING!!
%%
%% </KOLLA>

-spec setopt(Socket, otp, otp_socket_option(), Value) -> ok | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, socket, socket_option(), Value) -> ok | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, ip, ip_socket_option(), Value) -> ok | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, ipv6, ipv6_socket_option(), Value) -> ok | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, tcp, tcp_socket_option(), Value) -> ok | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, udp, udp_socket_option(), Value) -> ok | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, sctp, sctp_socket_option(), Value) -> ok | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term().

setopt(#socket{ref = SockRef}, Level, Key, Value) ->
    try
        begin
            Domain               = which_domain(SockRef),
            Type                 = which_type(SockRef),
            Protocol             = which_protocol(SockRef),
            {EIsEncoded, ELevel} = enc_setopt_level(Level),
            EKey = enc_setopt_key(Level, Key, Domain, Type, Protocol),
            EVal = enc_setopt_value(Level, Key, Value, Domain, Type, Protocol),
            nif_setopt(SockRef, EIsEncoded, ELevel, EKey, EVal)
        end
    catch
        throw:T ->
            T;
        error:Reason ->
            {error, Reason} % Process more?
    end.




%% ===========================================================================
%%
%% getopt - retrieve individual properties of a socket
%%
%% What properties are valid depend on what kind of socket it is
%% (domain, type and protocol).
%% If its an "invalid" option, we should not crash but return some
%% useful error...
%%
%% When specifying level as an integer, and therefor using "native mode",
%% we should make it possible to specify common types instead of the
%% value size. Example: int | bool | {string, pos_integer()} | non_neg_integer()
%%

-spec getopt(Socket, otp, otp_socket_option()) -> {ok, Value} | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, socket, socket_option()) -> {ok, Value} | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, ip, ip_socket_option()) -> {ok, Value} | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, ipv6, ipv6_socket_option()) -> {ok, Value} | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, tcp, tcp_socket_option()) -> {ok, Value} | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, udp, udp_socket_option()) -> {ok, Value} | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, sctp, sctp_socket_option()) -> {ok, Value} | {error, Reason} when
      Socket :: socket(),
      Value  :: term(),
      Reason :: term()
                ; (Socket, Level, Key) -> ok | {ok, Value} | {error, Reason} when
      Socket    :: socket(),
      Level     :: integer(),
      Key       :: {NativeOpt, ValueSize},
      NativeOpt :: integer(),
      ValueSize :: int | bool | non_neg_integer(),
      Value     :: term(),
      Reason    :: term().

getopt(#socket{ref = SockRef}, Level, Key) ->
    try
        begin
            Domain   = which_domain(SockRef),
            Type     = which_type(SockRef),
            Protocol = which_protocol(SockRef),
            {EIsEncoded, ELevel} = enc_getopt_level(Level),
            EKey     = enc_getopt_key(Level, Key, Domain, Type, Protocol),
            %% We may need to decode the value (for the same reason
            %% we (may have) needed to encode the value for setopt).
            case nif_getopt(SockRef, EIsEncoded, ELevel, EKey) of
                ok ->
                    ok;
                {ok, EVal} ->
                    Val = dec_getopt_value(Level, Key, EVal,
                                           Domain, Type, Protocol),
                    {ok, Val};
                {error, _} = ERROR ->
                    ERROR
            end
        end
    catch
        throw:T ->
            T;
        error:Reason ->
            {error, Reason} % Process more?
    end.


%% These are internal "shortcut" functions for the options
%% domain, type and protocol.
which_domain(SockRef) ->
    case nif_getopt(SockRef, true,
                    ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_DOMAIN) of
        {ok, Domain} ->
            Domain;
        {error, _} = ERROR ->
            throw(ERROR)
    end.
        

which_type(SockRef) ->
    case nif_getopt(SockRef, true,
                    ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_TYPE) of
        {ok, Type} ->
            Type;
        {error, _} = ERROR ->
            throw(ERROR)
    end.

which_protocol(SockRef) ->
    case nif_getopt(SockRef, true,
                    ?SOCKET_OPT_LEVEL_SOCKET, ?SOCKET_OPT_SOCK_PROTOCOL) of
        {ok, Type} ->
            Type;
        {error, _} = ERROR ->
            throw(ERROR)
    end.


%% ===========================================================================
%%
%% sockname - return the current address of the socket.
%%
%%

-spec sockname(Socket) -> {ok, SockAddr} | {error, Reason} when
      Socket   :: socket(),
      SockAddr :: sockaddr(),
      Reason   :: term().

sockname(#socket{ref = SockRef}) ->
    nif_sockname(SockRef).



%% ===========================================================================
%%
%% peername - return the address of the peer *connected* to the socket.
%%
%%

-spec peername(Socket) -> {ok, SockAddr} | {error, Reason} when
      Socket   :: socket(),
      SockAddr :: sockaddr(),
      Reason   :: term().

peername(#socket{ref = SockRef}) ->
    nif_peername(SockRef).



%% ===========================================================================
%%
%% Encode / decode
%%
%% ===========================================================================

-spec enc_domain(Domain) -> non_neg_integer() when
      Domain :: domain().

enc_domain(local)  -> ?SOCKET_DOMAIN_LOCAL;
enc_domain(inet)   -> ?SOCKET_DOMAIN_INET;
enc_domain(inet6)  -> ?SOCKET_DOMAIN_INET6;
enc_domain(Domain) -> throw({error, {invalid_domain, Domain}}).

-spec enc_type(Domain, Type) -> non_neg_integer() when
      Domain :: domain(),
      Type   :: type().

%% What combos are valid?
enc_type(_, stream)    -> ?SOCKET_TYPE_STREAM;
enc_type(_, dgram)     -> ?SOCKET_TYPE_DGRAM;
enc_type(_, raw)       -> ?SOCKET_TYPE_RAW;
enc_type(_, seqpacket) -> ?SOCKET_TYPE_SEQPACKET;
enc_type(_, Type)      -> throw({error, {invalid_type, Type}}).

-spec enc_protocol(Type, Protocol) -> non_neg_integer() | 
                                      {raw, non_neg_integer()} when
      Type     :: type(),
      Protocol :: protocol().

enc_protocol(dgram,     ip)   -> ?SOCKET_PROTOCOL_IP;
enc_protocol(stream,    tcp)  -> ?SOCKET_PROTOCOL_TCP;
enc_protocol(dgram,     udp)  -> ?SOCKET_PROTOCOL_UDP;
enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP;
enc_protocol(raw,       icmp) -> ?SOCKET_PROTOCOL_ICMP;
enc_protocol(raw,       igmp) -> ?SOCKET_PROTOCOL_IGMP;
enc_protocol(raw,       {raw, P} = RAW) when is_integer(P) -> RAW;
enc_protocol(Type, Proto) -> 
    throw({error, {invalid_protocol, {Type, Proto}}}).


-spec enc_send_flags(Flags) -> non_neg_integer() when
      Flags :: send_flags().

enc_send_flags(Flags) ->
    EFlags = [{confirm,   ?SOCKET_SEND_FLAG_CONFIRM},
              {dontroute, ?SOCKET_SEND_FLAG_DONTROUTE},
              {eor,       ?SOCKET_SEND_FLAG_EOR},
              {more,      ?SOCKET_SEND_FLAG_MORE},
              {nosignal,  ?SOCKET_SEND_FLAG_NOSIGNAL},
              {oob,       ?SOCKET_SEND_FLAG_OOB}],
    enc_flags(Flags, EFlags).

-spec enc_recv_flags(Flags) -> non_neg_integer() when
      Flags :: recv_flags().

enc_recv_flags(Flags) ->
    EFlags = [{cmsg_cloexec, ?SOCKET_RECV_FLAG_CMSG_CLOEXEC},
              {errqueue,     ?SOCKET_RECV_FLAG_ERRQUEUE},
              {oob,          ?SOCKET_RECV_FLAG_OOB},
              {peek,         ?SOCKET_RECV_FLAG_PEEK},
              {trunc,        ?SOCKET_RECV_FLAG_TRUNC}],
    enc_flags(Flags, EFlags).


enc_flags([], _) ->
    0;
enc_flags(Flags, EFlags) ->
    F = fun(Flag, Acc) ->
                case lists:keysearch(Flag, 1, EFlags) of
                    {value, {Flag, EFlag}} ->
                        Acc bor (1 bsl EFlag);
                    false ->
                        throw({error, {unknown_flag, Flag}})
                end
        end,
    lists:foldl(F, 0, Flags).


%% +++ Encode setopt level +++

-spec enc_setopt_level(Level) -> {IsEncoded, EncodedLevel} when
      Level        :: sockopt_level(),
      IsEncoded    :: boolean(),
      EncodedLevel :: integer().

enc_setopt_level(otp) ->
    {true, ?SOCKET_OPT_LEVEL_OTP};
enc_setopt_level(socket) ->
    {true, ?SOCKET_OPT_LEVEL_SOCKET};
enc_setopt_level(ip) ->
    {true, ?SOCKET_OPT_LEVEL_IP};
enc_setopt_level(ipv6) ->
    {true, ?SOCKET_OPT_LEVEL_IPV6};
enc_setopt_level(tcp) ->
    {true, ?SOCKET_OPT_LEVEL_TCP};
enc_setopt_level(udp) ->
    {true, ?SOCKET_OPT_LEVEL_UDP};
enc_setopt_level(sctp) ->
    {true, ?SOCKET_OPT_LEVEL_SCTP};
%% Any option that is of an plain level must be provided as a binary
%% already fully encoded!
enc_setopt_level(L) when is_integer(L) ->
    {false, L}.


%% +++ Encode setopt key +++

%% We should ...really... do something with the domain, type and protocol args...
%% Also, any option (key) which has an integer level (plain) must also be provided
%% in a plain mode, that is, as an integer.
%% Also, not all options are available on all platforms. That is something we
%% don't check here, but in the nif-code.

enc_setopt_key(Level, Opt, Domain, Type, Protocol) ->
    enc_sockopt_key(Level, Opt, set, Domain, Type, Protocol).


%% +++ Encode setopt value +++
%%
%% For the most part this function does *not* do an actual encode,
%% it simply validates the value type. But in some cases it will
%% encode the value into an more "manageable" type.
%% It also handles "aliases" (see linger).

enc_setopt_value(otp, debug, V, _, _, _) when is_boolean(V) ->
    V;
enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) ->
    V;
enc_setopt_value(otp, controlling_process, V, _, _, _) when is_pid(V) ->
    V;
enc_setopt_value(otp = L, Opt, V, _D, _T, _P) ->
    not_supported({L, Opt, V});

enc_setopt_value(socket, bindtodevice, V, _D, _T, _P) when is_list(V) ->
    V;
enc_setopt_value(socket, broadcast, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(socket, debug, V, _D, _T, _P) when is_integer(V) ->
    V;
enc_setopt_value(socket, dontroute, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(socket, keepalive, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(socket, linger, abort, D, T, P) ->
    enc_setopt_value(socket, linger, {true, 0}, D, T, P);
enc_setopt_value(socket, linger, {OnOff, Secs} = V, _D, _T, _P)
  when is_boolean(OnOff) andalso is_integer(Secs) andalso (Secs >= 0) ->
    V;
enc_setopt_value(socket, oobinline, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(socket, peek_off, V, _D, _T, _P) when is_integer(V) ->
    V;
enc_setopt_value(socket, priority, V, _D, _T, _P) when is_integer(V) ->
    V;
enc_setopt_value(socket, rcvbuf, V, _D, _T, _P) when is_integer(V) ->
    V;
enc_setopt_value(socket, rcvlowat, V, _D, _T, _P) when is_integer(V) ->
    V;
enc_setopt_value(socket, rcvtimeo, #{sec := Sec, usec := USec} = V, _D, _T, _P) 
  when is_integer(Sec) andalso is_integer(USec) ->
    V;
enc_setopt_value(socket, reuseaddr, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(socket, reuseport, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) ->
    V;
enc_setopt_value(socket, sndlowat, V, _D, _T, _P) when is_integer(V) ->
    V;
enc_setopt_value(socket, sndtimeo, #{sec := Sec, usec := USec} = V, _D, _T, _P) 
  when is_integer(Sec) andalso is_integer(USec) ->
    V;
enc_setopt_value(socket, timestamp, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(socket = L, Opt, V, _D, _T, _P) ->
    not_supported({L, Opt, V});

enc_setopt_value(ip, add_membership, #{multiaddr := MA,
                                       interface := IF} = V, _D, _T, _P)
  when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
       ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) ->
    V;
enc_setopt_value(ip, add_source_membership, #{multiaddr  := MA,
                                              interface  := IF,
                                              sourceaddr := SA} = V, _D, _T, _P)
  when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
       (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
       (is_tuple(SA) andalso (size(SA) =:= 4)) ->
    V;
enc_setopt_value(ip, block_source, #{multiaddr  := MA,
                                     interface  := IF,
                                     sourceaddr := SA} = V, _D, _T, _P)
  when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
       (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
       (is_tuple(SA) andalso (size(SA) =:= 4)) ->
    V;
enc_setopt_value(ip, drop_membership, #{multiaddr := MA,
                                        interface := IF} = V, _D, _T, _P)
  when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
       ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) ->
    V;
enc_setopt_value(ip, drop_source_membership, #{multiaddr  := MA,
                                               interface  := IF,
                                               sourceaddr := SA} = V, _D, _T, _P)
  when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
       (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
       (is_tuple(SA) andalso (size(SA) =:= 4)) ->
    V;
enc_setopt_value(ip, freebind, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(ip, hdrincl, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(ip, minttl, V, _D, _T, _P) when is_integer(V) ->
    V;
enc_setopt_value(ip, msfilter, null = V, _D, _T, _P) ->
    V;
enc_setopt_value(ip, msfilter, #{multiaddr := MA,
                                 interface := IF,
                                 fmode     := FMode,
                                 slist     := SL} = V, _D, _T, _P) 
  when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
       (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
       ((FMode =:= include) orelse (FMode =:= exclude)) andalso 
       is_list(SL) ->
    ensure_ip_msfilter_slist(SL),
    V;
enc_setopt_value(ip, mtu_discover, V, _D, _T, _P)
  when (V =:= want)  orelse
       (V =:= dont)  orelse
       (V =:= do)    orelse
       (V =:= probe) orelse
       is_integer(V) ->
    V;
enc_setopt_value(ip, multicast_all, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, multicast_if, V, _D, _T, _P)
  when (V =:= any) orelse (is_tuple(V) andalso (size(V) =:= 4)) ->
    V;
enc_setopt_value(ip, multicast_loop, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, multicast_ttl, V, _D, _T, _P)
  when is_integer(V) andalso (0 =< V) andalso (V =< 255) ->
    V;
enc_setopt_value(ip, nodefrag, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, pktinfo, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, recvdstaddr, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, recverr, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, recvif, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, recvopts, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, recvorigdstaddr, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, recvtos, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, recvttl, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, retopts, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, router_alert, V, _D, _T, _P)
  when is_integer(V) ->
    V;
enc_setopt_value(ip, tos, V, _D, _T, _P)
  when (V =:= lowdelay)    orelse
       (V =:= throughput)  orelse
       (V =:= reliability) orelse
       (V =:= mincost)     orelse
       is_integer(V) ->
    V;
enc_setopt_value(ip, transparent, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ip, ttl, V, _D, _T, _P)
  when is_integer(V) ->
    V;
enc_setopt_value(ip, unblock_source, #{multiaddr  := MA,
                                       interface  := IF,
                                       sourceaddr := SA} = V, _D, _T, _P)
  when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
       (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
       (is_tuple(SA) andalso (size(SA) =:= 4)) ->
    V;
enc_setopt_value(ip = L, Opt, V, _D, _T, _P) ->
    not_supported({L, Opt, V});

enc_setopt_value(ipv6, addrform, inet = V, _D, _T, _P) ->
    enc_domain(V);
enc_setopt_value(ipv6, add_membership, #{multiaddr := MA,
                                         interface := IF} = V, _D, _T, _P)
  when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso
        (is_integer(IF) andalso (IF >= 0))) ->
    V;
%% Is this obsolete? When get, the result is enoprotoopt and in the 
%% header file it says 'obsolete'...
%% But there might be (old?) versions of linux where it still works...
enc_setopt_value(ipv6, authhdr, V, _D, T, _P)
  when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
    V;
enc_setopt_value(ipv6, dstopts, V, _D, T, _P)
  when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
    V;
enc_setopt_value(ipv6, drop_membership, #{multiaddr := MA,
                                          interface := IF} = V, _D, _T, _P)
  when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso
        (is_integer(IF) andalso (IF >= 0))) ->
    V;
enc_setopt_value(ipv6, flowinfo, V, _D, T, _P)
  when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
    V;
enc_setopt_value(ipv6, hoplimit, V, _D, T, _P)
  when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
    V;
enc_setopt_value(ipv6, hopopts, V, _D, T, _P)
  when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
    V;
enc_setopt_value(ipv6, mtu, V, _D, _T, _P) when is_integer(V) ->
    V;
enc_setopt_value(ipv6, mtu_discover, V, _D, _T, _P)
  when (V =:= want)  orelse
       (V =:= dont)  orelse
       (V =:= do)    orelse
       (V =:= probe) orelse
       is_integer(V) ->
    V;
enc_setopt_value(ipv6, multicast_hops, V, _D, _T, _P)
  when (V =:= default) ->
    -1;
enc_setopt_value(ipv6, multicast_hops, V, _D, _T, _P)
  when is_integer(V) andalso (V >= 0) andalso (V =< 255) ->
    V;
enc_setopt_value(ipv6, multicast_if, V, _D, _T, _P)
  when is_integer(V) ->
    V;
enc_setopt_value(ipv6, multicast_loop, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ipv6, recverr, V, _D, _T, _P)
  when is_boolean(V) ->
    V;
enc_setopt_value(ipv6, Opt, V, _D, _T, _P)
  when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso 
       is_boolean(V) ->
    V;
enc_setopt_value(ipv6, router_alert, V, _D, T, _P)
  when is_integer(V) andalso (T =:= raw) ->
    V;
enc_setopt_value(ipv6, rthdr, V, _D, T, _P)
  when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
    V;
enc_setopt_value(ipv6, unicast_hops, V, _D, _T, _P)
  when (V =:= default) ->
    -1;
enc_setopt_value(ipv6, unicast_hops, V, _D, _T, _P)
  when is_integer(V) andalso (V >= 0) andalso (V =< 255) ->
    V;
enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) ->
    V;
enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) ->
    not_supported({L, Opt, V});

enc_setopt_value(tcp, congestion, V, _D, T, P)
  when is_list(V) andalso
       (T =:= stream) andalso
       (P =:= tcp) ->
    V;
enc_setopt_value(tcp, maxseg, V, _D, T, P)
  when is_integer(V) andalso
       (T =:= stream) andalso
       (P =:= tcp) ->
    V;
enc_setopt_value(tcp, nodelay, V, _D, T, P)
  when is_boolean(V) andalso
       (T =:= stream) andalso
       (P =:= tcp) ->
    V;
enc_setopt_value(tcp = L, Opt, V, _D, _T, _P) ->
    not_supported({L, Opt, V});

enc_setopt_value(udp, cork, V, _D, T, P)
  when is_boolean(V) andalso (T =:= dgram) andalso (P =:= udp) ->
    V;
enc_setopt_value(udp = L, Opt, _V, _D, _T, _P) ->
    not_supported({L, Opt});

enc_setopt_value(sctp, associnfo, #{assoc_id       := AssocId,
                                    asocmaxrxt     := MaxRxt,
                                    num_peer_dests := NumPeerDests,
                                    peer_rwnd      := PeerRWND,
                                    local_rwnd     := LocalRWND,
                                    cookie_life    := CLife} = V,
                 _D, _T, P)
  when is_integer(AssocId) andalso
       is_integer(MaxRxt)       andalso (MaxRxt >= 0)       andalso
       is_integer(NumPeerDests) andalso (NumPeerDests >= 0) andalso
       is_integer(PeerRWND)     andalso (PeerRWND >= 0)     andalso
       is_integer(LocalRWND)    andalso (LocalRWND >= 0)    andalso
       is_integer(CLife)        andalso (CLife >= 0)        andalso
       (P =:= sctp) ->
    V;
enc_setopt_value(sctp, autoclose, V, _D, _T, P)
  when is_integer(V) andalso (V >= 0) andalso (P =:= sctp) ->
    V;
enc_setopt_value(sctp, disable_fragments, V, _D, _T, P)
  when is_boolean(V) andalso (P =:= sctp) ->
    V;
enc_setopt_value(sctp, events, #{data_in          := DataIn,
                                 association      := Assoc,
                                 address          := Addr,
                                 send_failure     := SndFailure,
                                 peer_error       := PeerError,
                                 shutdown         := Shutdown,
                                 partial_delivery := PartialDelivery,
                                 adaptation_layer := AdaptLayer,
                                 authentication   := Auth,
                                 sender_dry       := SndDry} = V, _D, _T, P)
  when is_boolean(DataIn)          andalso
       is_boolean(Assoc)           andalso
       is_boolean(Addr)            andalso
       is_boolean(SndFailure)      andalso
       is_boolean(PeerError)       andalso
       is_boolean(Shutdown)        andalso
       is_boolean(PartialDelivery) andalso
       is_boolean(AdaptLayer)      andalso
       is_boolean(Auth)            andalso
       is_boolean(SndDry)          andalso
       (P =:= sctp) ->
    V;
enc_setopt_value(sctp, initmsg, #{num_outstreams := NumOut,
                                  max_instreams  := MaxIn,
                                  max_attempts   := MaxAttempts,
                                  max_init_timeo := MaxInitTO} = V,
                 _D, _T, P)
  when is_integer(NumOut)      andalso (NumOut >= 0)      andalso
       is_integer(MaxIn)       andalso (MaxIn >= 0)       andalso
       is_integer(MaxAttempts) andalso (MaxAttempts >= 0) andalso
       is_integer(MaxInitTO)   andalso (MaxInitTO >= 0)   andalso
       (P =:= sctp) ->
    V;
enc_setopt_value(sctp, maxseg, V, _D, _T, P)
  when is_integer(V) andalso (V >= 0) andalso (P =:= sctp) ->
    V;
enc_setopt_value(sctp, nodelay, V, _D, _T, P)
  when is_boolean(V) andalso (P =:= sctp) ->
    V;
enc_setopt_value(sctp, rtoinfo, #{assoc_id := AssocId,
                                  initial  := Init,
                                  max      := Max,
                                  min      := Min} = V,
                 _D, _T, P)
  when is_integer(AssocId) andalso
       is_integer(Init) andalso (Init >= 0) andalso
       is_integer(Max)  andalso (Max >= 0)  andalso
       is_integer(Min)  andalso (Min >= 0)  andalso
       (P =:= sctp) ->
    V;
enc_setopt_value(sctp = L, Opt, V, _D, _T, _P) ->
    not_supported({L, Opt, V});

enc_setopt_value(raw = L, Opt, _V, _D, _T, _P) ->
    not_supported({L, Opt});

%% Is this correct? What about getopt?
enc_setopt_value(L, Opt, V, _, _, _)
  when is_integer(L) andalso is_integer(Opt) andalso is_binary(V) ->
    V.




%% +++ Encode getopt value +++

enc_getopt_level(Level) ->
    enc_setopt_level(Level).


%% +++ Encode getopt key +++

enc_getopt_key(Level, Opt, Domain, Type, Protocol) ->
    enc_sockopt_key(Level, Opt, get, Domain, Type, Protocol).


%% +++ Decode getopt value +++
%%
%% For the most part, we simply let the value pass through, but for some
%% values we do an actual decode.
%%

%% Let the user deal with this...
dec_getopt_value(_L, _Opt, V, _D, _T, _P) ->
    V.



%% +++ Encode socket option key +++

%% Most options are usable both for set and get, but some are
%% are only available for e.g. get.
-spec enc_sockopt_key(Level, Opt,
                      Direction,
                      Domain, Type, Protocol) -> non_neg_integer() when
      Level     :: otp,
      Direction :: set | get,
      Opt       :: otp_socket_option(),
      Domain    :: domain(),
      Type      :: type(),
      Protocol  :: protocol()
                   ; (Level, Direction, Opt,
                      Domain, Type, Protocol) -> non_neg_integer() when
      Level     :: socket,
      Direction :: set | get,
      Opt       :: socket_option(),
      Domain    :: domain(),
      Type      :: type(),
      Protocol  :: protocol()
                   ; (Level, Direction, Opt,
                      Domain, Type, Protocol) -> non_neg_integer() when
      Level     :: ip,
      Direction :: set | get,
      Opt       :: ip_socket_option(),
      Domain    :: domain(),
      Type      :: type(),
      Protocol  :: protocol()
                   ; (Level, Direction, Opt,
                      Domain, Type, Protocol) -> non_neg_integer() when
      Level     :: ipv6,
      Direction :: set | get,
      Opt       :: ipv6_socket_option(),
      Domain    :: domain(),
      Type      :: type(),
      Protocol  :: protocol()
                   ; (Level, Direction, Opt,
                      Domain, Type, Protocol) -> non_neg_integer() when
      Level     :: tcp,
      Direction :: set | get,
      Opt       :: tcp_socket_option(),
      Domain    :: domain(),
      Type      :: type(),
      Protocol  :: protocol()
                   ; (Level, Direction, Opt,
                      Domain, Type, Protocol) -> non_neg_integer() when
      Level     :: udp,
      Direction :: set | get,
      Opt       :: udp_socket_option(),
      Domain    :: domain(),
      Type      :: type(),
      Protocol  :: protocol()
                   ; (Level, Direction, Opt,
                      Domain, Type, Protocol) -> non_neg_integer() when
      Level     :: sctp,
      Direction :: set | get,
      Opt       :: sctp_socket_option(),
      Domain    :: domain(),
      Type      :: type(),
      Protocol  :: protocol()
                   ; (Level, Direction, Opt,
                      Domain, Type, Protocol) -> non_neg_integer() when
      Level     :: integer(),
      Direction :: set,
      Opt       :: integer(),
      Domain    :: domain(),
      Type      :: type(),
      Protocol  :: protocol()
                   ; (Level, Direction, Opt,
                      Domain, Type, Protocol) -> non_neg_integer() when
      Level     :: integer(),
      Direction :: get,
      Opt       :: {NativeOpt, ValueSize},
      NativeOpt :: integer(),
      ValueSize :: non_neg_integer(),
      Domain    :: domain(),
      Type      :: type(),
      Protocol  :: protocol().


%% +++ OTP socket options +++
enc_sockopt_key(otp, debug, _, _, _, _) ->
    ?SOCKET_OPT_OTP_DEBUG;
enc_sockopt_key(otp, iow, _, _, _, _) ->
    ?SOCKET_OPT_OTP_IOW;
enc_sockopt_key(otp, controlling_process, _, _, _, _) ->
    ?SOCKET_OPT_OTP_CTRL_PROC;
enc_sockopt_key(otp = L, Opt, _, _, _, _) ->
    not_supported({L, Opt});

%% +++ SOCKET socket options +++
enc_sockopt_key(socket = _L, acceptconn = _Opt, get = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_ACCEPTCONN;
enc_sockopt_key(socket = L, acceptfilter = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
%% Before linux 3.8, this socket option could be set.
%% Maximum size of buffer for name: IFNAMSZIZ
%% So, we let the implementation decide.
enc_sockopt_key(socket = _L, bindtodevice = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_BINDTODEVICE;
enc_sockopt_key(socket, broadcast = _Opt, _Dir, _D, dgram = _T, _P) ->
    ?SOCKET_OPT_SOCK_BROADCAST;
enc_sockopt_key(socket = L, busy_poll = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(socket = _L, debug = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_DEBUG;
enc_sockopt_key(socket, domain = _Opt, get = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_DOMAIN;
enc_sockopt_key(socket, dontroute = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_DONTROUTE;
enc_sockopt_key(socket = L, error = Opt, get = _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
%% This is only for connection-oriented sockets, but who are those?
%% Type = stream or Protocol = tcp?
%% For now, we just let is pass and it will fail later if not ok...
enc_sockopt_key(socket, keepalive = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_KEEPALIVE;
enc_sockopt_key(socket, linger = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_LINGER;
enc_sockopt_key(socket = L, mark = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(socket = _L, oobinline = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_OOBINLINE;
enc_sockopt_key(socket = L, passcred = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(socket = _L, peek_off = _Opt, _Dir, local = _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_PEEK_OFF;
enc_sockopt_key(socket = L, peekcred = Opt, get = _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_PRIORITY;
enc_sockopt_key(socket, protocol = _Opt, get = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_PROTOCOL;
enc_sockopt_key(socket, rcvbuf = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_RCVBUF;
enc_sockopt_key(socket = L, rcvbufforce = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
%% May not work on linux.
enc_sockopt_key(socket = _L, rcvlowat = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_RCVLOWAT;
enc_sockopt_key(socket = _L, rcvtimeo = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_RCVTIMEO;
enc_sockopt_key(socket = _L, reuseaddr = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_REUSEADDR;
enc_sockopt_key(socket = _L, reuseport = _Opt, _Dir, D, _T, _P)
  when ((D =:= inet) orelse (D =:= inet6)) ->
    ?SOCKET_OPT_SOCK_REUSEPORT;
enc_sockopt_key(socket = L, rxq_ovfl = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(socket = L, setfib = Opt, set = _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(socket = _L, sndbuf = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_SNDBUF;
enc_sockopt_key(socket = L, sndbufforce = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
%% Not changeable on linux.
enc_sockopt_key(socket = _L, sndlowat = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_SNDLOWAT;
enc_sockopt_key(socket = _L, sndtimeo = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_SNDTIMEO;
enc_sockopt_key(socket = _L, timestamp = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_TIMESTAMP;
enc_sockopt_key(socket = _L, type = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SOCK_TYPE;
enc_sockopt_key(socket = L, UnknownOpt, _Dir, _D, _T, _P) ->
    unknown({L, UnknownOpt});

%% +++ IP socket options +++
enc_sockopt_key(ip = _L, add_membership = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_ADD_MEMBERSHIP;
enc_sockopt_key(ip = _L, add_source_membership = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP;
enc_sockopt_key(ip = _L, block_source = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_BLOCK_SOURCE;
%% FreeBSD only?
%% Only respected on udp and raw ip (unless the hdrincl option has been set).
enc_sockopt_key(ip = L, dontfrag = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ip = _L, drop_membership = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_DROP_MEMBERSHIP;
enc_sockopt_key(ip = _L, drop_source_membership = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP;
%% Linux only?
enc_sockopt_key(ip = _L, freebind = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_FREEBIND;
enc_sockopt_key(ip = _L, hdrincl = _Opt, _Dir, _D, raw = _T, _P) ->
    ?SOCKET_OPT_IP_HDRINCL;
enc_sockopt_key(ip = _L, minttl = _Opt, _Dir, _D, raw = _T, _P) ->
    ?SOCKET_OPT_IP_MINTTL;
enc_sockopt_key(ip = _L, msfilter = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_MSFILTER;
enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_MTU;
enc_sockopt_key(ip = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_MTU_DISCOVER;
enc_sockopt_key(ip = _L, multicast_all = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_MULTICAST_ALL;
enc_sockopt_key(ip = _L, multicast_if = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_MULTICAST_IF;
enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_MULTICAST_LOOP;
enc_sockopt_key(ip = _L, multicast_ttl = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_MULTICAST_TTL;
enc_sockopt_key(ip = _L, nodefrag = _Opt, _Dir, _D, raw = _T, _P) ->
    ?SOCKET_OPT_IP_NODEFRAG;
enc_sockopt_key(ip = L, options = Opt, _Dir, _D, _T, _P) ->
    not_supported({Opt, L});
enc_sockopt_key(ip = _L, pktinfo = _Opt, _Dir, _D, dgram = _T, _P) ->
    ?SOCKET_OPT_IP_PKTINFO;
enc_sockopt_key(ip = _L, recvdstaddr = _Opt, _Dir, _D, T, _P) when (T =:= dgram) ->
    ?SOCKET_OPT_IP_RECVDSTADDR;
enc_sockopt_key(ip = _L, recverr = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_RECVERR;
enc_sockopt_key(ip = _L, recvif = _Opt, _Dir, _D, T, _P)
  when (T =:= dgram) orelse (T =:= raw) ->
    ?SOCKET_OPT_IP_RECVIF;
enc_sockopt_key(ip = _L, recvopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) ->
    ?SOCKET_OPT_IP_RECVOPTS;
enc_sockopt_key(ip = _L, recvorigdstaddr = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_RECVORIGDSTADDR;
enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_RECVTOS;
enc_sockopt_key(ip = _L, recvttl = _Opt, _Dir, _D, T, _P) when (T =/= stream) ->
    ?SOCKET_OPT_IP_RECVTTL;
enc_sockopt_key(ip = _L, retopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) ->
    ?SOCKET_OPT_IP_RETOPTS;
enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) ->
    ?SOCKET_OPT_IP_ROUTER_ALERT;
%% On FreeBSD it specifies that this option is only valid
%% for stream, dgram and "some" raw sockets...
%% No such condition on linux (in the man page)...
enc_sockopt_key(ip, tos = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_TOS;
enc_sockopt_key(ip = _L, transparent = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_TRANSPARENT;
enc_sockopt_key(ip, ttl = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_TTL;
enc_sockopt_key(ip = _L, unblock_source = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IP_UNBLOCK_SOURCE;
enc_sockopt_key(ip = L, UnknownOpt, _Dir, _D, _T, _P) ->
    unknown({L, UnknownOpt});

%% IPv6 socket options
enc_sockopt_key(ipv6 = _L, addrform = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_ADDRFORM;
enc_sockopt_key(ipv6, add_membership = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_ADD_MEMBERSHIP;
enc_sockopt_key(ipv6 = _L, authhdr = _Opt, _Dir, _D, T, _P) 
  when ((T =:= dgram) orelse (T =:= raw)) ->
    ?SOCKET_OPT_IPV6_AUTHHDR;
enc_sockopt_key(ipv6 = L, auth_level = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = L, checksum = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6, drop_membership = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_DROP_MEMBERSHIP;
enc_sockopt_key(ipv6 = _L, dstopts = _Opt, _Dir, _D, T, _P)
  when (T =:= dgram) orelse (T =:= raw) ->
    ?SOCKET_OPT_IPV6_DSTOPTS;
enc_sockopt_key(ipv6 = L, esp_trans_level = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = L, esp_network_level = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = _L, flowinfo = _Opt, _Dir, _D, T, _P)
  when (T =:= dgram) orelse (T =:= raw) ->
    ?SOCKET_OPT_IPV6_DSTOPTS;
enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P)
  when (T =:= dgram) orelse (T =:= raw) ->
    ?SOCKET_OPT_IPV6_HOPLIMIT;
enc_sockopt_key(ipv6 = _L, hopopts = _Opt, _Dir, _D, T, _P) 
  when ((T =:= dgram) orelse (T =:= raw)) ->
    ?SOCKET_OPT_IPV6_HOPOPTS;
enc_sockopt_key(ipv6 = L, ipcomp_level = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = L, join_group = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = L, leave_group = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = _L, mtu = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_MTU;
enc_sockopt_key(ipv6 = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_MTU_DISCOVER;
enc_sockopt_key(ipv6 = _L, multicast_hops = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_MULTICAST_HOPS;
enc_sockopt_key(ipv6 = _L, multicast_if = _Opt, _Dir, _D, T, _P) 
  when (T =:= dgram) orelse (T =:= raw) ->
    ?SOCKET_OPT_IPV6_MULTICAST_IF;
enc_sockopt_key(ipv6 = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_MULTICAST_LOOP;
enc_sockopt_key(ipv6 = L, portrange = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = L, pktoptions = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = _L, recverr = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_RECVERR;
enc_sockopt_key(ipv6 = _L, Opt, _Dir, _D, T, _P) 
  when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso 
       ((T =:= dgram) orelse (T =:= raw)) ->
    ?SOCKET_OPT_IPV6_RECVPKTINFO;
enc_sockopt_key(ipv6 = L, recvtclass = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = _L, router_alert = _Opt, _Dir, _D, T, _P) when (T =:= raw) ->
    ?SOCKET_OPT_IPV6_ROUTER_ALERT;
enc_sockopt_key(ipv6 = _L, rthdr = _Opt, _Dir, _D, T, _P) 
  when ((T =:= dgram) orelse (T =:= raw)) ->
    ?SOCKET_OPT_IPV6_RTHDR;
enc_sockopt_key(ipv6 = L, tclass = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = _L, unicast_hops = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_UNICAST_HOPS;
enc_sockopt_key(ipv6 = L, use_min_mtu = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(ipv6 = _L, v6only = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_IPV6_V6ONLY;
enc_sockopt_key(ipv6 = L, UnknownOpt, _Dir, _D, _T, _P) ->
    unknown({L, UnknownOpt});

%% TCP socket options
%% There are other options that would be useful; info,
%% but they are difficult to get portable...
enc_sockopt_key(tcp, congestion = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_TCP_CONGESTION;
enc_sockopt_key(tcp = L, cork = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(tcp = L, keepidle = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(tcp = L, keepintvl = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(tcp = L, keepcnt = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(tcp, maxseg = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_TCP_MAXSEG;
enc_sockopt_key(tcp = L, md5sig = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(tcp, nodelay = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_TCP_NODELAY;
enc_sockopt_key(tcp = L, noopt = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(tcp = L, nopush = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(tcp = L, syncnt = Opt, _Dir, _D, _T, _P) -> % Only set? 1..255
    not_supported({L, Opt});
enc_sockopt_key(tcp = L, user_timeout = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(tcp = L, UnknownOpt, _Dir, _D, _T, _P) ->
    unknown({L, UnknownOpt});

%% UDP socket options
enc_sockopt_key(udp, cork = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_UDP_CORK;
enc_sockopt_key(udp = L, UnknownOpt, _Dir, _D, _T, _P) ->
    unknown({L, UnknownOpt});

%% SCTP socket options
enc_sockopt_key(sctp = L, adaption_layer = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = _L, associnfo = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SCTP_ASSOCINFO;
enc_sockopt_key(sctp = L, auth_active_key = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, auth_asconf = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, auth_chunk = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, auth_key = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, auth_delete_key = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp, autoclose = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SCTP_AUTOCLOSE;
enc_sockopt_key(sctp = L, context = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, default_send_params = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, delayed_ack_time = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = _L, disable_fragments = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SCTP_DISABLE_FRAGMENTS;
enc_sockopt_key(sctp = L, hmac_ident = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = _L, events = _Opt, set = _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SCTP_EVENTS;
enc_sockopt_key(sctp = L, explicit_eor = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, fragment_interleave = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, get_peer_addr_info = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = _L, initmsg = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SCTP_INITMSG;
enc_sockopt_key(sctp = L, i_want_mapped_v4_addr = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, local_auth_chunks = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = _L, maxseg = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SCTP_MAXSEG;
enc_sockopt_key(sctp = L, maxburst = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp, nodelay = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SCTP_NODELAY;
enc_sockopt_key(sctp = L, partial_delivery_point = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, peer_addr_params = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, peer_auth_chunks = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, primary_addr = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, reset_streams = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = _L, rtoinfo = _Opt, _Dir, _D, _T, _P) ->
    ?SOCKET_OPT_SCTP_RTOINFO;
enc_sockopt_key(sctp = L, set_peer_primary_addr = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, status = Opt, get = _Dir, _D, _T, _P) ->
    not_supported({L, Opt}); % ?SOCKET_OPT_SCTP_RTOINFO;
enc_sockopt_key(sctp = L, use_exp_recvinfo = Opt, _Dir, _D, _T, _P) ->
    not_supported({L, Opt});
enc_sockopt_key(sctp = L, UnknownOpt, _Dir, _D, _T, _P) ->
    unknown({L, UnknownOpt});

%% +++ "Native" socket options +++
enc_sockopt_key(Level, Opt, set = _Dir, _D, _T, _P)
  when is_integer(Level) andalso is_integer(Opt) ->
    Opt;
enc_sockopt_key(Level, {NativeOpt, ValueSize} = Opt, get = _Dir, _D, _T, _P)
  when is_integer(Level) andalso
       is_integer(NativeOpt) andalso
       ((is_integer(ValueSize) andalso (ValueSize >= 0)) orelse
        ((ValueSize =:= int) orelse (ValueSize =:= bool))) ->
    Opt;

enc_sockopt_key(Level, Opt, _Dir, _Domain, _Type, _Protocol) ->
    unknown({Level, Opt}).



enc_shutdown_how(read) ->
    ?SOCKET_SHUTDOWN_HOW_READ;
enc_shutdown_how(write) ->
    ?SOCKET_SHUTDOWN_HOW_WRITE;
enc_shutdown_how(read_write) ->
    ?SOCKET_SHUTDOWN_HOW_READ_WRITE.




%% ===========================================================================
%%
%% Misc utility functions
%%
%% ===========================================================================

ensure_ip_msfilter_slist(SL) ->
    EnsureSA = fun(SA) when is_tuple(SA) andalso (size(SA) =:= 4) -> ok;
                  (_) -> einval()
               end,
    lists:foreach(EnsureSA, SL).


ensure_sockaddr(#{family := inet} = SockAddr) ->
    maps:merge(?SOCKADDR_IN4_DEFAULTS, SockAddr);
ensure_sockaddr(#{family := inet6} = SockAddr) ->
    maps:merge(?SOCKADDR_IN6_DEFAULTS, SockAddr);
ensure_sockaddr(#{family := local, path := Path} = SockAddr) 
  when is_list(Path) andalso 
       (length(Path) > 0) andalso 
       (length(Path) =< 255) ->
    BinPath = unicode:characters_to_binary(Path, file:native_name_encoding()),
    ensure_sockaddr(SockAddr#{path => BinPath});
ensure_sockaddr(#{family := local, path := Path} = SockAddr) 
  when is_binary(Path) andalso 
       (byte_size(Path) > 0) andalso 
       (byte_size(Path) =< 255) ->
    SockAddr;
ensure_sockaddr(_SockAddr) ->
    einval().



flush_select_msgs(LSRef, Ref) ->
    receive
        {select, LSRef, Ref, _} ->
            flush_select_msgs(LSRef, Ref)
    after 0 ->
            ok
    end.


%% formated_timestamp() ->
%%     format_timestamp(os:timestamp()).

%% format_timestamp(Now) ->
%%     N2T = fun(N) -> calendar:now_to_local_time(N) end,
%%     format_timestamp(Now, N2T, true).

%% format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
%%     FormatExtra = ".~.2.0w",
%%     ArgsExtra   = [N3 div 10000],
%%     format_timestamp(N, N2T, FormatExtra, ArgsExtra);
%% format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
%%     FormatExtra = "",
%%     ArgsExtra   = [],
%%     format_timestamp(N, N2T, FormatExtra, ArgsExtra).

%% format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
%%     {Date, Time}   = N2T(N),
%%     {YYYY,MM,DD}   = Date,
%%     {Hour,Min,Sec} = Time,
%%     FormatDate =
%%         io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
%%                       [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
%%     lists:flatten(FormatDate).


%% A timestamp in ms

timestamp(infinity) ->
    undefined;
timestamp(_) ->
    timestamp().

timestamp() ->
    {A,B,C} = os:timestamp(),
    A*1000000000+B*1000+(C div 1000).

next_timeout(_, infinity = Timeout) ->
    Timeout;
next_timeout(TS, Timeout) ->
    NewTimeout = Timeout - tdiff(TS, timestamp()),
    if
        (NewTimeout > 0) ->
            NewTimeout;
        true ->
            0
    end.

tdiff(T1, T2) ->
    T2 - T1.



%% p(F) ->
%%     p(F, []).

%% p(F, A) ->
%%     p(get(sname), F, A).

%% p(undefined, F, A) ->
%%     p("***", F, A);
%% p(SName, F, A) ->
%%     io:format("[~s,~p] " ++ F ++ "~n", [SName, self()|A]).



%% ===========================================================================
%%
%% Error functions
%%
%% ===========================================================================

not_supported(What) ->
    error({not_supported, What}).

unknown(What) ->
    error({unknown, What}).

einval() ->
    error(einval).

error(Reason) ->
    throw({error, Reason}).


%% ===========================================================================
%%
%% Below follows the actual NIF-functions.
%%
%% ===========================================================================

nif_is_loaded() -> false.

nif_info() ->
    erlang:error(badarg).

nif_open(_Domain, _Type, _Protocol, _Extra) ->
    erlang:error(badarg).

nif_bind(_SRef, _SockAddr) ->
    erlang:error(badarg).

nif_bind(_SRef, _SockAddrs, _Action) ->
    erlang:error(badarg).

nif_connect(_SRef, _SockAddr) ->
    erlang:error(badarg).

nif_finalize_connection(_SRef) ->
    erlang:error(badarg).

nif_listen(_SRef, _Backlog) ->
    erlang:error(badarg).

nif_accept(_SRef, _Ref) ->
    erlang:error(badarg).

nif_send(_SockRef, _SendRef, _Data, _Flags) ->
    erlang:error(badarg).

nif_sendto(_SRef, _SendRef, _Data, _Dest, _Flags) ->
    erlang:error(badarg).

nif_recv(_SRef, _RecvRef, _Length, _Flags) ->
    erlang:error(badarg).

nif_recvfrom(_SRef, _RecvRef, _Length, _Flags) ->
    erlang:error(badarg).

nif_cancel(_SRef, _Op, _Ref) ->
    erlang:error(badarg).

nif_close(_SRef) ->
    erlang:error(badarg).

nif_shutdown(_SRef, _How) ->
    erlang:error(badarg).

nif_finalize_close(_SRef) ->
    erlang:error(badarg).

nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) ->
    erlang:error(badarg).

nif_getopt(_Ref, _IsEnc, _Lev, _Key) ->
    erlang:error(badarg).

nif_sockname(_Ref) ->
    erlang:error(badarg).

nif_peername(_Ref) ->
    erlang:error(badarg).