Tcp connect error codes

TCP socket error codes. GitHub Gist: instantly share code, notes, and snippets.
0 = Success 1 = Operation not permitted 2 = No such file or directory 3 = No such process 4 = Interrupted system call 5 = Input/output error 6 = No such device or address 7 = Argument list too long 8 = Exec format error 9 = Bad file descriptor 10 = No child processes 11 = Resource temporarily unavailable 12 = Cannot allocate memory 13 = Permission denied 14 = Bad address 15 = Block device required 16 = Device or resource busy 17 = File exists 18 = Invalid cross-device link 19 = No such device 20 = Not a directory 21 = Is a directory 22 = Invalid argument 23 = Too many open files in system 24 = Too many open files 25 = Inappropriate ioctl for device 26 = Text file busy 27 = File too large 28 = No space left on device 29 = Illegal seek 30 = Read-only file system 31 = Too many links 32 = Broken pipe 33 = Numerical argument out of domain 34 = Numerical result out of range 35 = Resource deadlock avoided 36 = File name too long 37 = No locks available 38 = Function not implemented 39 = Directory not empty 40 = Too many levels of symbolic links 41 = Unknown error 41 42 = No message of desired type 43 = Identifier removed 44 = Channel number out of range 45 = Level 2 not synchronized 46 = Level 3 halted 47 = Level 3 reset 48 = Link number out of range 49 = Protocol driver not attached 50 = No CSI structure available 51 = Level 2 halted 52 = Invalid exchange 53 = Invalid request descriptor 54 = Exchange full 55 = No anode 56 = Invalid request code 57 = Invalid slot 58 = Unknown error 58 59 = Bad font file format 60 = Device not a stream 61 = No data available 62 = Timer expired 63 = Out of streams resources 64 = Machine is not on the network 65 = Package not installed 66 = Object is remote 67 = Link has been severed 68 = Advertise error 69 = Srmount error 70 = Communication error on send 71 = Protocol error 72 = Multihop attempted 73 = RFS specific error 74 = Bad message 75 = Value too large for defined data type 76 = Name not unique on network 77 = File descriptor in bad state 78 = Remote address changed 79 = Can not access a needed shared library 80 = Accessing a corrupted shared library 81 = .lib section in a.out corrupted 82 = Attempting to link in too many shared libraries 83 = Cannot exec a shared library directly 84 = Invalid or incomplete multibyte or wide character 85 = Interrupted system call should be restarted 86 = Streams pipe error 87 = Too many users 88 = Socket operation on non-socket 89 = Destination address required 90 = Message too long 91 = Protocol wrong type for socket 92 = Protocol not available 93 = Protocol not supported 94 = Socket type not supported 95 = Operation not supported 96 = Protocol family not supported 97 = Address family not supported by protocol 98 = Address already in use 99 = Cannot assign requested address 100 = Network is down 101 = Network is unreachable 102 = Network dropped connection on reset 103 = Software caused connection abort 104 = Connection reset by peer 105 = No buffer space available 106 = Transport endpoint is already connected 107 = Transport endpoint is not connected 108 = Cannot send after transport endpoint shutdown 109 = Too many references: cannot splice 110 = Connection timed out 111 = Connection refused 112 = Host is down 113 = No route to host 114 = Operation already in progress 115 = Operation now in progress 116 = Stale NFS file handle 117 = Structure needs cleaning 118 = Not a XENIX named type file 119 = No XENIX semaphores available 120 = Is a named type file 121 = Remote I/O error 122 = Disk quota exceeded 123 = No medium found 124 = Wrong medium type

Содержание

  1. TCP/IP communication troubleshooting guidance
  2. Troubleshooting tools
  3. Troubleshooting checklist
  4. Step 1: Capture a network diagram
  5. Step 2: Networking traces
  6. Step 3: Ping the computer’s local IP address
  7. Step 4: Troubleshoot error messages that occurs during the ping or telnet test
  8. Step 5: Ping or Telnet to the default gateway
  9. Step 6: Check issues that affects the specific destination node
  10. Common issues and solutions
  11. TCP/IP connection to a host appears to have stopped
  12. Long connect times when using Lmhosts for name resolution
  13. System error 53 occurred
  14. Can’t connect to a specific server
  15. Unable to add a default gateway
  16. Data collection
  17. Prerequisites
  18. Gather key information before contacting Microsoft support
  19. Tcp connect error codes
  20. Introduction¶
  21. socket Function¶
  22. AF_ xxx Versus PF_ xxx¶
  23. connect Function¶
  24. Example: nonexistent host on the local subnet *¶
  25. Example: no server process running *¶
  26. Example: destination not reachable on the Internet *¶
  27. bind Function¶
  28. Wildcard Address and INADDR_ANY *¶
  29. Binding a non-wildcard IP address *¶
  30. listen Function¶
  31. Connection queues *¶
  32. Packet exchanges during conenction establishment *¶
  33. The backlog argument *¶
  34. SYN Flooding *¶
  35. accept Function¶
  36. Example: Value-Result Arguments¶
  37. fork and exec Functions¶
  38. Concurrent Servers¶
  39. Reference count of sockets *¶
  40. Visualizing the sockets and connection *¶
  41. close Function¶
  42. Descriptor Reference Counts¶
  43. getsockname and getpeername Functions¶
  44. Example: Obtaining the Address Family of a Socket¶

TCP/IP communication troubleshooting guidance

This article is designed to help you troubleshoot TCP/IP communication issues.

The ping command is useful to test basic connectivity. However, you shouldn’t rely upon it to prove overall connectivity. Telnet and PsPing are more useful, for the following reasons:

  • These tools can test connectivity to the application layer by using TCP or UDP (PsPing only) as the transport protocol.
  • You can specify which port will be used. Therefore, you can navigate open ports on a firewall.
  • You can connect to any «listening» port on the destination node to verify access to a specific application’s port.

Troubleshooting checklist

Step 1: Capture a network diagram

Capture a network diagram that details the devices that are in the path to the affected area. Specifically, note the following devices:

  • Firewalls
  • IPS (Intrusion Protection/Prevention Systems)
  • DPI (Deep Packet Inspection)
  • WAN accelerators

The diagram can help you visualize and identify where to look for the cause of the issue.

Step 2: Networking traces

Networking traces are useful to see what’s occurring at the network level when the issue occurs.

Step 3: Ping the computer’s local IP address

Try to ping the computer’s local IP address.

If the node can’t ping its local IP, the local stack isn’t working. Note any error messages that are displayed.

If you receive a General Failure error, this error means that there are no valid interfaces to process the request. This issue could be caused by a hardware issue or a stack issue.

Check whether you see a red «X» character or a yellow exclamation mark on the Network Connection icon in the system tray. A red X indicates that Windows isn’t detecting a network connection. A yellow exclamation mark indicates that the Network Connection Status Indicator (NSCI) failed a probe check.

To troubleshoot this issue, verify that the network adapter reports connectivity. Make sure that the network adapter is plugged in and that the switch port where the node is connected isn’t in an error state. You can change cables, switch ports, and network adapters to narrow down where this issue occurs. However, ultimately, the issue is outside the OS. To investigate further, contact the hardware vendors.

An issue might also occur between the network driver and Windows. This issue is typically because of a corruption in the stack. Use the following troubleshooting steps:

Make sure that the latest bits on the node (TCP/IP, NDIS, AFD, Winsock, and so on).

Reset IP and Winsock by running the following commands. Back up all the network configuration.

Restart the node.

Restore the network settings after the restart. This operation works only if the interface names haven’t changed, or the script is updated to use the new names.

Uninstall or reinstall the network adapter driver, as appropriate.

Check for and remove third-party filter drivers (for example, antivirus).

Try to start the computer in Safe Mode with Networking. If Safe Mode with Networking works, run a «clean boot» process by disabling all third-party apps and services within MSConfig, then re-enabling them one by one until the issue returns. You can then contact the vendor for support.

  1. If none of these items are successful, the issue is likely a registry corruption.
  2. If you have a backup copy of the registry (such as a physical backup or a system restore point), you can try to restore the node to a previously working configuration. Catching the root cause of the corruption can be difficult and extremely time consuming. Even if the corruption is found, knowing what caused it’s even more challenging. Modifying the corrupted registry key manually puts the node into an unsupported state. As such, we recommend that the customer restore or reload the node in order to correct the corruption.

If the NSCI fails its probe check (yellow exclamation mark), this doesn’t necessarily indicate a connectivity problem. Make sure that typical communication is occurring as it should.

  • If so, the investigation should focus specifically on why the NCSI is failing its probe checks. The details for this are covered in a separate topic.
  • If not, investigate the connectivity issues first because this would likely get corrected after connectivity is restored.

Step 4: Troubleshoot error messages that occurs during the ping or telnet test

If the node can ping or telnet to nodes on the same subnet or network segment, this would confirm that external connectivity is working. Further testing is still required to understand whether a basic connectivity issue exists.

If the node can’t ping/telnet to nodes on the same subnet/network segment. Note any error messages that are displayed.

Destination host unreachable error means that the ARP requests that are sent by the node aren’t getting a response.

Gather a two-sided trace from the nodes that you’re testing between. Make sure that the ARP request that’s sent by the source node reaches the destination node and that the destination node replies accordingly. This reply should be seen back in the source trace. If this process fails, the issue is likely a misconfiguration or other issues that affect the infrastructure.

