Annotation of embedaddon/iperf/src/iperf_tcp.c, revision 1.1.1.2
1.1 misho 1: /*
1.1.1.2 ! misho 2: * iperf, Copyright (c) 2014-2019, The Regents of the University of
1.1 misho 3: * California, through Lawrence Berkeley National Laboratory (subject
4: * to receipt of any required approvals from the U.S. Dept. of
5: * Energy). All rights reserved.
6: *
7: * If you have questions about your rights to use or distribute this
8: * software, please contact Berkeley Lab's Technology Transfer
9: * Department at TTD@lbl.gov.
10: *
11: * NOTICE. This software is owned by the U.S. Department of Energy.
12: * As such, the U.S. Government has been granted for itself and others
13: * acting on its behalf a paid-up, nonexclusive, irrevocable,
14: * worldwide license in the Software to reproduce, prepare derivative
15: * works, and perform publicly and display publicly. Beginning five
16: * (5) years after the date permission to assert copyright is obtained
17: * from the U.S. Department of Energy, and subject to any subsequent
18: * five (5) year renewals, the U.S. Government is granted for itself
19: * and others acting on its behalf a paid-up, nonexclusive,
20: * irrevocable, worldwide license in the Software to reproduce,
21: * prepare derivative works, distribute copies to the public, perform
22: * publicly and display publicly, and to permit others to do so.
23: *
24: * This code is distributed under a BSD style license, see the LICENSE
25: * file for complete information.
26: */
27: #include <stdio.h>
28: #include <stdlib.h>
29: #include <string.h>
30: #include <errno.h>
31: #include <unistd.h>
1.1.1.2 ! misho 32: #include <arpa/inet.h>
1.1 misho 33: #include <sys/socket.h>
34: #include <sys/types.h>
35: #include <netinet/in.h>
36: #include <netdb.h>
37: #include <sys/time.h>
38: #include <sys/select.h>
1.1.1.2 ! misho 39: #include <limits.h>
1.1 misho 40:
41: #include "iperf.h"
42: #include "iperf_api.h"
43: #include "iperf_tcp.h"
44: #include "net.h"
1.1.1.2 ! misho 45: #include "cjson.h"
1.1 misho 46:
47: #if defined(HAVE_FLOWLABEL)
48: #include "flowlabel.h"
49: #endif /* HAVE_FLOWLABEL */
50:
51: /* iperf_tcp_recv
52: *
53: * receives the data for TCP
54: */
55: int
56: iperf_tcp_recv(struct iperf_stream *sp)
57: {
58: int r;
59:
60: r = Nread(sp->socket, sp->buffer, sp->settings->blksize, Ptcp);
61:
62: if (r < 0)
63: return r;
64:
1.1.1.2 ! misho 65: /* Only count bytes received while we're in the correct state. */
! 66: if (sp->test->state == TEST_RUNNING) {
! 67: sp->result->bytes_received += r;
! 68: sp->result->bytes_received_this_interval += r;
! 69: }
! 70: else {
! 71: if (sp->test->debug)
! 72: printf("Late receive, state = %d\n", sp->test->state);
! 73: }
1.1 misho 74:
75: return r;
76: }
77:
78:
79: /* iperf_tcp_send
80: *
81: * sends the data for TCP
82: */
83: int
84: iperf_tcp_send(struct iperf_stream *sp)
85: {
86: int r;
87:
88: if (sp->test->zerocopy)
89: r = Nsendfile(sp->buffer_fd, sp->socket, sp->buffer, sp->settings->blksize);
90: else
91: r = Nwrite(sp->socket, sp->buffer, sp->settings->blksize, Ptcp);
92:
93: if (r < 0)
94: return r;
95:
96: sp->result->bytes_sent += r;
97: sp->result->bytes_sent_this_interval += r;
98:
1.1.1.2 ! misho 99: if (sp->test->debug)
! 100: printf("sent %d bytes of %d, total %" PRIu64 "\n", r, sp->settings->blksize, sp->result->bytes_sent);
! 101:
1.1 misho 102: return r;
103: }
104:
105:
106: /* iperf_tcp_accept
107: *
108: * accept a new TCP stream connection
109: */
110: int
111: iperf_tcp_accept(struct iperf_test * test)
112: {
113: int s;
114: signed char rbuf = ACCESS_DENIED;
115: char cookie[COOKIE_SIZE];
116: socklen_t len;
117: struct sockaddr_storage addr;
118:
119: len = sizeof(addr);
120: if ((s = accept(test->listener, (struct sockaddr *) &addr, &len)) < 0) {
121: i_errno = IESTREAMCONNECT;
122: return -1;
123: }
124:
125: if (Nread(s, cookie, COOKIE_SIZE, Ptcp) < 0) {
126: i_errno = IERECVCOOKIE;
127: return -1;
128: }
129:
130: if (strcmp(test->cookie, cookie) != 0) {
131: if (Nwrite(s, (char*) &rbuf, sizeof(rbuf), Ptcp) < 0) {
132: i_errno = IESENDMESSAGE;
133: return -1;
134: }
135: close(s);
136: }
137:
138: return s;
139: }
140:
141:
142: /* iperf_tcp_listen
143: *
144: * start up a listener for TCP stream connections
145: */
146: int
147: iperf_tcp_listen(struct iperf_test *test)
148: {
149: int s, opt;
1.1.1.2 ! misho 150: socklen_t optlen;
1.1 misho 151: int saved_errno;
1.1.1.2 ! misho 152: int rcvbuf_actual, sndbuf_actual;
1.1 misho 153:
154: s = test->listener;
155:
156: /*
157: * If certain parameters are specified (such as socket buffer
158: * size), then throw away the listening socket (the one for which
159: * we just accepted the control connection) and recreate it with
160: * those parameters. That way, when new data connections are
161: * set, they'll have all the correct parameters in place.
162: *
163: * It's not clear whether this is a requirement or a convenience.
164: */
165: if (test->no_delay || test->settings->mss || test->settings->socket_bufsize) {
1.1.1.2 ! misho 166: struct addrinfo hints, *res;
! 167: char portstr[6];
! 168:
1.1 misho 169: FD_CLR(s, &test->read_set);
170: close(s);
171:
172: snprintf(portstr, 6, "%d", test->server_port);
173: memset(&hints, 0, sizeof(hints));
174:
175: /*
176: * If binding to the wildcard address with no explicit address
177: * family specified, then force us to get an AF_INET6 socket.
178: * More details in the comments in netanounce().
179: */
180: if (test->settings->domain == AF_UNSPEC && !test->bind_address) {
181: hints.ai_family = AF_INET6;
182: }
183: else {
184: hints.ai_family = test->settings->domain;
185: }
186: hints.ai_socktype = SOCK_STREAM;
187: hints.ai_flags = AI_PASSIVE;
1.1.1.2 ! misho 188: if ((gerror = getaddrinfo(test->bind_address, portstr, &hints, &res)) != 0) {
1.1 misho 189: i_errno = IESTREAMLISTEN;
190: return -1;
191: }
192:
193: if ((s = socket(res->ai_family, SOCK_STREAM, 0)) < 0) {
194: freeaddrinfo(res);
195: i_errno = IESTREAMLISTEN;
196: return -1;
197: }
198:
199: if (test->no_delay) {
200: opt = 1;
201: if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
202: saved_errno = errno;
203: close(s);
204: freeaddrinfo(res);
205: errno = saved_errno;
206: i_errno = IESETNODELAY;
207: return -1;
208: }
209: }
210: // XXX: Setting MSS is very buggy!
211: if ((opt = test->settings->mss)) {
212: if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
213: saved_errno = errno;
214: close(s);
215: freeaddrinfo(res);
216: errno = saved_errno;
217: i_errno = IESETMSS;
218: return -1;
219: }
220: }
221: if ((opt = test->settings->socket_bufsize)) {
222: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
223: saved_errno = errno;
224: close(s);
225: freeaddrinfo(res);
226: errno = saved_errno;
227: i_errno = IESETBUF;
228: return -1;
229: }
230: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
231: saved_errno = errno;
232: close(s);
233: freeaddrinfo(res);
234: errno = saved_errno;
235: i_errno = IESETBUF;
236: return -1;
237: }
238: }
239: #if defined(HAVE_SO_MAX_PACING_RATE)
1.1.1.2 ! misho 240: /* If fq socket pacing is specified, enable it. */
! 241: if (test->settings->fqrate) {
1.1 misho 242: /* Convert bits per second to bytes per second */
1.1.1.2 ! misho 243: unsigned int fqrate = test->settings->fqrate / 8;
! 244: if (fqrate > 0) {
1.1 misho 245: if (test->debug) {
1.1.1.2 ! misho 246: printf("Setting fair-queue socket pacing to %u\n", fqrate);
1.1 misho 247: }
1.1.1.2 ! misho 248: if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
! 249: warning("Unable to set socket pacing");
1.1 misho 250: }
251: }
252: }
253: #endif /* HAVE_SO_MAX_PACING_RATE */
1.1.1.2 ! misho 254: {
! 255: unsigned int rate = test->settings->rate / 8;
! 256: if (rate > 0) {
! 257: if (test->debug) {
! 258: printf("Setting application pacing to %u\n", rate);
! 259: }
! 260: }
! 261: }
1.1 misho 262: opt = 1;
263: if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
264: saved_errno = errno;
265: close(s);
266: freeaddrinfo(res);
267: errno = saved_errno;
268: i_errno = IEREUSEADDR;
269: return -1;
270: }
271:
272: /*
273: * If we got an IPv6 socket, figure out if it shoudl accept IPv4
274: * connections as well. See documentation in netannounce() for
275: * more details.
276: */
277: #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__)
278: if (res->ai_family == AF_INET6 && (test->settings->domain == AF_UNSPEC || test->settings->domain == AF_INET)) {
279: if (test->settings->domain == AF_UNSPEC)
280: opt = 0;
281: else
282: opt = 1;
283: if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
284: (char *) &opt, sizeof(opt)) < 0) {
285: saved_errno = errno;
286: close(s);
287: freeaddrinfo(res);
288: errno = saved_errno;
289: i_errno = IEV6ONLY;
290: return -1;
291: }
292: }
293: #endif /* IPV6_V6ONLY */
294:
295: if (bind(s, (struct sockaddr *) res->ai_addr, res->ai_addrlen) < 0) {
296: saved_errno = errno;
297: close(s);
298: freeaddrinfo(res);
299: errno = saved_errno;
300: i_errno = IESTREAMLISTEN;
301: return -1;
302: }
303:
304: freeaddrinfo(res);
305:
1.1.1.2 ! misho 306: if (listen(s, INT_MAX) < 0) {
1.1 misho 307: i_errno = IESTREAMLISTEN;
308: return -1;
309: }
310:
311: test->listener = s;
312: }
313:
1.1.1.2 ! misho 314: /* Read back and verify the sender socket buffer size */
! 315: optlen = sizeof(sndbuf_actual);
! 316: if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
! 317: saved_errno = errno;
! 318: close(s);
! 319: errno = saved_errno;
! 320: i_errno = IESETBUF;
! 321: return -1;
! 322: }
! 323: if (test->debug) {
! 324: printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
! 325: }
! 326: if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
! 327: i_errno = IESETBUF2;
! 328: return -1;
! 329: }
! 330:
! 331: /* Read back and verify the receiver socket buffer size */
! 332: optlen = sizeof(rcvbuf_actual);
! 333: if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
! 334: saved_errno = errno;
! 335: close(s);
! 336: errno = saved_errno;
! 337: i_errno = IESETBUF;
! 338: return -1;
! 339: }
! 340: if (test->debug) {
! 341: printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
! 342: }
! 343: if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
! 344: i_errno = IESETBUF2;
! 345: return -1;
! 346: }
! 347:
! 348: if (test->json_output) {
! 349: cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
! 350: cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
! 351: cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
! 352: }
! 353:
1.1 misho 354: return s;
355: }
356:
357:
358: /* iperf_tcp_connect
359: *
360: * connect to a TCP stream listener
1.1.1.2 ! misho 361: * This function is roughly similar to netdial(), and may indeed have
! 362: * been derived from it at some point, but it sets many TCP-specific
! 363: * options between socket creation and connection.
1.1 misho 364: */
365: int
366: iperf_tcp_connect(struct iperf_test *test)
367: {
368: struct addrinfo hints, *local_res, *server_res;
369: char portstr[6];
370: int s, opt;
1.1.1.2 ! misho 371: socklen_t optlen;
1.1 misho 372: int saved_errno;
1.1.1.2 ! misho 373: int rcvbuf_actual, sndbuf_actual;
1.1 misho 374:
375: if (test->bind_address) {
376: memset(&hints, 0, sizeof(hints));
377: hints.ai_family = test->settings->domain;
378: hints.ai_socktype = SOCK_STREAM;
1.1.1.2 ! misho 379: if ((gerror = getaddrinfo(test->bind_address, NULL, &hints, &local_res)) != 0) {
1.1 misho 380: i_errno = IESTREAMCONNECT;
381: return -1;
382: }
383: }
384:
385: memset(&hints, 0, sizeof(hints));
386: hints.ai_family = test->settings->domain;
387: hints.ai_socktype = SOCK_STREAM;
388: snprintf(portstr, sizeof(portstr), "%d", test->server_port);
1.1.1.2 ! misho 389: if ((gerror = getaddrinfo(test->server_hostname, portstr, &hints, &server_res)) != 0) {
1.1 misho 390: if (test->bind_address)
391: freeaddrinfo(local_res);
392: i_errno = IESTREAMCONNECT;
393: return -1;
394: }
395:
396: if ((s = socket(server_res->ai_family, SOCK_STREAM, 0)) < 0) {
397: if (test->bind_address)
398: freeaddrinfo(local_res);
399: freeaddrinfo(server_res);
400: i_errno = IESTREAMCONNECT;
401: return -1;
402: }
403:
1.1.1.2 ! misho 404: /*
! 405: * Various ways to bind the local end of the connection.
! 406: * 1. --bind (with or without --cport).
! 407: */
1.1 misho 408: if (test->bind_address) {
409: struct sockaddr_in *lcladdr;
410: lcladdr = (struct sockaddr_in *)local_res->ai_addr;
411: lcladdr->sin_port = htons(test->bind_port);
412:
413: if (bind(s, (struct sockaddr *) local_res->ai_addr, local_res->ai_addrlen) < 0) {
414: saved_errno = errno;
415: close(s);
416: freeaddrinfo(local_res);
417: freeaddrinfo(server_res);
418: errno = saved_errno;
419: i_errno = IESTREAMCONNECT;
420: return -1;
421: }
422: freeaddrinfo(local_res);
423: }
1.1.1.2 ! misho 424: /* --cport, no --bind */
! 425: else if (test->bind_port) {
! 426: size_t addrlen;
! 427: struct sockaddr_storage lcl;
! 428:
! 429: /* IPv4 */
! 430: if (server_res->ai_family == AF_INET) {
! 431: struct sockaddr_in *lcladdr = (struct sockaddr_in *) &lcl;
! 432: lcladdr->sin_family = AF_INET;
! 433: lcladdr->sin_port = htons(test->bind_port);
! 434: lcladdr->sin_addr.s_addr = INADDR_ANY;
! 435: addrlen = sizeof(struct sockaddr_in);
! 436: }
! 437: /* IPv6 */
! 438: else if (server_res->ai_family == AF_INET6) {
! 439: struct sockaddr_in6 *lcladdr = (struct sockaddr_in6 *) &lcl;
! 440: lcladdr->sin6_family = AF_INET6;
! 441: lcladdr->sin6_port = htons(test->bind_port);
! 442: lcladdr->sin6_addr = in6addr_any;
! 443: addrlen = sizeof(struct sockaddr_in6);
! 444: }
! 445: /* Unknown protocol */
! 446: else {
! 447: saved_errno = errno;
! 448: close(s);
! 449: freeaddrinfo(server_res);
! 450: errno = saved_errno;
! 451: i_errno = IEPROTOCOL;
! 452: return -1;
! 453: }
! 454:
! 455: if (bind(s, (struct sockaddr *) &lcl, addrlen) < 0) {
! 456: saved_errno = errno;
! 457: close(s);
! 458: freeaddrinfo(server_res);
! 459: errno = saved_errno;
! 460: i_errno = IESTREAMCONNECT;
! 461: return -1;
! 462: }
! 463: }
1.1 misho 464:
465: /* Set socket options */
466: if (test->no_delay) {
467: opt = 1;
468: if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) {
469: saved_errno = errno;
470: close(s);
471: freeaddrinfo(server_res);
472: errno = saved_errno;
473: i_errno = IESETNODELAY;
474: return -1;
475: }
476: }
477: if ((opt = test->settings->mss)) {
478: if (setsockopt(s, IPPROTO_TCP, TCP_MAXSEG, &opt, sizeof(opt)) < 0) {
479: saved_errno = errno;
480: close(s);
481: freeaddrinfo(server_res);
482: errno = saved_errno;
483: i_errno = IESETMSS;
484: return -1;
485: }
486: }
487: if ((opt = test->settings->socket_bufsize)) {
488: if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
489: saved_errno = errno;
490: close(s);
491: freeaddrinfo(server_res);
492: errno = saved_errno;
493: i_errno = IESETBUF;
494: return -1;
495: }
496: if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
497: saved_errno = errno;
498: close(s);
499: freeaddrinfo(server_res);
500: errno = saved_errno;
501: i_errno = IESETBUF;
502: return -1;
503: }
504: }
1.1.1.2 ! misho 505:
! 506: /* Read back and verify the sender socket buffer size */
! 507: optlen = sizeof(sndbuf_actual);
! 508: if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_actual, &optlen) < 0) {
! 509: saved_errno = errno;
! 510: close(s);
! 511: freeaddrinfo(server_res);
! 512: errno = saved_errno;
! 513: i_errno = IESETBUF;
! 514: return -1;
! 515: }
1.1 misho 516: if (test->debug) {
1.1.1.2 ! misho 517: printf("SNDBUF is %u, expecting %u\n", sndbuf_actual, test->settings->socket_bufsize);
! 518: }
! 519: if (test->settings->socket_bufsize && test->settings->socket_bufsize > sndbuf_actual) {
! 520: i_errno = IESETBUF2;
! 521: return -1;
1.1 misho 522: }
1.1.1.2 ! misho 523:
! 524: /* Read back and verify the receiver socket buffer size */
! 525: optlen = sizeof(rcvbuf_actual);
! 526: if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf_actual, &optlen) < 0) {
! 527: saved_errno = errno;
! 528: close(s);
! 529: freeaddrinfo(server_res);
! 530: errno = saved_errno;
! 531: i_errno = IESETBUF;
! 532: return -1;
! 533: }
! 534: if (test->debug) {
! 535: printf("RCVBUF is %u, expecting %u\n", rcvbuf_actual, test->settings->socket_bufsize);
! 536: }
! 537: if (test->settings->socket_bufsize && test->settings->socket_bufsize > rcvbuf_actual) {
! 538: i_errno = IESETBUF2;
! 539: return -1;
! 540: }
! 541:
! 542: if (test->json_output) {
! 543: cJSON_AddNumberToObject(test->json_start, "sock_bufsize", test->settings->socket_bufsize);
! 544: cJSON_AddNumberToObject(test->json_start, "sndbuf_actual", sndbuf_actual);
! 545: cJSON_AddNumberToObject(test->json_start, "rcvbuf_actual", rcvbuf_actual);
! 546: }
! 547:
1.1 misho 548: #if defined(HAVE_FLOWLABEL)
549: if (test->settings->flowlabel) {
550: if (server_res->ai_addr->sa_family != AF_INET6) {
551: saved_errno = errno;
552: close(s);
553: freeaddrinfo(server_res);
554: errno = saved_errno;
555: i_errno = IESETFLOW;
556: return -1;
557: } else {
558: struct sockaddr_in6* sa6P = (struct sockaddr_in6*) server_res->ai_addr;
559: char freq_buf[sizeof(struct in6_flowlabel_req)];
560: struct in6_flowlabel_req *freq = (struct in6_flowlabel_req *)freq_buf;
561: int freq_len = sizeof(*freq);
562:
563: memset(freq, 0, sizeof(*freq));
564: freq->flr_label = htonl(test->settings->flowlabel & IPV6_FLOWINFO_FLOWLABEL);
565: freq->flr_action = IPV6_FL_A_GET;
566: freq->flr_flags = IPV6_FL_F_CREATE;
1.1.1.2 ! misho 567: freq->flr_share = IPV6_FL_S_ANY;
1.1 misho 568: memcpy(&freq->flr_dst, &sa6P->sin6_addr, 16);
569:
570: if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWLABEL_MGR, freq, freq_len) < 0) {
571: saved_errno = errno;
572: close(s);
573: freeaddrinfo(server_res);
574: errno = saved_errno;
575: i_errno = IESETFLOW;
576: return -1;
577: }
578: sa6P->sin6_flowinfo = freq->flr_label;
579:
580: opt = 1;
581: if (setsockopt(s, IPPROTO_IPV6, IPV6_FLOWINFO_SEND, &opt, sizeof(opt)) < 0) {
582: saved_errno = errno;
583: close(s);
584: freeaddrinfo(server_res);
585: errno = saved_errno;
586: i_errno = IESETFLOW;
587: return -1;
588: }
589: }
590: }
591: #endif /* HAVE_FLOWLABEL */
592:
593: #if defined(HAVE_SO_MAX_PACING_RATE)
1.1.1.2 ! misho 594: /* If socket pacing is specified try to enable it. */
! 595: if (test->settings->fqrate) {
1.1 misho 596: /* Convert bits per second to bytes per second */
1.1.1.2 ! misho 597: unsigned int fqrate = test->settings->fqrate / 8;
! 598: if (fqrate > 0) {
1.1 misho 599: if (test->debug) {
1.1.1.2 ! misho 600: printf("Setting fair-queue socket pacing to %u\n", fqrate);
1.1 misho 601: }
1.1.1.2 ! misho 602: if (setsockopt(s, SOL_SOCKET, SO_MAX_PACING_RATE, &fqrate, sizeof(fqrate)) < 0) {
! 603: warning("Unable to set socket pacing");
1.1 misho 604: }
605: }
606: }
607: #endif /* HAVE_SO_MAX_PACING_RATE */
1.1.1.2 ! misho 608: {
! 609: unsigned int rate = test->settings->rate / 8;
! 610: if (rate > 0) {
! 611: if (test->debug) {
! 612: printf("Setting application pacing to %u\n", rate);
! 613: }
! 614: }
! 615: }
1.1 misho 616:
617: if (connect(s, (struct sockaddr *) server_res->ai_addr, server_res->ai_addrlen) < 0 && errno != EINPROGRESS) {
618: saved_errno = errno;
619: close(s);
620: freeaddrinfo(server_res);
621: errno = saved_errno;
622: i_errno = IESTREAMCONNECT;
623: return -1;
624: }
625:
626: freeaddrinfo(server_res);
627:
628: /* Send cookie for verification */
629: if (Nwrite(s, test->cookie, COOKIE_SIZE, Ptcp) < 0) {
630: saved_errno = errno;
631: close(s);
632: errno = saved_errno;
633: i_errno = IESENDCOOKIE;
634: return -1;
635: }
636:
637: return s;
638: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>