version 1.1.1.1, 2016/10/18 13:28:18
|
version 1.1.1.3, 2023/09/27 11:14:54
|
Line 1
|
Line 1
|
/* |
/* |
* iperf, Copyright (c) 2014, 2016, The Regents of the University of | * iperf, Copyright (c) 2014-2022, The Regents of the University of |
* California, through Lawrence Berkeley National Laboratory (subject |
* California, through Lawrence Berkeley National Laboratory (subject |
* to receipt of any required approvals from the U.S. Dept. of |
* to receipt of any required approvals from the U.S. Dept. of |
* Energy). All rights reserved. |
* Energy). All rights reserved. |
Line 24
|
Line 24
|
* This code is distributed under a BSD style license, see the LICENSE |
* This code is distributed under a BSD style license, see the LICENSE |
* file for complete information. |
* file for complete information. |
*/ |
*/ |
#include "iperf_config.h" |
|
|
|
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <errno.h> |
#include <errno.h> |
#include <unistd.h> |
#include <unistd.h> |
|
#include <arpa/inet.h> |
#include <sys/socket.h> |
#include <sys/socket.h> |
#include <sys/types.h> |
#include <sys/types.h> |
#include <netinet/in.h> |
#include <netinet/in.h> |
#include <netdb.h> |
#include <netdb.h> |
#include <netinet/tcp.h> |
|
#include <sys/time.h> |
#include <sys/time.h> |
#include <sys/select.h> |
#include <sys/select.h> |
|
#include <limits.h> |
|
|
#include "iperf.h" |
#include "iperf.h" |
#include "iperf_api.h" |
#include "iperf_api.h" |
#include "iperf_tcp.h" |
#include "iperf_tcp.h" |
#include "net.h" |
#include "net.h" |
|
#include "cjson.h" |
|
|
#if defined(HAVE_FLOWLABEL) |
#if defined(HAVE_FLOWLABEL) |
#include "flowlabel.h" |
#include "flowlabel.h" |
Line 62 iperf_tcp_recv(struct iperf_stream *sp)
|
Line 62 iperf_tcp_recv(struct iperf_stream *sp)
|
if (r < 0) |
if (r < 0) |
return r; |
return r; |
|
|
sp->result->bytes_received += r; | /* Only count bytes received while we're in the correct state. */ |
sp->result->bytes_received_this_interval += r; | if (sp->test->state == TEST_RUNNING) { |
| sp->result->bytes_received += r; |
| sp->result->bytes_received_this_interval += r; |
| } |
| else { |
| if (sp->test->debug) |
| printf("Late receive, state = %d\n", sp->test->state); |
| } |
|
|
return r; |
return r; |
} |
} |
|
|
|
|
/* iperf_tcp_send | /* iperf_tcp_send |
* |
* |
* sends the data for TCP |
* sends the data for TCP |
*/ |
*/ |
Line 78 iperf_tcp_send(struct iperf_stream *sp)
|
Line 85 iperf_tcp_send(struct iperf_stream *sp)
|
{ |
{ |
int r; |
int r; |
|
|
|
if (!sp->pending_size) |
|
sp->pending_size = sp->settings->blksize; |
|
|
if (sp->test->zerocopy) |
if (sp->test->zerocopy) |
r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->settings->blksize); | r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->pending_size); |
else |
else |
r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Ptcp); | r = Nwrite(sp->socket, sp->buffer, sp->pending_size, Ptcp); |
|
|
if (r < 0) |
if (r < 0) |
return r; |
return r; |
|
|
|
sp->pending_size -= r; |
sp->result->bytes_sent += r; |
sp->result->bytes_sent += r; |
sp->result->bytes_sent_this_interval += r; |
sp->result->bytes_sent_this_interval += r; |
|
|
|
if (sp->test->debug_level >= DEBUG_LEVEL_DEBUG) |
|
printf("sent %d bytes of %d, pending %d, total %" PRIu64 "\n", |
|
r, sp->settings->blksize, sp->pending_size, sp->result->bytes_sent); |
|
|
return r; |
return r; |
} |
} |
|
|
Line 119 iperf_tcp_accept(struct iperf_test * test)
|
Line 134 iperf_tcp_accept(struct iperf_test * test)
|
|
|
if (strcmp(test->cookie, cookie) != 0) { |
if (strcmp(test->cookie, cookie) != 0) { |
if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) { |
if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) { |
i_errno = IESENDMESSAGE; | iperf_err(test, "failed to send access denied from busy server to new connecting client, errno = %d\n", errno); |
return -1; | |
} |
} |
close(s); |
close(s); |
} |
} |
Line 136 iperf_tcp_accept(struct iperf_test * test)
|
Line 150 iperf_tcp_accept(struct iperf_test * test)
|
int |
int |
iperf_tcp_listen(struct iperf_test *test) |
iperf_tcp_listen(struct iperf_test *test) |
{ |
{ |
struct addrinfo hints, *res; |
|
char portstr[6]; |
|
int s, opt; |
int s, opt; |
|
socklen_t optlen; |
int saved_errno; |
int saved_errno; |
|
int rcvbuf_actual, sndbuf_actual; |
|
|
s = test->listener; |
s = test->listener; |
|
|
Line 153 iperf_tcp_listen(struct iperf_test *test)
|
Line 167 iperf_tcp_listen(struct iperf_test *test)
|
* It's not clear whether this is a requirement or a convenience. |
* It's not clear whether this is a requirement or a convenience. |
*/ |
*/ |
if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { |
if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) { |
|
struct addrinfo hints, *res; |
|
char portstr[6]; |
|
|
FD_CLR(s, &test->read_set); |
FD_CLR(s, &test->read_set); |
close(s); |
close(s); |
|
|
Line 172 iperf_tcp_listen(struct iperf_test *test)
|
Line 189 iperf_tcp_listen(struct iperf_test *test)
|
} |
} |
hints.ai_socktype = SOCK_STREAM; |
hints.ai_socktype = SOCK_STREAM; |
hints.ai_flags = AI_PASSIVE; |
hints.ai_flags = AI_PASSIVE; |
if (getaddrinfo(test->bind_address, portstr, &hints, &res) != 0) { | if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) { |
i_errno = IESTREAMLISTEN; |
i_errno = IESTREAMLISTEN; |
return -1; |
return -1; |
} |
} |
Line 223 iperf_tcp_listen(struct iperf_test *test)
|
Line 240 iperf_tcp_listen(struct iperf_test *test)
|
return -1; |
return -1; |
} |
} |
} |
} |
if (test->debug) { |
|
socklen_t optlen = sizeof(opt); |
|
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, &optlen) < 0) { |
|
saved_errno = errno; |
|
close(s); |
|
freeaddrinfo(res); |
|
errno = saved_errno; |
|
i_errno = IESETBUF; |
|
return -1; |
|
} |
|
printf("SO_SNDBUF is %u\n", opt); |
|
} |
|
#if defined(HAVE_TCP_CONGESTION) |
|
if (test->congestion) { |
|
if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) { |
|
close(s); |
|
freeaddrinfo(res); |
|
i_errno = IESETCONGESTION; |
|
return -1; |
|
} |
|
} |
|
#endif /* HAVE_TCP_CONGESTION */ |
|
#if defined(HAVE_SO_MAX_PACING_RATE) |
#if defined(HAVE_SO_MAX_PACING_RATE) |
/* If socket pacing is available and not disabled, try it. */ | /* If fq socket pacing is specified, enable it. */ |
if (! test->no_fq_socket_pacing) { | if (test->settings->fqrate) { |
/* Convert bits per second to bytes per second */ |
/* Convert bits per second to bytes per second */ |
unsigned int rate = test->settings->rate / 8; | unsigned int fqrate = test->settings->fqrate / 8; |
if (rate > 0) { | if (fqrate > 0) { |
if (test->debug) { |
if (test->debug) { |
printf("Setting fair-queue socket pacing to %u\n", rate); | printf("Setting fair-queue socket pacing to %u\n", fqrate); |
} |
} |
if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &rate, sizeof(rate)) < 0) { | if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) { |
warning("Unable to set socket pacing, using application pacing instead"); | warning("Unable to set socket pacing"); |
test->no_fq_socket_pacing = 1; | |
} |
} |
} |
} |
} |
} |
#endif /* HAVE_SO_MAX_PACING_RATE */ |
#endif /* HAVE_SO_MAX_PACING_RATE */ |
|
{ |
|
unsigned int rate = test->settings->rate / 8; |
|
if (rate > 0) { |
|
if (test->debug) { |
|
printf("Setting application pacing to %u\n", rate); |
|
} |
|
} |
|
} |
opt = 1; |
opt = 1; |
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { |
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { |
saved_errno = errno; |
saved_errno = errno; |
Line 272 iperf_tcp_listen(struct iperf_test *test)
|
Line 274 iperf_tcp_listen(struct iperf_test *test)
|
} |
} |
|
|
/* |
/* |
* If we got an IPv6 socket, figure out if it shoudl accept IPv4 | * If we got an IPv6 socket, figure out if it should accept IPv4 |
* connections as well. See documentation in netannounce() for |
* connections as well. See documentation in netannounce() for |
* more details. |
* more details. |
*/ |
*/ |
Line 280 iperf_tcp_listen(struct iperf_test *test)
|
Line 282 iperf_tcp_listen(struct iperf_test *test)
|
if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET)) { |
if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET)) { |
if (test->settings->domain == AF_UNSPEC) |
if (test->settings->domain == AF_UNSPEC) |
opt = 0; |
opt = 0; |
else | else |
opt = 1; |
opt = 1; |
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, | if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, |
(char *) &opt, sizeof(opt)) < 0) { |
(char *) &opt, sizeof(opt)) < 0) { |
saved_errno = errno; |
saved_errno = errno; |
close(s); |
close(s); |
Line 305 iperf_tcp_listen(struct iperf_test *test)
|
Line 307 iperf_tcp_listen(struct iperf_test *test)
|
|
|
freeaddrinfo(res); |
freeaddrinfo(res); |
|
|
if (listen(s, 5) < 0) { | if (listen(s, INT_MAX) < 0) { |
i_errno = IESTREAMLISTEN; |
i_errno = IESTREAMLISTEN; |
return -1; |
return -1; |
} |
} |
|
|
test->listener = s; |
test->listener = s; |
} |
} |
| |
| /* Read back and verify the sender socket buffer size */ |
| optlen = sizeof(sndbuf_actual); |
| if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) { |
| saved_errno = errno; |
| close(s); |
| errno = saved_errno; |
| i_errno = IESETBUF; |
| return -1; |
| } |
| if (test->debug) { |
| printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize); |
| } |
| if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) { |
| i_errno = IESETBUF2; |
| return -1; |
| } |
| |
| /* Read back and verify the receiver socket buffer size */ |
| optlen = sizeof(rcvbuf_actual); |
| if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) { |
| saved_errno = errno; |
| close(s); |
| errno = saved_errno; |
| i_errno = IESETBUF; |
| return -1; |
| } |
| if (test->debug) { |
| printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize); |
| } |
| if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) { |
| i_errno = IESETBUF2; |
| return -1; |
| } |
| |
| if (test->json_output) { |
| cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize); |
| cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual); |
| cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual); |
| } |
| |
return s; |
return s; |
} |
} |
|
|
Line 320 iperf_tcp_listen(struct iperf_test *test)
|
Line 362 iperf_tcp_listen(struct iperf_test *test)
|
/* iperf_tcp_connect |
/* iperf_tcp_connect |
* |
* |
* connect to a TCP stream listener |
* connect to a TCP stream listener |
|
* This function is roughly similar to netdial(), and may indeed have |
|
* been derived from it at some point, but it sets many TCP-specific |
|
* options between socket creation and connection. |
*/ |
*/ |
int |
int |
iperf_tcp_connect(struct iperf_test *test) |
iperf_tcp_connect(struct iperf_test *test) |
{ |
{ |
struct addrinfo hints, *local_res, *server_res; | struct addrinfo *server_res; |
char portstr[6]; | |
int s, opt; |
int s, opt; |
|
socklen_t optlen; |
int saved_errno; |
int saved_errno; |
|
int rcvbuf_actual, sndbuf_actual; |
|
|
if (test->bind_address) { | s = create_socket(test->settings->domain, SOCK_STREAM, test->bind_address, test->bind_dev, test->bind_port, test->server_hostname, test->server_port, &server_res); |
memset(&hints, 0, sizeof(hints)); | if (s < 0) { |
hints.ai_family = test->settings->domain; | i_errno = IESTREAMCONNECT; |
hints.ai_socktype = SOCK_STREAM; | return -1; |
if (getaddrinfo(test->bind_address, NULL, &hints, &local_res) != 0) { | |
i_errno = IESTREAMCONNECT; | |
return -1; | |
} | |
} |
} |
|
|
memset(&hints, 0, sizeof(hints)); |
|
hints.ai_family = test->settings->domain; |
|
hints.ai_socktype = SOCK_STREAM; |
|
snprintf(portstr, sizeof(portstr), "%d", test->server_port); |
|
if (getaddrinfo(test->server_hostname, portstr, &hints, &server_res) != 0) { |
|
if (test->bind_address) |
|
freeaddrinfo(local_res); |
|
i_errno = IESTREAMCONNECT; |
|
return -1; |
|
} |
|
|
|
if ((s = socket(server_res->ai_family, SOCK_STREAM, 0)) < 0) { |
|
if (test->bind_address) |
|
freeaddrinfo(local_res); |
|
freeaddrinfo(server_res); |
|
i_errno = IESTREAMCONNECT; |
|
return -1; |
|
} |
|
|
|
if (test->bind_address) { |
|
struct sockaddr_in *lcladdr; |
|
lcladdr = (struct sockaddr_in *)local_res->ai_addr; |
|
lcladdr->sin_port = htons(test->bind_port); |
|
local_res->ai_addr = (struct sockaddr *)lcladdr; |
|
|
|
if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) { |
|
saved_errno = errno; |
|
close(s); |
|
freeaddrinfo(local_res); |
|
freeaddrinfo(server_res); |
|
errno = saved_errno; |
|
i_errno = IESTREAMCONNECT; |
|
return -1; |
|
} |
|
freeaddrinfo(local_res); |
|
} |
|
|
|
/* Set socket options */ |
/* Set socket options */ |
if (test->no_delay) { |
if (test->no_delay) { |
opt = 1; |
opt = 1; |
Line 416 iperf_tcp_connect(struct iperf_test *test)
|
Line 421 iperf_tcp_connect(struct iperf_test *test)
|
return -1; |
return -1; |
} |
} |
} |
} |
if (test->debug) { | #if defined(HAVE_TCP_USER_TIMEOUT) |
socklen_t optlen = sizeof(opt); | if ((opt = test->settings->snd_timeout)) { |
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, &optlen) < 0) { | if (setsockopt(s, IPPROTO_TCP, TCP_USER_TIMEOUT, &opt, sizeof(opt)) < 0) { |
saved_errno = errno; |
saved_errno = errno; |
close(s); |
close(s); |
freeaddrinfo(server_res); |
freeaddrinfo(server_res); |
errno = saved_errno; |
errno = saved_errno; |
i_errno = IESETBUF; | i_errno = IESETUSERTIMEOUT; |
return -1; | return -1; |
} | } |
printf("SO_SNDBUF is %u\n", opt); | |
} |
} |
|
#endif /* HAVE_TCP_USER_TIMEOUT */ |
|
|
|
/* Read back and verify the sender socket buffer size */ |
|
optlen = sizeof(sndbuf_actual); |
|
if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) { |
|
saved_errno = errno; |
|
close(s); |
|
freeaddrinfo(server_res); |
|
errno = saved_errno; |
|
i_errno = IESETBUF; |
|
return -1; |
|
} |
|
if (test->debug) { |
|
printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize); |
|
} |
|
if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) { |
|
i_errno = IESETBUF2; |
|
return -1; |
|
} |
|
|
|
/* Read back and verify the receiver socket buffer size */ |
|
optlen = sizeof(rcvbuf_actual); |
|
if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) { |
|
saved_errno = errno; |
|
close(s); |
|
freeaddrinfo(server_res); |
|
errno = saved_errno; |
|
i_errno = IESETBUF; |
|
return -1; |
|
} |
|
if (test->debug) { |
|
printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize); |
|
} |
|
if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) { |
|
i_errno = IESETBUF2; |
|
return -1; |
|
} |
|
|
|
if (test->json_output) { |
|
cJSON *sock_bufsize_item = cJSON_GetObjectItem(test->json_start, "sock_bufsize"); |
|
if (sock_bufsize_item == NULL) { |
|
cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize); |
|
} |
|
|
|
cJSON *sndbuf_actual_item = cJSON_GetObjectItem(test->json_start, "sndbuf_actual"); |
|
if (sndbuf_actual_item == NULL) { |
|
cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual); |
|
} |
|
|
|
cJSON *rcvbuf_actual_item = cJSON_GetObjectItem(test->json_start, "rcvbuf_actual"); |
|
if (rcvbuf_actual_item == NULL) { |
|
cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual); |
|
} |
|
} |
|
|
#if defined(HAVE_FLOWLABEL) |
#if defined(HAVE_FLOWLABEL) |
if (test->settings->flowlabel) { |
if (test->settings->flowlabel) { |
if (server_res->ai_addr->sa_family != AF_INET6) { |
if (server_res->ai_addr->sa_family != AF_INET6) { |
Line 447 iperf_tcp_connect(struct iperf_test *test)
|
Line 506 iperf_tcp_connect(struct iperf_test *test)
|
freq->flr_label = htonl(test->settings->flowlabel & IPV6_FLOWINFO_FLOWLABEL); |
freq->flr_label = htonl(test->settings->flowlabel & IPV6_FLOWINFO_FLOWLABEL); |
freq->flr_action = IPV6_FL_A_GET; |
freq->flr_action = IPV6_FL_A_GET; |
freq->flr_flags = IPV6_FL_F_CREATE; |
freq->flr_flags = IPV6_FL_F_CREATE; |
freq->flr_share = IPV6_FL_F_CREATE | IPV6_FL_S_EXCL; | freq->flr_share = IPV6_FL_S_ANY; |
memcpy(&freq->flr_dst, &sa6P->sin6_addr, 16); |
memcpy(&freq->flr_dst, &sa6P->sin6_addr, 16); |
|
|
if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) { |
if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) { |
Line 468 iperf_tcp_connect(struct iperf_test *test)
|
Line 527 iperf_tcp_connect(struct iperf_test *test)
|
errno = saved_errno; |
errno = saved_errno; |
i_errno = IESETFLOW; |
i_errno = IESETFLOW; |
return -1; |
return -1; |
} | } |
} |
} |
} |
} |
#endif /* HAVE_FLOWLABEL */ |
#endif /* HAVE_FLOWLABEL */ |
|
|
#if defined(HAVE_TCP_CONGESTION) |
|
if (test->congestion) { |
|
if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, test->congestion, strlen(test->congestion)) < 0) { |
|
close(s); |
|
freeaddrinfo(server_res); |
|
i_errno = IESETCONGESTION; |
|
return -1; |
|
} |
|
} |
|
#endif /* HAVE_TCP_CONGESTION */ |
|
|
|
#if defined(HAVE_SO_MAX_PACING_RATE) |
#if defined(HAVE_SO_MAX_PACING_RATE) |
/* If socket pacing is available and not disabled, try it. */ | /* If socket pacing is specified try to enable it. */ |
if (! test->no_fq_socket_pacing) { | if (test->settings->fqrate) { |
/* Convert bits per second to bytes per second */ |
/* Convert bits per second to bytes per second */ |
unsigned int rate = test->settings->rate / 8; | unsigned int fqrate = test->settings->fqrate / 8; |
if (rate > 0) { | if (fqrate > 0) { |
if (test->debug) { |
if (test->debug) { |
printf("Socket pacing set to %u\n", rate); | printf("Setting fair-queue socket pacing to %u\n", fqrate); |
} |
} |
if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &rate, sizeof(rate)) < 0) { | if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) { |
warning("Unable to set socket pacing, using application pacing instead"); | warning("Unable to set socket pacing"); |
test->no_fq_socket_pacing = 1; | |
} |
} |
} |
} |
} |
} |
#endif /* HAVE_SO_MAX_PACING_RATE */ |
#endif /* HAVE_SO_MAX_PACING_RATE */ |
|
{ |
|
unsigned int rate = test->settings->rate / 8; |
|
if (rate > 0) { |
|
if (test->debug) { |
|
printf("Setting application pacing to %u\n", rate); |
|
} |
|
} |
|
} |
|
|
|
/* Set common socket options */ |
|
iperf_common_sockopts(test, s); |
|
|
if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) { |
if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) { |
saved_errno = errno; |
saved_errno = errno; |