Possible causes could be:

  1. Incorrect or mismatched VLANs.
  2. An incorrect switch port configuration (trunk versus access port).
  3. Other hardware issues.

The Request timed out error means that the ARP request got a response, but the ICMP Echo Request that’s sent by the node isn’t getting an ICMP echo response. This, alone, doesn’t indicate an issue. ICMP traffic could be blocked by the network or firewall software on the nodes. It could be beneficial to turn off the firewall profiles (Windows) or disable them through the firewall vendor’s supported method for testing ICMP.

  1. Telnet and PsPing are better suited for testing. Run Telnet or PsPing from the source node to the destination node on a listening port (such as 445).
  2. If step 1 is successful, external connectivity is working. Continue testing basic connectivity.
  3. If step 1 isn’t successful (and if firewall profiles are disabled), gather a two-sided netsh netconnection scenario trace to troubleshoot further.

Step 5: Ping or Telnet to the default gateway

When the node can ping its default gateway, then external connectivity (such as off-box connectivity) is possible from the source node. Further testing would still be required to understand whether a basic connectivity issue exists. If the node can’t ping or Telnet to its default gateway, this means that the ICMP replies are disabled on the router.

Step 6: Check issues that affects the specific destination node

If the source node can ping, Telnet, or PsPing to other nodes on the destination subnet, then basic connectivity and routing within the infrastructure is working. This outcome points to an issue that affects the specific destination node.

Try to Telnet or PsPing to the specific port that the application is listening on (for example, TCP port 445 for SMB). If the connection is successful, then basic application-level connectivity can be confirmed. In this situation, you’ll have to contact the application vendor to help investigate why the application doesn’t connect.

The application vendor could be Microsoft if the issue is a failure to connect to a share, for example. In these situations, it would be useful to take two-sided netsh netconnection scenario trace to gather additional information and help you to verify that there are no issues in the networking stack.

If the connection to the specific port isn’t successful:

    Make sure that the port is in a ‘listening’ state:
    CMD: netstat -nato | findstr :

PowerShell: Get-NetTcpConnection -LocalPort

If the node can’t ping, Telnet, or PsPing to other nodes on the destination subnet, the issue could likely be infrastructure-related. Again, ICMP could be blocked within the environment. Therefore, verify connectivity by using Telnet or PsPing to connect to known-listening ports. At this point, a two-sided network trace is necessary to show where the packet loss is occurring on the network. Make sure that both traces are running before you try the connectivity test so that the issue is captured.

Common issues and solutions

TCP/IP connection to a host appears to have stopped

This issue occurs because either data is blocked in TCP and UDP queues or there are network or user-level software delay problems.

To troubleshoot this issue, use the netstat -a command to show the status of all activity on TCP and UDP ports on the local computer.
The state of a good TCP connection is established while having zero (0) bytes in the send and receive queues. If data is blocked in either queue, or if the state is irregular, the connection is probably at fault. If not, you’re probably experiencing a network or user-level software delay.

Long connect times when using Lmhosts for name resolution

This issue occurs because the Lmhosts file is parsed sequentially to locate entries without the #PRE option.

To troubleshoot this issue, put frequently used entries near the top of the file and the #PRE entries near the bottom. If an entry is added to the end of a large Lmhosts file, mark the entry in Lmhosts as a preloaded entry by using the #PRE option. Then, run the nbtstat -R command to update the local name cache immediately.

System error 53 occurred

System error 53 is returned if name resolution fails for a particular computer name when the net use command is used.

If the computer is on the local subnet, verify that the name is spelled correctly and that the target computer is also running TCP/IP. If the computer isn’t on the local subnet, make sure that its name and IP address mapping are available in the Lmhosts file or the WINS database. If all TCP/IP elements appear to be installed properly, use the ping command together with the remote computer to verify that its TCP/IP software is working.

Can’t connect to a specific server

This issue occurs because either NetBIOS name resolution isn’t resolving the name or the wrong IP address is being resolved.

To troubleshoot this issue, use the nbtstat -n command on the server to determine which names the server registered on the network. The computer name of the computer that you’re trying to connect to should be on the displayed list. If the name isn’t listed, try one of the other unique computer names that are displayed by nbtstat . If the name that’s used by a remote computer is the same as the name that’s displayed by the nbtstat -n command, make sure that the remote computer has an entry for the server name that’s on the WINS server or in its Lmhosts file.

Unable to add a default gateway

This issue occurs because the IP address of the default gateway isn’t on the same IP network ID as your IP address.

To troubleshoot this issue, determine whether the default gateway is located on the same logical network as the network adapter of the computer by comparing the IP address of the default gateway with the network IDs of any of the network adapters of the computer.

For example, a computer has a single network adapter that’s configured with an IP address of 192.168.0.33 and a subnet mask of 255.255.0.0. This requires that the default gateway to be of the form «192.168. . » because the network ID portion of the IP interface is 192.168.0.0.

Data collection

Before contacting Microsoft support, you can gather information about your issue.

Prerequisites

  1. TSSv2 must be run by accounts with administrator privileges on the local system, and EULA must be accepted (once EULA is accepted, TSSv2 won’t prompt again).
  2. We recommend the local machine RemoteSigned PowerShell execution policy.

If the current PowerShell execution policy doesn’t allow running TSSv2, take the following actions:

  • Set the RemoteSigned execution policy for the process level by running the cmdlet PS C:> Set-ExecutionPolicy -scope Process -ExecutionPolicy RemoteSigned .
  • To verify if the change takes effect, run the cmdlet PS C:> Get-ExecutionPolicy -List .
  • Because the process level permissions only apply to the current PowerShell session, once the given PowerShell window in which TSSv2 runs is closed, the assigned permission for the process level will also go back to the previously configured state.

Gather key information before contacting Microsoft support

Download TSSv2 on all nodes and unzip it in the C:tss_tool folder.

Open the C:tss_tool folder from an elevated PowerShell command prompt.

Start the traces on the source and destination server by using the following cmdlet:

Accept the EULA if the traces are run for the first time on the source or the destination server.

Allow recording (PSR or video).

Reproduce the issue before entering Y.

If you collect logs on both the client and the server, wait for this message on both nodes before reproducing the issue.

Enter Y to finish the log collection after the issue is reproduced.

The traces will be stored in a zip file in the C:MSDATA folder, which can be uploaded to the workspace for analysis.

Источник

Tcp connect error codes

In C, we cannot represent a constant structure on the right-hand side of an assignment. UNP

Introduction¶

This chapter describes the elementary socket functions required to write a complete TCP client and server, along with concurrent servers, a common Unix technique for providing concurrency when numerous clients are connected to the same server at the same time. Each client connection causes the server to fork a new process just for that client. In this chapter, we consider only the one-process-per-client model using fork .

The figure below shows a timeline of the typical scenario that takes place between a TCP client and server. First, the server is started, then sometime later, a client is started that connects to the server. We assume that the client sends a request to the server, the server processes the request, and the server sends a reply back to the client. This continues until the client closes its end of the connection, which sends an end-of-file notification to the server. The server then closes its end of the connection and either terminates or waits for a new client connection.

socket Function¶

To perform network I/O, the first thing a process must do is call the socket function, specifying the type of communication protocol desired (TCP using IPv4, UDP using IPv6, Unix domain stream protocol, etc.).

family specifies the protocol family and is one of the constants in the table below. This argument is often referred to as domain instead of family.

family Description
AF_INET IPv4 protocols
AF_INET6 IPv6 protocols
AF_LOCAL Unix domain protocols (Chapter 15)
AF_ROUTE Routing sockets (Chapter 18)
AF_KEY Key socket (Chapter 19)

The socket type is one of the constants shown in table below:

type Description
SOCK_STREAM stream socket
SOCK_DGRAM datagram socket
SOCK_SEQPACKET sequenced packet socket
SOCK_RAW raw socket

The protocol argument to the socket function should be set to the specific protocol type found in the table below, or 0 to select the system’s default for the given combination of family and type.

protocol Description
IPPROTO_TCP TCP transport protocol
IPPROTO_UDP UDP transport protocol
IPPROTO_SCTP SCTP transport protocol

Not all combinations of socket family and type are valid. The table below shows the valid combinations, along with the actual protocols that are valid for each pair. The boxes marked «Yes» are valid but do not have handy acronyms. The blank boxes are not supported.

AF_INET AF_INET6 AF_LOCAL AF_ROUTE AF_KEY
SOCK_STREAM TCP/SCTP TCP/SCTP Yes
SOCK_DGRAM UDP UDP Yes
SOCK_SEQPACKET SCTP SCTP Yes
SOCK_RAW IPv4 IPv6 Yes Yes
  • You may also encounter the corresponding PF_ xxx constant as the first argument to socket. This is discussed in the next section in this Chapter.
  • You may encounter AF_UNIX (the historical Unix name) instead of AF_LOCAL (the POSIX name). This is discussed in Chapter 15.
  • Linux supports a new socket type, SOCK_PACKET , that provides access to the datalink, similar to BPF and DLPI (Section 2.2). This is discussed in Chapter 29
  • The key socket, AF_KEY , is newer than the others. It provides support for cryptographic security. Similar to the way that a routing socket ( AF_ROUTE ) is an interface to the kernel’s routing table, the key socket is an interface into the kernel’s key table. This is discussed in Chapter 19.

On success, the socket function returns a small non-negative integer value, similar to a file descriptor. We call this a socket descriptor, or a sockfd. To obtain this socket descriptor, all we have specified is a protocol family (IPv4, IPv6, or Unix) and the socket type (stream, datagram, or raw). We have not yet specified either the local protocol address or the foreign protocol address.

AF_ xxx Versus PF_ xxx

The » AF_ » prefix stands for «address family» and the » PF_ » prefix stands for «protocol family.» Historically, the intent was that a single protocol family might support multiple address families and that the PF_ value was used to create the socket and the AF_ value was used in socket address structures. But in actuality, a protocol family supporting multiple address families has never been supported and the header defines the PF_ value for a given protocol to be equal to the AF_ value for that protocol. While there is no guarantee that this equality between the two will always be true, should anyone change this for existing protocols, lots of existing code would break.

To conform to existing coding practice, we use only the AF_ constants in this text, although you may encounter the PF_ value, mainly in calls to socket .

connect Function¶

The connect function is used by a TCP client to establish a connection with a TCP server.

  • sockfd is a socket descriptor returned by the socket function.
  • The servaddr and addrlen arguments are a pointer to a socket address structure (which contains the IP address and port number of the server) and its size. (Section 3.3)

The client does not have to call bind before calling connect : the kernel will choose both an ephemeral port and the source IP address if necessary.

In the case of a TCP socket, the connect function initiates TCP’s three-way handshake (Section 2.6). The function returns only when the connection is established or an error occurs. There are several different error returns possible:

  1. If the client TCP receives no response to its SYN segment, ETIMEDOUT is returned.
    • For example, in 4.4BSD, the client sends one SYN when connect is called, sends another SYN 6 seconds later, and sends another SYN 24 seconds later. If no response is received after a total of 75 seconds, the error is returned.
    • Some systems provide administrative control over this timeout.
  2. If the server’s response to the client’s SYN is a reset (RST), this indicates that no process is waiting for connections on the server host at the port specified (the server process is probably not running). This is a hard error and the error ECONNREFUSED is returned to the client as soon as the RST is received. An RST is a type of TCP segment that is sent by TCP when something is wrong. Three conditions that generate an RST are:
    • When a SYN arrives for a port that has no listening server.
    • When TCP wants to abort an existing connection.
    • When TCP receives a segment for a connection that does not exist.
  3. If the client’s SYN elicits an ICMP «destination unreachable» from some intermediate router, this is considered a soft error. The client kernel saves the message but keeps sending SYNs with the same time between each SYN as in the first scenario. If no response is received after some fixed amount of time (75 seconds for 4.4BSD), the saved ICMP error is returned to the process as either EHOSTUNREACH or ENETUNREACH . It is also possible that the remote system is not reachable by any route in the local system’s forwarding table, or that the connect call returns without waiting at all. Note that network unreachables are considered obsolete, and applications should just treat ENETUNREACH and EHOSTUNREACH as the same error.

Example: nonexistent host on the local subnet *¶

We run the client daytimetcpcli (Figure 1.5) and specify an IP address that is on the local subnet (192.168.1/24) but the host ID (100) is nonexistent. When the client host sends out ARP requests (asking for that host to respond with its hardware address), it will never receive an ARP reply.

We only get the error after the connect times out. Notice that our err_sys function prints the human-readable string associated with the ETIMEDOUT error.

Example: no server process running *¶

We specify a host (a local router) that is not running a daytime server:

The server responds immediately with an RST.

Example: destination not reachable on the Internet *¶

Our final example specifies an IP address that is not reachable on the Internet. If we watch the packets with tcpdump , we see that a router six hops away returns an ICMP host unreachable error.

As with the ETIMEDOUT error, connect returns the EHOSTUNREACH error only after waiting its specified amount of time.

In terms of the TCP state transition diagram (Figure 2.4):

  • connect moves from the CLOSED state (the state in which a socket begins when it is created by the socket function) to the SYN_SENT state, and then, on success, to the ESTABLISHED state.
  • If connect fails, the socket is no longer usable and must be closed. We cannot call connect again on the socket.

In Figure 11.10, we will see that when we call connect in a loop, trying each IP address for a given host until one works, each time connect fails, we must close the socket descriptor and call socket again.

bind Function¶

The bind function assigns a local protocol address to a socket. The protocol address is the combination of either a 32-bit IPv4 address or a 128-bit IPv6 address, along with a 16-bit TCP or UDP port number.

  • The second argument myaddr is a pointer to a protocol-specific addres
  • The third argument addrlen is the size of this address structure.

With TCP, calling bind lets us specify a port number, an IP address, both, or neither.

Servers bind their well-known port when they start. (Figure 1.9) If a TCP client or server does not do this, the kernel chooses an ephemeral port for the socket when either connect or listen is called.

  • It is normal for a TCP client to let the kernel choose an ephemeral port, unless the application requires a reserved port (Figure 2.10)
  • However, it is rare for a TCP server to let the kernel choose an ephemeral port, since servers are known by their well-known port.

Exceptions to this rule are Remote Procedure Call (RPC) servers. They normally let the kernel choose an ephemeral port for their listening socket since this port is then registered with the RPC port mapper. Clients have to contact the port mapper to obtain the ephemeral port before they can connect to the server. This also applies to RPC servers using UDP.

A process can bind a specific IP address to its socket. The IP address must belong to an interface on the host.

  • For a TCP client, this assigns the source IP address that will be used for IP datagrams sent on the socket. Normally, a TCP client does not bind an IP address to its socket. The kernel chooses the source IP address when the socket is connected, based on the outgoing interface that is used, which in turn is based on the route required to reach the server
  • For a TCP server, this restricts the socket to receive incoming client connections destined only to that IP address. If a TCP server does not bind an IP address to its socket, the kernel uses the destination IP address of the client’s SYN as the server’s source IP address.

As mentioned, calling bind lets us specify the IP address, the port, both, or neither. The following table summarizes the values to which we set sin_addr and sin_port , or sin6_addr and sin6_port , depending on the desired result.

IP address Port Result
Wildcard Kernel chooses IP address and port
Wildcard nonzero Kernel chooses IP address, process specifies port
Local IP address Process specifies IP address, kernel chooses port
Local IP address nonzero Process specifies IP address and port
  • If we specify a port number of 0, the kernel chooses an ephemeral port when bind is called.
  • If we specify a wildcard IP address, the kernel does not choose the local IP address until either the socket is connected (TCP) or a datagram is sent on the socket (UDP).

Wildcard Address and INADDR_ANY *¶

With IPv4, the wildcard address is specified by the constant INADDR_ANY , whose value is normally 0. This tells the kernel to choose the IP address. Figure 1.9 has the assignment:

While this works with IPv4, where an IP address is a 32-bit value that can be represented as a simple numeric constant (0 in this case), we cannot use this technique with IPv6, since the 128-bit IPv6 address is stored in a structure. In C we cannot represent a constant structure on the right-hand side of an assignment. To solve this problem, we write:

The system allocates and initializes the in6addr_any variable to the constant IN6ADDR_ANY_INIT . The header contains the extern declaration for in6addr_any .

The value of INADDR_ANY (0) is the same in either network or host byte order, so the use of htonl is not really required. But, since all the INADDR_ constants defined by the header are defined in host byte order, we should use htonl with any of these constants.

If we tell the kernel to choose an ephemeral port number for our socket (by specifying a 0 for port number), bind does not return the chosen value. It cannot return this value since the second argument to bind has the const qualifier. To obtain the value of the ephemeral port assigned by the kernel, we must call getsockname to return the protocol address.

Binding a non-wildcard IP address *¶

A common example of a process binding a non-wildcard IP address to a socket is a host that provides Web servers to multiple organizations:

  • First, each organization has its own domain name, such as www.organization.com.
  • Next, each organization’s domain name maps into a different IP address, but typically on the same subnet.

For example, if the subnet is 198.69.10, the first organization’s IP address could be 198.69.10.128, the next 198.69.10.129, and so on. All these IP addresses are then aliased onto a single network interface (using the alias option of the ifconfig command on 4.4BSD, for example) so that the IP layer will accept incoming datagrams destined for any of the aliased addresses. Finally, one copy of the HTTP server is started for each organization and each copy bind s only the IP address for that organization.

An alternative technique is to run a single server that binds the wildcard address. When a connection arrives, the server calls getsockname to obtain the destination IP address from the client, which in our discussion above could be 198.69.10.128, 198.69.10.129, and so on. The server then handles the client request based on the IP address to which the connection was issued.

One advantage in binding a non-wildcard IP address is that the demultiplexing of a given destination IP address to a given server process is then done by the kernel.

We must be careful to distinguish between the interface on which a packet arrives versus the destination IP address of that packet. In Section 8.8, we will talk about the weak end system model and the strong end system model. Most implementations employ the former, meaning it is okay for a packet to arrive with a destination IP address that identifies an interface other than the interface on which the packet arrives. (This assumes a multihomed host.) Binding a non-wildcard IP address restricts the datagrams that will be delivered to the socket based only on the destination IP address. It says nothing about the arriving interface, unless the host employs the strong end system model.

A common error from bind is EADDRINUSE («Address already in use»), which is detailed in Section 7.5 when discussing the SO_REUSEADDR and SO_REUSEPORT socket options.

listen Function¶

The listen function is called only by a TCP server and it performs two actions:

  1. The listen function converts an unconnected socket into a passive socket, indicating that the kernel should accept incoming connection requests directed to this socket. In terms of the TCP state transition diagram (Figure 2.4), the call to listen moves the socket from the CLOSED state to the LISTEN state.
    • When a socket is created by the socket function (and before calling listen ), it is assumed to be an active socket, that is, a client socket that will issue a connect .
  2. The second argument backlog to this function specifies the maximum number of connections the kernel should queue for this socket.

This function is normally called after both the socket and bind functions and must be called before calling the accept function.

Connection queues *¶

To understand the backlog argument, we must realize that for a given listening socket, the kernel maintains two queues:

  1. An incomplete connection queue, which contains an entry for each SYN that has arrived from a client for which the server is awaiting completion of the TCP three-way handshake. These sockets are in the SYN_RCVD state (Figure 2.4).
  2. A completed connection queue, which contains an entry for each client with whom the TCP three-way handshake has completed. These sockets are in the ESTABLISHED state (Figure 2.4).

These two queues are depicted in the figure below:

When an entry is created on the incomplete queue, the parameters from the listen socket are copied over to the newly created connection. The connection creation mechanism is completely automatic; the server process is not involved.

Packet exchanges during conenction establishment *¶

The following figure depicts the packets exchanged during the connection establishment with these two queues:

  • When a SYN arrives from a client, TCP creates a new entry on the incomplete queue and then responds with the second segment of the three-way handshake: the server’s SYN with an ACK of the client’s SYN (Section 2.6).
  • This entry will remain on the incomplete queue, until:
    • The third segment of the three-way handshake arrives (the client’s ACK of the server’s SYN), or
    • The entry times out. (Berkeley-derived implementations have a timeout of 75 seconds for these incomplete entries.)
  • If the three-way handshake completes normally, the entry moves from the incomplete queue to the end of the completed queue.
  • When the process calls accept :
    • The first entry on the completed queue is returned to the process, or
    • If the queue is empty, the process is put to sleep until an entry is placed onto the completed queue.

The backlog argument *¶

Several points to consider when handling the two queues:

  • Sum of both queues. The backlog argument to the listen function has historically specified the maximum value for the sum of both queues.
  • Multiplied by 1.5. Berkeley-derived implementations add a fudge factor to the backlog: It is multiplied by 1.5.
    • If the backlog specifies the maximum number of completed connections the kernel will queue for a socket, then the reason for the fudge factor is to take into account incomplete connections on the queue.
  • Do not specify value of 0 for backlog, as different implementations interpret this differently (Figure 4.10). If you do not want any clients connecting to your listening socket, close the listening socket.
  • One RTT. If the three-way handshake completes normally (no lost segments and no retransmissions), an entry remains on the incomplete connection queue for one RTT.
  • Configurable maximum value. Many current systems allow the administrator to modify the maximum value for the backlog. Historically, sample code always shows a backlog of 5 (which is adequate today).
  • What value should the application specify for the backlog (5 is often inadequate)? There is no easy answer to this.
    • HTTP servers now specify a larger value, but if the value specified is a constant in the source code, to increase the constant requires recompiling the server.
    • Another method is to allow a command-line option or an environment variable to override the default. It is always acceptable to specify a value that is larger than supported by the kernel, as the kernel should silently truncate the value to the maximum value that it supports, without returning an error. The following example is the wrapper function for listen which allows the environment variable LISTENQ to override the value specified by the caller:
  • Fixed number of connections. Historically the reason for queuing a fixed number of connections is to handle the case of the server process being busy between successive calls to accept . This implies that of the two queues, the completed queue should normally have more entries than the incomplete queue. Again, busy Web servers have shown that this is false. The reason for specifying a large backlog is because the incomplete connection queue can grow as client SYNs arrive, waiting for completion of the three-way handshake.
  • No RST sent if queues are full. If the queues are full when a client SYN arrives, TCP ignores the arriving SYN; it does not send an RST. This is because the condition is considered temporary, and the client TCP will retransmit its SYN, hopefully finding room on the queue in the near future. If the server TCP immediately responded with an RST, the client’s connect would return an error, forcing the application to handle this condition instead of letting TCP’s normal retransmission take over. Also, the client could not differentiate between an RST in response to a SYN meaning «there is no server at this port» versus «there is a server at this port but its queues are full.»
  • Data queued in the socket’s receive buffer. Data that arrives after the three-way handshake completes, but before the server calls accept , should be queued by the server TCP, up to the size of the connected socket’s receive buffer.

The following figure shows actual number of queued connections for values of backlog:

SYN Flooding *¶

SYN flooding is a type of attack (the attacker writes a program to send SYNs at a high rate to the victim) that attempts to fill the incomplete connection queue for one or more TCP ports. Additionally, the source IP address of each SYN is set to a random number (called IP spoofing) so that the server’s SYN/ACK goes nowhere.This also prevents the server from knowing the real IP address of the attacker. By filling the incomplete queue with bogus SYNs, legitimate SYNs are not queued, providing a denial of service to legitimate clients.

The listen ‘s backlog argument should specify the maximum number of completed connections for a given socket that the kernel will queue. The purpose ofto limit completed connections is to stop the kernel from accepting new connection requests for a given socket when the application is not accepting them. If a system implements this interpretation, then the application need not specify huge backlog values just because the server handles lots of client requests or to provide protection against SYN flooding. The kernel handles lots of incomplete connections, regardless of whether they are legitimate or from a hacker. But even with this interpretation, scenarios do occur where the traditional value of 5 is inadequate.

accept Function¶

accept is called by a TCP server to return the next completed connection from the front of the completed connection queue (Figure 4.7). If the completed connection queue is empty, the process is put to sleep (assuming the default of a blocking socket).

The cliaddr and addrlen arguments are used to return the protocol address of the connected peer process (the client). addrlen is a value-result argument (Section 3.3):

  • Before the call, we set the integer value referenced by *addrlen to the size of the socket address structure pointed to by cliaddr;
  • On return, this integer value contains the actual number of bytes stored by the kernel in the socket address structure.

If successful, accept returns a new descriptor automatically created by the kernel. This new descriptor refers to the TCP connection with the client.

  • The listening socket is the first argument (sockfd) to accept (the descriptor created by socket and used as the first argument to both bind and listen ).
  • The connected socket is the return value from accept the connected socket.

It is important to differentiate between these two sockets:

  • A given server normally creates only one listening socket, which then exists for the lifetime of the server.
  • The kernel creates one connected socket for each client connection that is accepted (for which the TCP three-way handshake completes).
  • When the server is finished serving a given client, the connected socket is closed.

This function returns up to three values:

  • An integer return code that is either a new socket descriptor or an error indication,
  • The protocol address of the client process (through the cliaddr pointer),
  • The size of this address (through the addrlen pointer).

If we are not interested in having the protocol address of the client returned, we set both cliaddr and addrlen to null pointers. See intro/daytimetcpsrv.c.

Example: Value-Result Arguments¶

The following code shows how to handle the value-result argument to accept by modifying the code from intro/daytimetcpsrv.c to print the IP address and port of the client:

This program does the following:

  • New declarations. Two new variables are defined:
    • len will be a value-result variable.
    • cliaddr will contain the client’s protocol address.
  • Accept connection and print client’s address.
    • Initialize len to the size of the socket address structure
    • Pass a pointer to the cliaddr structure and a pointer to len as the second and third arguments to accept.
    • Call inet_ntop (Section 3.7) to convert the 32-bit IP address in the socket address structure to a dotted-decimal ASCII string and call ntohs (Section 3.4) to convert the 16-bit port number from network byte order to host byte order.

Run this new server and then run our client on the same host, connecting to our server twice in a row:

We first specify the server’s IP address as the loopback address (127.0.0.1) and then as its own IP address (192.168.1.20). Here is the corresponding server output:

Since our daytime client (Figure 1.5) does not call bind , the kernel chooses the source IP address based on the outgoing interface that is used (Section 4.4).

  • In the first case, the kernel sets the source IP address to the loopback address;
  • In the second case, it sets the address to the IP address of the Ethernet interface.

We can also see in this example that the ephemeral port chosen by the Solaris kernel is 43388, and then 43389

The pound sign (#) as the shell prompt indicates that our server must run with superuser privileges to bind the reserved port of 13. If we do not have superuser privileges, the call to bind will fail:

fork and exec Functions¶

Concurrent Servers¶

The server described in intro/daytimetcpsrv1.c is an iterative server. But when a client request can take longer to service, we do not want to tie up a single server with one client; we want to handle multiple clients at the same time. The simplest way to write a concurrent server under Unix is to fork a child process to handle each client.

The following code shows the outline for a typical concurrent server:

  • When a connection is established, accept returns, the server calls fork, and the child process services the client (on connfd , the connected socket) and the parent process waits for another connection (on listenfd , the listening socket). The parent closes the connected socket since the child handles the new client.
  • We assume that the function doit does whatever is required to service the client. When this function returns, we explicitly close the connected socket in the child. This is not required since the next statement calls exit , and part of process termination is to close all open descriptors by the kernel. Whether to include this explicit call to close or not is a matter of personal programming taste.

Reference count of sockets *¶

Calling close on a TCP socket causes a FIN to be sent, followed by the normal TCP connection termination sequence. Why doesn’t the close of connfd by the parent terminate its connection with the client? To understand what’s happening, we must understand that every file or socket has a reference count. The reference count is maintained in the file table entry. This is a count of the number of descriptors that are currently open that refer to this file or socket. In the above code:

  • After socket returns, the file table entry associated with listenfd has a reference count of 1.
  • After accept returns, the file table entry associated with connfd has a reference count of 1.
  • But, after fork returns, both descriptors are shared (duplicated) between the parent and child, so the file table entries associated with both sockets now have a reference count of 2. Therefore, when the parent closes connfd , it just decrements the reference count from 2 to 1 and that is all.
  • The actual cleanup and de-allocation of the socket does not happen until the reference count reaches 0. This will occur at some time later when the child closes connfd .

Visualizing the sockets and connection *¶

The following figures visualize the sockets and connection in the code above:

Before call to accept returns, the server is blocked in the call to accept and the connection request arrives from the client:

Ater return from accept , the connection is accepted by the kernel and a new socket connfd is created (this is a connected socket and data can now be read and written across the connection):

After fork returns, both descriptors, listenfd and connfd , are shared (duplicated) between the parent and child:

After the parent closes the connected socket and the child closes the listening socket:

This is the desired final state of the sockets. The child is handling the connection with the client and the parent can call accept again on the listening socket, to handle the next client connection.

close Function¶

The normal Unix close function is also used to close a socket and terminate a TCP connection.

The default action of close with a TCP socket is to mark the socket as closed and return to the process immediately. The socket descriptor is no longer usable by the process: It cannot be used as an argument to read or write . But, TCP will try to send any data that is already queued to be sent to the other end, and after this occurs, the normal TCP connection termination sequence takes place.

SO_LINGER socket option (detailed in Section 7.5) lets us change this default action with a TCP socket.

Descriptor Reference Counts¶

As mentioned in end of Section 4.8, when the parent process in our concurrent server closes the connected socket, this just decrements the reference count for the descriptor. Since the reference count was still greater than 0, this call to close did not initiate TCP’s four-packet connection termination sequence. This is the behavior we want with our concurrent server with the connected socket that is shared between the parent and child.

If we really want to send a FIN on a TCP connection, the shutdown function can be used (Section 6.6) instead of close .

Be aware if the parent does not call close for each connected socket returned by accept :

  1. The parent will eventually run out of descriptors (there is usually a limit to the number of descriptors that any process can have open at any time)
  2. None of the client connections will be terminated. When the child closes the connected socket, its reference count will go from 2 to 1 and it will remain at 1 since the parent never close s the connected socket. This will prevent TCP’s connection termination sequence from occurring, and the connection will remain open.

getsockname and getpeername Functions¶

  • getsockname returns the local protocol address associated with a socket.
  • getpeername returns the foreign protocol address associated with a socket.

The addrlen argument for both functions is value-result argument: both functions fill in the socket address structure pointed to by localaddr or peeraddr.

The term «name» in the function name is misleading. These two functions return the protocol address associated with one of the two ends of a network connection, which for IPV4 and IPV6 is the combination of an IP address and port number. These functions have nothing to do with domain names.

These two functions are required for the following reasons:

  • After connect successfully returns in a TCP client that does not call bind , getsockname returns the local IP address and local port number assigned to the connection by the kernel.
  • After calling bind with a port number of 0 (telling the kernel to choose the local port number), getsockname returns the local port number that was assigned.
  • getsockname can be called to obtain the address family of a socket.
  • In a TCP server that bind s the wildcard IP address (intro/daytimetcpsrv.c), once a connection is established with a client ( accept returns successfully), the server can call getsockname to obtain the local IP address assigned to the connection. The socket descriptor argument to getsockname must be that of the connected socket, and not the listening socket.
  • When a server is exec ed by the process that calls accept , the only way the server can obtain the identity of the client is to call getpeername . For example, inetd fork s and exec s a TCP server (follwing figure):
    • inetd calls accept , which return two values: the connected socket descriptor ( connfd , return value of the function) and the «peer’s address» (an Internet socket address structure) that contains the IP address and port number of the client.
    • fork is called and a child of inetd is created, with a copy of the parent’s memory image, so the socket address structure is available to the child, as is the connected socket descriptor (since the descriptors are shared between the parent and child).
    • When the child exec s the real server (e.g. Telnet server that we show), the memory image of the child is replaced with the new program file for the Telnet server (the socket address structure containing the peer’s address is lost), and the connected socket descriptor remains open across the exec . One of the first function calls performed by the Telnet server is getpeername to obtain the IP address and port number of the client.

In this example, the Telnet server must know the value of connfd when it starts. There are two common ways to do this.

  1. The process calling exec pass it as a command-line argument to the newly exec ed program.
  2. A convention can be established that a certain descriptor is always set to the connected socket before calling exec .

The second one is what inetd does, always setting descriptors 0, 1, and 2 to be the connected socket.

Example: Obtaining the Address Family of a Socket¶

The sockfd_to_family function shown in the code below returns the address family of a socket.

This program does the following:

  • Allocate room for largest socket address structure. Since we do not know what type of socket address structure to allocate, we use a sockaddr_storage value, since it can hold any socket address structure supported by the system.
  • Call getsockname . We call getsockname and return the address family. The POSIX specification allows a call to getsockname on an unbound socket.

Источник

This blog post was originally posted on JetBrains .NET blog.

Rider consists of several processes that send messages to each other via sockets. To ensure the reliability of the whole application, it’s important to properly handle all the socket errors. In our codebase, we had the following code which was adopted from Mono Debugger Libs and helps us communicate with debugger processes:

protected virtual bool ShouldRetryConnection (Exception ex, int attemptNumber)
{
    var sx = ex as SocketException;
    if (sx != null) {
        if (sx.ErrorCode == 10061) //connection refused
            return true;
    }
    return false;
}

In the case of a failed connection because of a “ConnectionRefused” error, we are retrying the connection attempt. It works fine with .NET Framework and Mono. However, once we migrated to .NET Core, this method no longer correctly detects the “connection refused” situation on Linux and macOS. If we open the SocketException documentation, we will learn that this class has three different properties with error codes:

  • SocketError SocketErrorCode: Gets the error code that is associated with this exception.
  • int ErrorCode: Gets the error code that is associated with this exception.
  • int NativeErrorCode: Gets the Win32 error code associated with this exception.

What’s the difference between these properties? Should we expect different values on different runtimes or different operating systems? Which one should we use in production? Why do we have problems with ShouldRetryConnection on .NET Core? Let’s figure it all out!

Digging into the problem

Let’s start with the following program, which prints error code property values for SocketError.ConnectionRefused:

var se = new SocketException((int) SocketError.ConnectionRefused);
Console.WriteLine((int)se.SocketErrorCode);
Console.WriteLine(se.ErrorCode);
Console.WriteLine(se.NativeErrorCode);

If we run it on Windows, we will get the same value on .NET Framework, Mono, and .NET Core:

SocketErrorCode ErrorCode NativeErrorCode
.NET Framework 10061 10061 10061
Mono 10061 10061 10061
.NET Core 10061 10061 10061

10061 corresponds to the code of the connection refused socket error code in Windows (also known as WSAECONNREFUSED).
Now let’s run the same program on Linux:

SocketErrorCode ErrorCode NativeErrorCode
Mono 10061 10061 10061
.NET Core 10061 111 111

As you can see, Mono returns Windows-compatible error codes. The situation with .NET Core is different: it returns a Windows-compatible value for SocketErrorCode (10061) and a Linux-like value for ErrorCode and NativeErrorCode (111).
Finally, let’s check macOS:

SocketErrorCode ErrorCode NativeErrorCode
Mono 10061 10061 10061
.NET Core 10061 61 61

Here, Mono is completely Windows-compatible again, but .NET Core returns 61 for ErrorCode and NativeErrorCode.
In the IBM Knowledge Center, we can find a few more values for the connection refused error code from the Unix world (also known as ECONNREFUSED):

  • AIX: 79
  • HP-UX: 239
  • Solaris: 146

For a better understanding of what’s going on, let’s check out the source code of all the properties.

SocketErrorCode

SocketException.SocketErrorCode returns a value from the SocketError enum. The numerical values of the enum elements are the same on all the runtimes (see its implementation in .NET Framework, .NET Core 3.1.3, and Mono 6.8.0.105):

public enum SocketError
{
    SocketError = -1, // 0xFFFFFFFF
    Success = 0,
    OperationAborted = 995, // 0x000003E3
    IOPending = 997, // 0x000003E5
    Interrupted = 10004, // 0x00002714
    AccessDenied = 10013, // 0x0000271D
    Fault = 10014, // 0x0000271E
    InvalidArgument = 10022, // 0x00002726
    TooManyOpenSockets = 10024, // 0x00002728
    WouldBlock = 10035, // 0x00002733
    InProgress = 10036, // 0x00002734
    AlreadyInProgress = 10037, // 0x00002735
    NotSocket = 10038, // 0x00002736
    DestinationAddressRequired = 10039, // 0x00002737
    MessageSize = 10040, // 0x00002738
    ProtocolType = 10041, // 0x00002739
    ProtocolOption = 10042, // 0x0000273A
    ProtocolNotSupported = 10043, // 0x0000273B
    SocketNotSupported = 10044, // 0x0000273C
    OperationNotSupported = 10045, // 0x0000273D
    ProtocolFamilyNotSupported = 10046, // 0x0000273E
    AddressFamilyNotSupported = 10047, // 0x0000273F
    AddressAlreadyInUse = 10048, // 0x00002740
    AddressNotAvailable = 10049, // 0x00002741
    NetworkDown = 10050, // 0x00002742
    NetworkUnreachable = 10051, // 0x00002743
    NetworkReset = 10052, // 0x00002744
    ConnectionAborted = 10053, // 0x00002745
    ConnectionReset = 10054, // 0x00002746
    NoBufferSpaceAvailable = 10055, // 0x00002747
    IsConnected = 10056, // 0x00002748
    NotConnected = 10057, // 0x00002749
    Shutdown = 10058, // 0x0000274A
    TimedOut = 10060, // 0x0000274C
    ConnectionRefused = 10061, // 0x0000274D
    HostDown = 10064, // 0x00002750
    HostUnreachable = 10065, // 0x00002751
    ProcessLimit = 10067, // 0x00002753
    SystemNotReady = 10091, // 0x0000276B
    VersionNotSupported = 10092, // 0x0000276C
    NotInitialized = 10093, // 0x0000276D
    Disconnecting = 10101, // 0x00002775
    TypeNotFound = 10109, // 0x0000277D
    HostNotFound = 11001, // 0x00002AF9
    TryAgain = 11002, // 0x00002AFA
    NoRecovery = 11003, // 0x00002AFB
    NoData = 11004, // 0x00002AFC
}

These values correspond to the Windows Sockets Error Codes.

NativeErrorCode

In .NET Framework and Mono, SocketErrorCode and NativeErrorCode always have the same values:

public SocketError SocketErrorCode {
    //
    // the base class returns the HResult with this property
    // we need the Win32 Error Code, hence the override.
    //
    get {
        return (SocketError)NativeErrorCode;
    }
}

In .NET Core, the native code is calculated in the constructor (see SocketException.cs#L20):

public SocketException(int errorCode) : this((SocketError)errorCode)
// ...
internal SocketException(SocketError socketError) : base(GetNativeErrorForSocketError(socketError))

The Windows implementation of GetNativeErrorForSocketError is trivial (see SocketException.Windows.cs):

private static int GetNativeErrorForSocketError(SocketError error)
{
    // SocketError values map directly to Win32 error codes
    return (int)error;
}

The Unix implementation is more complicated (see SocketException.Unix.cs):

private static int GetNativeErrorForSocketError(SocketError error)
{
    int nativeErr = (int)error;
    if (error != SocketError.SocketError)
    {
        Interop.Error interopErr;

        // If an interop error was not found, then don't invoke Info().RawErrno as that will fail with assert.
        if (SocketErrorPal.TryGetNativeErrorForSocketError(error, out interopErr))
        {
            nativeErr = interopErr.Info().RawErrno;
        }
    }

    return nativeErr;
}

TryGetNativeErrorForSocketError should convert SocketError to the native Unix error code.
Unfortunately, there exists no unequivocal mapping between Windows and Unix error codes. As such, the .NET team decided to create a Dictionary that maps error codes in the best possible way (see SocketErrorPal.Unix.cs):

private const int NativeErrorToSocketErrorCount = 42;
private const int SocketErrorToNativeErrorCount = 40;

// No Interop.Errors are included for the following SocketErrors, as there's no good mapping:
// - SocketError.NoRecovery
// - SocketError.NotInitialized
// - SocketError.ProcessLimit
// - SocketError.SocketError
// - SocketError.SystemNotReady
// - SocketError.TypeNotFound
// - SocketError.VersionNotSupported

private static readonly Dictionary<Interop.Error, SocketError> s_nativeErrorToSocketError = new Dictionary<Interop.Error, SocketError>(NativeErrorToSocketErrorCount)
{
    { Interop.Error.EACCES, SocketError.AccessDenied },
    { Interop.Error.EADDRINUSE, SocketError.AddressAlreadyInUse },
    { Interop.Error.EADDRNOTAVAIL, SocketError.AddressNotAvailable },
    { Interop.Error.EAFNOSUPPORT, SocketError.AddressFamilyNotSupported },
    { Interop.Error.EAGAIN, SocketError.WouldBlock },
    { Interop.Error.EALREADY, SocketError.AlreadyInProgress },
    { Interop.Error.EBADF, SocketError.OperationAborted },
    { Interop.Error.ECANCELED, SocketError.OperationAborted },
    { Interop.Error.ECONNABORTED, SocketError.ConnectionAborted },
    { Interop.Error.ECONNREFUSED, SocketError.ConnectionRefused },
    { Interop.Error.ECONNRESET, SocketError.ConnectionReset },
    { Interop.Error.EDESTADDRREQ, SocketError.DestinationAddressRequired },
    { Interop.Error.EFAULT, SocketError.Fault },
    { Interop.Error.EHOSTDOWN, SocketError.HostDown },
    { Interop.Error.ENXIO, SocketError.HostNotFound }, // not perfect, but closest match available
    { Interop.Error.EHOSTUNREACH, SocketError.HostUnreachable },
    { Interop.Error.EINPROGRESS, SocketError.InProgress },
    { Interop.Error.EINTR, SocketError.Interrupted },
    { Interop.Error.EINVAL, SocketError.InvalidArgument },
    { Interop.Error.EISCONN, SocketError.IsConnected },
    { Interop.Error.EMFILE, SocketError.TooManyOpenSockets },
    { Interop.Error.EMSGSIZE, SocketError.MessageSize },
    { Interop.Error.ENETDOWN, SocketError.NetworkDown },
    { Interop.Error.ENETRESET, SocketError.NetworkReset },
    { Interop.Error.ENETUNREACH, SocketError.NetworkUnreachable },
    { Interop.Error.ENFILE, SocketError.TooManyOpenSockets },
    { Interop.Error.ENOBUFS, SocketError.NoBufferSpaceAvailable },
    { Interop.Error.ENODATA, SocketError.NoData },
    { Interop.Error.ENOENT, SocketError.AddressNotAvailable },
    { Interop.Error.ENOPROTOOPT, SocketError.ProtocolOption },
    { Interop.Error.ENOTCONN, SocketError.NotConnected },
    { Interop.Error.ENOTSOCK, SocketError.NotSocket },
    { Interop.Error.ENOTSUP, SocketError.OperationNotSupported },
    { Interop.Error.EPERM, SocketError.AccessDenied },
    { Interop.Error.EPIPE, SocketError.Shutdown },
    { Interop.Error.EPFNOSUPPORT, SocketError.ProtocolFamilyNotSupported },
    { Interop.Error.EPROTONOSUPPORT, SocketError.ProtocolNotSupported },
    { Interop.Error.EPROTOTYPE, SocketError.ProtocolType },
    { Interop.Error.ESOCKTNOSUPPORT, SocketError.SocketNotSupported },
    { Interop.Error.ESHUTDOWN, SocketError.Disconnecting },
    { Interop.Error.SUCCESS, SocketError.Success },
    { Interop.Error.ETIMEDOUT, SocketError.TimedOut },
};

private static readonly Dictionary<SocketError, Interop.Error> s_socketErrorToNativeError = new Dictionary<SocketError, Interop.Error>(SocketErrorToNativeErrorCount)
{
    // This is *mostly* an inverse mapping of s_nativeErrorToSocketError.  However, some options have multiple mappings and thus
    // can't be inverted directly.  Other options don't have a mapping from native to SocketError, but when presented with a SocketError,
    // we want to provide the closest relevant Error possible, e.g. EINPROGRESS maps to SocketError.InProgress, and vice versa, but
    // SocketError.IOPending also maps closest to EINPROGRESS.  As such, roundtripping won't necessarily provide the original value 100% of the time,
    // but it's the best we can do given the mismatch between Interop.Error and SocketError.

    { SocketError.AccessDenied, Interop.Error.EACCES}, // could also have been EPERM
    { SocketError.AddressAlreadyInUse, Interop.Error.EADDRINUSE  },
    { SocketError.AddressNotAvailable, Interop.Error.EADDRNOTAVAIL },
    { SocketError.AddressFamilyNotSupported, Interop.Error.EAFNOSUPPORT  },
    { SocketError.AlreadyInProgress, Interop.Error.EALREADY },
    { SocketError.ConnectionAborted, Interop.Error.ECONNABORTED },
    { SocketError.ConnectionRefused, Interop.Error.ECONNREFUSED },
    { SocketError.ConnectionReset, Interop.Error.ECONNRESET },
    { SocketError.DestinationAddressRequired, Interop.Error.EDESTADDRREQ },
    { SocketError.Disconnecting, Interop.Error.ESHUTDOWN },
    { SocketError.Fault, Interop.Error.EFAULT },
    { SocketError.HostDown, Interop.Error.EHOSTDOWN },
    { SocketError.HostNotFound, Interop.Error.EHOSTNOTFOUND },
    { SocketError.HostUnreachable, Interop.Error.EHOSTUNREACH },
    { SocketError.InProgress, Interop.Error.EINPROGRESS },
    { SocketError.Interrupted, Interop.Error.EINTR },
    { SocketError.InvalidArgument, Interop.Error.EINVAL },
    { SocketError.IOPending, Interop.Error.EINPROGRESS },
    { SocketError.IsConnected, Interop.Error.EISCONN },
    { SocketError.MessageSize, Interop.Error.EMSGSIZE },
    { SocketError.NetworkDown, Interop.Error.ENETDOWN },
    { SocketError.NetworkReset, Interop.Error.ENETRESET },
    { SocketError.NetworkUnreachable, Interop.Error.ENETUNREACH },
    { SocketError.NoBufferSpaceAvailable, Interop.Error.ENOBUFS },
    { SocketError.NoData, Interop.Error.ENODATA },
    { SocketError.NotConnected, Interop.Error.ENOTCONN },
    { SocketError.NotSocket, Interop.Error.ENOTSOCK },
    { SocketError.OperationAborted, Interop.Error.ECANCELED },
    { SocketError.OperationNotSupported, Interop.Error.ENOTSUP },
    { SocketError.ProtocolFamilyNotSupported, Interop.Error.EPFNOSUPPORT },
    { SocketError.ProtocolNotSupported, Interop.Error.EPROTONOSUPPORT },
    { SocketError.ProtocolOption, Interop.Error.ENOPROTOOPT },
    { SocketError.ProtocolType, Interop.Error.EPROTOTYPE },
    { SocketError.Shutdown, Interop.Error.EPIPE },
    { SocketError.SocketNotSupported, Interop.Error.ESOCKTNOSUPPORT },
    { SocketError.Success, Interop.Error.SUCCESS },
    { SocketError.TimedOut, Interop.Error.ETIMEDOUT },
    { SocketError.TooManyOpenSockets, Interop.Error.ENFILE }, // could also have been EMFILE
    { SocketError.TryAgain, Interop.Error.EAGAIN }, // not a perfect mapping, but better than nothing
    { SocketError.WouldBlock, Interop.Error.EAGAIN  },
};

internal static bool TryGetNativeErrorForSocketError(SocketError error, out Interop.Error errno)
{
    return s_socketErrorToNativeError.TryGetValue(error, out errno);
}

Once we have an instance of Interop.Error, we call interopErr.Info().RawErrno. The implementation of RawErrno can be found in Interop.Errors.cs:

internal int RawErrno
{
    get { return _rawErrno == -1 ? (_rawErrno = Interop.Sys.ConvertErrorPalToPlatform(_error)) : _rawErrno; }
}

[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_ConvertErrorPalToPlatform")]
internal static extern int ConvertErrorPalToPlatform(Error error);

Here we are jumping to the native function SystemNative_ConvertErrorPalToPlatform that maps Error to the native integer code that is defined in errno.h. You can get all the values using the errno util. Here is a typical output on Linux:

$ errno -ls
EPERM 1 Operation not permitted
ENOENT 2 No such file or directory
ESRCH 3 No such process
EINTR 4 Interrupted system call
EIO 5 Input/output error
ENXIO 6 No such device or address
E2BIG 7 Argument list too long
ENOEXEC 8 Exec format error
EBADF 9 Bad file descriptor
ECHILD 10 No child processes
EAGAIN 11 Resource temporarily unavailable
ENOMEM 12 Cannot allocate memory
EACCES 13 Permission denied
EFAULT 14 Bad address
ENOTBLK 15 Block device required
EBUSY 16 Device or resource busy
EEXIST 17 File exists
EXDEV 18 Invalid cross-device link
ENODEV 19 No such device
ENOTDIR 20 Not a directory
EISDIR 21 Is a directory
EINVAL 22 Invalid argument
ENFILE 23 Too many open files in system
EMFILE 24 Too many open files
ENOTTY 25 Inappropriate ioctl for device
ETXTBSY 26 Text file busy
EFBIG 27 File too large
ENOSPC 28 No space left on device
ESPIPE 29 Illegal seek
EROFS 30 Read-only file system
EMLINK 31 Too many links
EPIPE 32 Broken pipe
EDOM 33 Numerical argument out of domain
ERANGE 34 Numerical result out of range
EDEADLK 35 Resource deadlock avoided
ENAMETOOLONG 36 File name too long
ENOLCK 37 No locks available
ENOSYS 38 Function not implemented
ENOTEMPTY 39 Directory not empty
ELOOP 40 Too many levels of symbolic links
EWOULDBLOCK 11 Resource temporarily unavailable
ENOMSG 42 No message of desired type
EIDRM 43 Identifier removed
ECHRNG 44 Channel number out of range
EL2NSYNC 45 Level 2 not synchronized
EL3HLT 46 Level 3 halted
EL3RST 47 Level 3 reset
ELNRNG 48 Link number out of range
EUNATCH 49 Protocol driver not attached
ENOCSI 50 No CSI structure available
EL2HLT 51 Level 2 halted
EBADE 52 Invalid exchange
EBADR 53 Invalid request descriptor
EXFULL 54 Exchange full
ENOANO 55 No anode
EBADRQC 56 Invalid request code
EBADSLT 57 Invalid slot
EDEADLOCK 35 Resource deadlock avoided
EBFONT 59 Bad font file format
ENOSTR 60 Device not a stream
ENODATA 61 No data available
ETIME 62 Timer expired
ENOSR 63 Out of streams resources
ENONET 64 Machine is not on the network
ENOPKG 65 Package not installed
EREMOTE 66 Object is remote
ENOLINK 67 Link has been severed
EADV 68 Advertise error
ESRMNT 69 Srmount error
ECOMM 70 Communication error on send
EPROTO 71 Protocol error
EMULTIHOP 72 Multihop attempted
EDOTDOT 73 RFS specific error
EBADMSG 74 Bad message
EOVERFLOW 75 Value too large for defined data type
ENOTUNIQ 76 Name not unique on network
EBADFD 77 File descriptor in bad state
EREMCHG 78 Remote address changed
ELIBACC 79 Can not access a needed shared library
ELIBBAD 80 Accessing a corrupted shared library
ELIBSCN 81 .lib section in a.out corrupted
ELIBMAX 82 Attempting to link in too many shared libraries
ELIBEXEC 83 Cannot exec a shared library directly
EILSEQ 84 Invalid or incomplete multibyte or wide character
ERESTART 85 Interrupted system call should be restarted
ESTRPIPE 86 Streams pipe error
EUSERS 87 Too many users
ENOTSOCK 88 Socket operation on non-socket
EDESTADDRREQ 89 Destination address required
EMSGSIZE 90 Message too long
EPROTOTYPE 91 Protocol wrong type for socket
ENOPROTOOPT 92 Protocol not available
EPROTONOSUPPORT 93 Protocol not supported
ESOCKTNOSUPPORT 94 Socket type not supported
EOPNOTSUPP 95 Operation not supported
EPFNOSUPPORT 96 Protocol family not supported
EAFNOSUPPORT 97 Address family not supported by protocol
EADDRINUSE 98 Address already in use
EADDRNOTAVAIL 99 Cannot assign requested address
ENETDOWN 100 Network is down
ENETUNREACH 101 Network is unreachable
ENETRESET 102 Network dropped connection on reset
ECONNABORTED 103 Software caused connection abort
ECONNRESET 104 Connection reset by peer
ENOBUFS 105 No buffer space available
EISCONN 106 Transport endpoint is already connected
ENOTCONN 107 Transport endpoint is not connected
ESHUTDOWN 108 Cannot send after transport endpoint shutdown
ETOOMANYREFS 109 Too many references: cannot splice
ETIMEDOUT 110 Connection timed out
ECONNREFUSED 111 Connection refused
EHOSTDOWN 112 Host is down
EHOSTUNREACH 113 No route to host
EALREADY 114 Operation already in progress
EINPROGRESS 115 Operation now in progress
ESTALE 116 Stale file handle
EUCLEAN 117 Structure needs cleaning
ENOTNAM 118 Not a XENIX named type file
ENAVAIL 119 No XENIX semaphores available
EISNAM 120 Is a named type file
EREMOTEIO 121 Remote I/O error
EDQUOT 122 Disk quota exceeded
ENOMEDIUM 123 No medium found
EMEDIUMTYPE 124 Wrong medium type
ECANCELED 125 Operation canceled
ENOKEY 126 Required key not available
EKEYEXPIRED 127 Key has expired
EKEYREVOKED 128 Key has been revoked
EKEYREJECTED 129 Key was rejected by service
EOWNERDEAD 130 Owner died
ENOTRECOVERABLE 131 State not recoverable
ERFKILL 132 Operation not possible due to RF-kill
EHWPOISON 133 Memory page has hardware error
ENOTSUP 95 Operation not supported

Note that errno may be not available by default in your Linux distro. For example, on Debian, you should call sudo apt-get install moreutils to get this utility.
Here is a typical output on macOS:

$ errno -ls
EPERM 1 Operation not permitted
ENOENT 2 No such file or directory
ESRCH 3 No such process
EINTR 4 Interrupted system call
EIO 5 Input/output error
ENXIO 6 Device not configured
E2BIG 7 Argument list too long
ENOEXEC 8 Exec format error
EBADF 9 Bad file descriptor
ECHILD 10 No child processes
EDEADLK 11 Resource deadlock avoided
ENOMEM 12 Cannot allocate memory
EACCES 13 Permission denied
EFAULT 14 Bad address
ENOTBLK 15 Block device required
EBUSY 16 Resource busy
EEXIST 17 File exists
EXDEV 18 Cross-device link
ENODEV 19 Operation not supported by device
ENOTDIR 20 Not a directory
EISDIR 21 Is a directory
EINVAL 22 Invalid argument
ENFILE 23 Too many open files in system
EMFILE 24 Too many open files
ENOTTY 25 Inappropriate ioctl for device
ETXTBSY 26 Text file busy
EFBIG 27 File too large
ENOSPC 28 No space left on device
ESPIPE 29 Illegal seek
EROFS 30 Read-only file system
EMLINK 31 Too many links
EPIPE 32 Broken pipe
EDOM 33 Numerical argument out of domain
ERANGE 34 Result too large
EAGAIN 35 Resource temporarily unavailable
EWOULDBLOCK 35 Resource temporarily unavailable
EINPROGRESS 36 Operation now in progress
EALREADY 37 Operation already in progress
ENOTSOCK 38 Socket operation on non-socket
EDESTADDRREQ 39 Destination address required
EMSGSIZE 40 Message too long
EPROTOTYPE 41 Protocol wrong type for socket
ENOPROTOOPT 42 Protocol not available
EPROTONOSUPPORT 43 Protocol not supported
ESOCKTNOSUPPORT 44 Socket type not supported
ENOTSUP 45 Operation not supported
EPFNOSUPPORT 46 Protocol family not supported
EAFNOSUPPORT 47 Address family not supported by protocol family
EADDRINUSE 48 Address already in use
EADDRNOTAVAIL 49 Can`t assign requested address
ENETDOWN 50 Network is down
ENETUNREACH 51 Network is unreachable
ENETRESET 52 Network dropped connection on reset
ECONNABORTED 53 Software caused connection abort
ECONNRESET 54 Connection reset by peer
ENOBUFS 55 No buffer space available
EISCONN 56 Socket is already connected
ENOTCONN 57 Socket is not connected
ESHUTDOWN 58 Can`t send after socket shutdown
ETOOMANYREFS 59 Too many references: can`t splice
ETIMEDOUT 60 Operation timed out
ECONNREFUSED 61 Connection refused
ELOOP 62 Too many levels of symbolic links
ENAMETOOLONG 63 File name too long
EHOSTDOWN 64 Host is down
EHOSTUNREACH 65 No route to host
ENOTEMPTY 66 Directory not empty
EPROCLIM 67 Too many processes
EUSERS 68 Too many users
EDQUOT 69 Disc quota exceeded
ESTALE 70 Stale NFS file handle
EREMOTE 71 Too many levels of remote in path
EBADRPC 72 RPC struct is bad
ERPCMISMATCH 73 RPC version wrong
EPROGUNAVAIL 74 RPC prog. not avail
EPROGMISMATCH 75 Program version wrong
EPROCUNAVAIL 76 Bad procedure for program
ENOLCK 77 No locks available
ENOSYS 78 Function not implemented
EFTYPE 79 Inappropriate file type or format
EAUTH 80 Authentication error
ENEEDAUTH 81 Need authenticator
EPWROFF 82 Device power is off
EDEVERR 83 Device error
EOVERFLOW 84 Value too large to be stored in data type
EBADEXEC 85 Bad executable (or shared library)
EBADARCH 86 Bad CPU type in executable
ESHLIBVERS 87 Shared library version mismatch
EBADMACHO 88 Malformed Mach-o file
ECANCELED 89 Operation canceled
EIDRM 90 Identifier removed
ENOMSG 91 No message of desired type
EILSEQ 92 Illegal byte sequence
ENOATTR 93 Attribute not found
EBADMSG 94 Bad message
EMULTIHOP 95 EMULTIHOP (Reserved)
ENODATA 96 No message available on STREAM
ENOLINK 97 ENOLINK (Reserved)
ENOSR 98 No STREAM resources
ENOSTR 99 Not a STREAM
EPROTO 100 Protocol error
ETIME 101 STREAM ioctl timeout
EOPNOTSUPP 102 Operation not supported on socket
ENOPOLICY 103 Policy not found
ENOTRECOVERABLE 104 State not recoverable
EOWNERDEAD 105 Previous owner died
EQFULL 106 Interface output queue is full
ELAST 106 Interface output queue is full

Hooray! We’ve finished our fascinating journey into the internals of socket error codes. Now you know where .NET is getting the native error code for each SocketException from!

ErrorCode

The ErrorCode property is the most boring one, as it always returns NativeErrorCode.
.NET Framework, Mono 6.8.0.105:

public override int ErrorCode {
    //
    // the base class returns the HResult with this property
    // we need the Win32 Error Code, hence the override.
    //
    get {
        return NativeErrorCode;
    }
}

In .NET Core 3.1.3:

public override int ErrorCode => base.NativeErrorCode;

Writing cross-platform socket error handling

Circling back to the original method we started this post with, we rewrote ShouldRetryConnection as follows:

protected virtual bool ShouldRetryConnection(Exception ex)
{
    if (ex is SocketException sx)
        return sx.SocketErrorCode == SocketError.ConnectionRefused;
    return false;
}

There was a lot of work involved in tracking down the error code to check against, but in the end, our code is much more readable now. Adding to that, this method is now also completely cross-platform, and works correctly on any runtime.

Overview of the native error codes

In some situations, you may want to have a table with native error codes on different operating systems. We can get these values with the following code snippet:

var allErrors = Enum.GetValues(typeof(SocketError)).Cast<SocketError>().ToList();
var maxNameWidth = allErrors.Select(x => x.ToString().Length).Max();
foreach (var socketError in allErrors)
{
    var name = socketError.ToString().PadRight(maxNameWidth);
    var code = new SocketException((int) socketError).NativeErrorCode.ToString().PadLeft(7);
    Console.WriteLine("| {name} | {code} |");
}

We executed this program on Windows, Linux, and macOS. Here are the aggregated results:

SocketError Windows Linux macOS
Success 0 0 0
OperationAborted 995 125 89
IOPending 997 115 36
Interrupted 10004 4 4
AccessDenied 10013 13 13
Fault 10014 14 14
InvalidArgument 10022 22 22
TooManyOpenSockets 10024 23 23
WouldBlock 10035 11 35
InProgress 10036 115 36
AlreadyInProgress 10037 114 37
NotSocket 10038 88 38
DestinationAddressRequired 10039 89 39
MessageSize 10040 90 40
ProtocolType 10041 91 41
ProtocolOption 10042 92 42
ProtocolNotSupported 10043 93 43
SocketNotSupported 10044 94 44
OperationNotSupported 10045 95 45
ProtocolFamilyNotSupported 10046 96 46
AddressFamilyNotSupported 10047 97 47
AddressAlreadyInUse 10048 98 48
AddressNotAvailable 10049 99 49
NetworkDown 10050 100 50
NetworkUnreachable 10051 101 51
NetworkReset 10052 102 52
ConnectionAborted 10053 103 53
ConnectionReset 10054 104 54
NoBufferSpaceAvailable 10055 105 55
IsConnected 10056 106 56
NotConnected 10057 107 57
Shutdown 10058 32 32
TimedOut 10060 110 60
ConnectionRefused 10061 111 61
HostDown 10064 112 64
HostUnreachable 10065 113 65
ProcessLimit 10067 10067 10067
SystemNotReady 10091 10091 10091
VersionNotSupported 10092 10092 10092
NotInitialized 10093 10093 10093
Disconnecting 10101 108 58
TypeNotFound 10109 10109 10109
HostNotFound 11001 -131073 -131073
TryAgain 11002 11 35
NoRecovery 11003 11003 11003
NoData 11004 61 96
SocketError -1 -1 -1

This table may be useful if you work with native socket error codes.

Summary

From this investigation, we’ve learned the following:

  • SocketException.SocketErrorCode returns a value from the SocketError enum. The numerical values of the enum elements always correspond to the Windows socket error codes.
  • SocketException.ErrorCode always returns SocketException.NativeErrorCode.
  • SocketException.NativeErrorCode on .NET Framework and Mono always corresponds to the Windows error codes (even if you are using Mono on Unix). On .NET Core, SocketException.NativeErrorCode equals the corresponding native error code from the current operating system.

A few practical recommendations:

  • If you want to write portable code, always use SocketException.SocketErrorCode and compare it with the values of SocketError. Never use raw numerical error codes.
  • If you want to get the native error code on .NET Core (e.g., for passing to another native program), use SocketException.NativeErrorCode. Remember that different Unix-based operating systems (e.g., Linux, macOS, Solaris) have different native code sets. You can get the exact values of the native error codes by using the errno command.

References

  • Microsoft Docs: Windows Sockets Error Codes
  • IBM Knowledge Center: TCP/IP error codes
  • MariaDB: Operating System Error Codes
  • gnu.org: Error Codes
  • Stackoverflow: Identical Error Codes

Понравилась статья? Поделить с друзьями:
  • Tco ошибка мерседес актрос
  • Tco handling ошибка ивеко стралис
  • Tcm 0117 ошибка вольво
  • Tcm 0115 volvo ошибка
  • Tcl код ошибки стиральной машины lg