VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/slirp/tcp_subr.c@ 57784

Last change on this file since 57784 was 57784, checked in by vboxsync, 9 years ago

NAT: rewrite handling of port-forwarding.

The most interesting part is handling of wildcard guest address
(0.0.0.0) for which we are supposed to guess the real guest IP. For
TCP we delay the decision until new connection come and the we can use
the current guess for each new connection. For UDP things are
trickier. For now we set the current guess as the destination on
first incoming packet, but that doesn't handle changes of the guest
address or outgoing packets. This needs more thought.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.6 KB
Line 
1/* $Id: tcp_subr.c 57784 2015-09-16 15:36:31Z vboxsync $ */
2/** @file
3 * NAT - TCP support.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*
19 * This code is based on:
20 *
21 * Copyright (c) 1982, 1986, 1988, 1990, 1993
22 * The Regents of the University of California. All rights reserved.
23 *
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
26 * are met:
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. Redistributions in binary form must reproduce the above copyright
30 * notice, this list of conditions and the following disclaimer in the
31 * documentation and/or other materials provided with the distribution.
32 * 3. All advertising materials mentioning features or use of this software
33 * must display the following acknowledgement:
34 * This product includes software developed by the University of
35 * California, Berkeley and its contributors.
36 * 4. Neither the name of the University nor the names of its contributors
37 * may be used to endorse or promote products derived from this software
38 * without specific prior written permission.
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
41 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
43 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
44 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
45 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
46 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
48 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
49 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50 * SUCH DAMAGE.
51 *
52 * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93
53 * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp
54 */
55
56/*
57 * Changes and additions relating to SLiRP
58 * Copyright (c) 1995 Danny Gasparovski.
59 *
60 * Please read the file COPYRIGHT for the
61 * terms and conditions of the copyright.
62 */
63
64#include <slirp.h>
65
66
67/*
68 * Tcp initialization
69 */
70void
71tcp_init(PNATState pData)
72{
73 tcp_iss = 1; /* wrong */
74 tcb.so_next = tcb.so_prev = &tcb;
75 tcp_last_so = &tcb;
76 tcp_reass_maxqlen = 48;
77 tcp_reass_maxseg = 256;
78}
79
80/*
81 * Create template to be used to send tcp packets on a connection.
82 * Call after host entry created, fills
83 * in a skeletal tcp/ip header, minimizing the amount of work
84 * necessary when the connection is used.
85 */
86/* struct tcpiphdr * */
87void
88tcp_template(struct tcpcb *tp)
89{
90 struct socket *so = tp->t_socket;
91 register struct tcpiphdr *n = &tp->t_template;
92
93 memset(n->ti_x1, 0, 9);
94 n->ti_pr = IPPROTO_TCP;
95 n->ti_len = RT_H2N_U16(sizeof (struct tcpiphdr) - sizeof (struct ip));
96 n->ti_src = so->so_faddr;
97 n->ti_dst = so->so_laddr;
98 n->ti_sport = so->so_fport;
99 n->ti_dport = so->so_lport;
100
101 n->ti_seq = 0;
102 n->ti_ack = 0;
103 n->ti_x2 = 0;
104 n->ti_off = 5;
105 n->ti_flags = 0;
106 n->ti_win = 0;
107 n->ti_sum = 0;
108 n->ti_urp = 0;
109}
110
111/*
112 * Send a single message to the TCP at address specified by
113 * the given TCP/IP header. If m == 0, then we make a copy
114 * of the tcpiphdr at ti and send directly to the addressed host.
115 * This is used to force keep alive messages out using the TCP
116 * template for a connection tp->t_template. If flags are given
117 * then we send a message back to the TCP which originated the
118 * segment ti, and discard the mbuf containing it and any other
119 * attached mbufs.
120 *
121 * In any case the ack and sequence number of the transmitted
122 * segment are as specified by the parameters.
123 */
124void
125tcp_respond(PNATState pData, struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, tcp_seq ack, tcp_seq seq, int flags)
126{
127 register int tlen;
128
129 LogFlowFunc(("ENTER: tp = %R[tcpcb793], ti = %lx, m = %lx, ack = %u, seq = %u, flags = %x\n",
130 tp, (long)ti, (long)m, ack, seq, flags));
131
132 if (m == 0)
133 {
134 if ((m = m_gethdr(pData, M_DONTWAIT, MT_HEADER)) == NULL)
135 return;
136#ifdef TCP_COMPAT_42
137 tlen = 1;
138#else
139 tlen = 0;
140#endif
141 m->m_data += if_maxlinkhdr;
142 m->m_pkthdr.header = mtod(m, void *);
143 *mtod(m, struct tcpiphdr *) = *ti;
144 ti = mtod(m, struct tcpiphdr *);
145 flags = TH_ACK;
146 }
147 else
148 {
149 /*
150 * ti points into m so the next line is just making
151 * the mbuf point to ti
152 */
153 m->m_data = (caddr_t)ti;
154
155 m->m_len = sizeof (struct tcpiphdr);
156 tlen = 0;
157#define xchg(a,b,type) { type t; t = a; a = b; b = t; }
158 xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t);
159 xchg(ti->ti_dport, ti->ti_sport, u_int16_t);
160#undef xchg
161 }
162 ti->ti_len = RT_H2N_U16((u_short)(sizeof (struct tcphdr) + tlen));
163 tlen += sizeof (struct tcpiphdr);
164 m->m_len = tlen;
165
166 memset(ti->ti_x1, 0, 9);
167 ti->ti_seq = RT_H2N_U32(seq);
168 ti->ti_ack = RT_H2N_U32(ack);
169 ti->ti_x2 = 0;
170 ti->ti_off = sizeof (struct tcphdr) >> 2;
171 ti->ti_flags = flags;
172 if (tp)
173 {
174 int win = sbspace(&tp->t_socket->so_rcv);
175 ti->ti_win = RT_H2N_U16((u_int16_t) (win >> tp->rcv_scale));
176 }
177 else
178 ti->ti_win = 0;
179 ti->ti_urp = 0;
180 ti->ti_sum = 0;
181 ti->ti_sum = cksum(m, tlen);
182 ((struct ip *)ti)->ip_len = tlen;
183
184 if(flags & TH_RST)
185 ((struct ip *)ti)->ip_ttl = MAXTTL;
186 else
187 ((struct ip *)ti)->ip_ttl = ip_defttl;
188
189 (void) ip_output(pData, (struct socket *)0, m);
190}
191
192/*
193 * Create a new TCP control block, making an
194 * empty reassembly queue and hooking it to the argument
195 * protocol control block.
196 */
197struct tcpcb *
198tcp_newtcpcb(PNATState pData, struct socket *so)
199{
200 register struct tcpcb *tp;
201
202 tp = (struct tcpcb *)RTMemAllocZ(sizeof(*tp));
203 if (tp == NULL)
204 return ((struct tcpcb *)0);
205
206 tp->t_maxseg = tcp_mssdflt;
207
208 tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
209 tp->t_socket = so;
210
211 /*
212 * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
213 * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
214 * reasonable initial retransmit time.
215 */
216 tp->t_srtt = TCPTV_SRTTBASE;
217 tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2;
218 tp->t_rttmin = TCPTV_MIN;
219
220 TCPT_RANGESET(tp->t_rxtcur,
221 ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
222 TCPTV_MIN, TCPTV_REXMTMAX);
223
224 tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
225 tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
226 TCP_STATE_SWITCH_TO(tp, TCPS_CLOSED);
227
228 so->so_tcpcb = tp;
229 so->so_type = IPPROTO_TCP;
230
231 return (tp);
232}
233
234/*
235 * Drop a TCP connection, reporting
236 * the specified error. If connection is synchronized,
237 * then send a RST to peer.
238 */
239struct tcpcb *tcp_drop(PNATState pData, struct tcpcb *tp, int err)
240{
241/* tcp_drop(tp, errno)
242 register struct tcpcb *tp;
243 int errno;
244{
245*/
246 int fUninitiolizedTemplate = 0;
247#ifndef LOG_ENABLED
248 NOREF(err);
249#endif
250 LogFlowFunc(("ENTER: tp = %R[tcpcb793], errno = %d\n", tp, err));
251 fUninitiolizedTemplate = RT_BOOL(( tp
252 && ( tp->t_template.ti_src.s_addr == INADDR_ANY
253 || tp->t_template.ti_dst.s_addr == INADDR_ANY)));
254
255 if ( TCPS_HAVERCVDSYN(tp->t_state)
256 && !fUninitiolizedTemplate)
257 {
258 TCP_STATE_SWITCH_TO(tp, TCPS_CLOSED);
259 (void) tcp_output(pData, tp);
260 tcpstat.tcps_drops++;
261 }
262 else
263 tcpstat.tcps_conndrops++;
264#if 0
265 if (errno == ETIMEDOUT && tp->t_softerror)
266 errno = tp->t_softerror;
267
268 so->so_error = errno;
269#endif
270 return (tcp_close(pData, tp));
271}
272
273/*
274 * Close a TCP control block:
275 * discard all space held by the tcp
276 * discard internet protocol block
277 * wake up any sleepers
278 */
279struct tcpcb *
280tcp_close(PNATState pData, register struct tcpcb *tp)
281{
282 struct socket *so = tp->t_socket;
283
284 struct tseg_qent *te = NULL;
285 LogFlowFunc(("ENTER: tp = %R[tcpcb793]\n", tp));
286 /*XXX: freeing the reassembly queue */
287 while (!LIST_EMPTY(&tp->t_segq))
288 {
289 te = LIST_FIRST(&tp->t_segq);
290 LIST_REMOVE(te, tqe_q);
291 m_freem(pData, te->tqe_m);
292 RTMemFree(te);
293 tcp_reass_qsize--;
294 }
295 RTMemFree(tp);
296 so->so_tcpcb = 0;
297 soisfdisconnected(so);
298 /* clobber input socket cache if we're closing the cached connection */
299 if (so == tcp_last_so)
300 tcp_last_so = &tcb;
301 if (so->s != -1)
302 closesocket(so->s);
303 /* Avoid double free if the socket is listening and therefore doesn't have
304 * any sbufs reserved. */
305 if (!(so->so_state & SS_FACCEPTCONN))
306 {
307 sbfree(&so->so_rcv);
308 sbfree(&so->so_snd);
309 }
310 sofree(pData, so);
311 SOCKET_UNLOCK(so);
312 tcpstat.tcps_closed++;
313 return ((struct tcpcb *)0);
314}
315
316void
317tcp_drain()
318{
319 /* XXX */
320}
321
322/*
323 * When a source quench is received, close congestion window
324 * to one segment. We will gradually open it again as we proceed.
325 */
326
327#if 0
328
329void
330tcp_quench(i, int errno)
331{
332 struct tcpcb *tp = intotcpcb(inp);
333
334 if (tp)
335 tp->snd_cwnd = tp->t_maxseg;
336}
337
338#endif
339
340/*
341 * TCP protocol interface to socket abstraction.
342 */
343
344/*
345 * User issued close, and wish to trail through shutdown states:
346 * if never received SYN, just forget it. If got a SYN from peer,
347 * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
348 * If already got a FIN from peer, then almost done; go to LAST_ACK
349 * state. In all other cases, have already sent FIN to peer (e.g.
350 * after PRU_SHUTDOWN), and just have to play tedious game waiting
351 * for peer to send FIN or not respond to keep-alives, etc.
352 * We can let the user exit from the close as soon as the FIN is acked.
353 */
354void
355tcp_sockclosed(PNATState pData, struct tcpcb *tp)
356{
357 LogFlowFunc(("ENTER: tp = %R[tcpcb793]\n", tp));
358 LogFunc(("tp->t_socket:%R[natsock]\n",tp->t_socket));
359
360 switch (tp->t_state)
361 {
362 case TCPS_CLOSED:
363 case TCPS_LISTEN:
364 case TCPS_SYN_SENT:
365 TCP_STATE_SWITCH_TO(tp, TCPS_CLOSED);
366 tp = tcp_close(pData, tp);
367 break;
368
369 case TCPS_SYN_RECEIVED:
370 case TCPS_ESTABLISHED:
371 TCP_STATE_SWITCH_TO(tp, TCPS_FIN_WAIT_1);
372 break;
373
374 case TCPS_CLOSE_WAIT:
375 TCP_STATE_SWITCH_TO(tp, TCPS_LAST_ACK);
376 break;
377 }
378/* soisfdisconnecting(tp->t_socket); */
379 if ( tp
380 && tp->t_state >= TCPS_FIN_WAIT_2)
381 soisfdisconnected(tp->t_socket);
382 /*
383 * (vasily) there're situations when the FIN or FIN,ACK are lost (Windows host)
384 * and retransmitting keeps VBox busy on sending closing sequences *very* frequent,
385 * easting a lot of CPU. To avoid this we don't sent on sockets marked as closed
386 * (see slirp.c for details about setting so_close member).
387 */
388 if ( tp
389 && tp->t_socket
390 && !tp->t_socket->so_close)
391 tcp_output(pData, tp);
392}
393
394/*
395 * Connect to a host on the Internet
396 * Called by tcp_input
397 * Only do a connect, the tcp fields will be set in tcp_input
398 * return 0 if there's a result of the connect,
399 * else return -1 means we're still connecting
400 * The return value is almost always -1 since the socket is
401 * nonblocking. Connect returns after the SYN is sent, and does
402 * not wait for ACK+SYN.
403 */
404int tcp_fconnect(PNATState pData, struct socket *so)
405{
406 int ret = 0;
407
408 LogFlowFunc(("ENTER: so = %R[natsock]\n", so));
409
410 if ((ret = so->s = socket(AF_INET, SOCK_STREAM, 0)) >= 0)
411 {
412 int opt, s = so->s;
413 struct sockaddr_in addr;
414
415 fd_nonblock(s);
416 opt = 1;
417 setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
418 opt = 1;
419 setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(opt));
420
421 addr.sin_family = AF_INET;
422 if ((so->so_faddr.s_addr & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
423 {
424 /* It's an alias */
425 switch(RT_N2H_U32(so->so_faddr.s_addr) & ~pData->netmask)
426 {
427 case CTL_DNS:
428 case CTL_ALIAS:
429 default:
430 addr.sin_addr = loopback_addr;
431 break;
432 }
433 }
434 else
435 addr.sin_addr = so->so_faddr;
436 addr.sin_port = so->so_fport;
437
438 Log2((" connect()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n",
439 RT_N2H_U16(addr.sin_port), inet_ntoa(addr.sin_addr)));
440 /* We don't care what port we get */
441 ret = connect(s,(struct sockaddr *)&addr,sizeof (addr));
442
443 /*
444 * If it's not in progress, it failed, so we just return 0,
445 * without clearing SS_NOFDREF
446 */
447 soisfconnecting(so);
448 }
449
450 return(ret);
451}
452
453/*
454 * Accept the socket and connect to the local-host
455 *
456 * We have a problem. The correct thing to do would be
457 * to first connect to the local-host, and only if the
458 * connection is accepted, then do an accept() here.
459 * But, a) we need to know who's trying to connect
460 * to the socket to be able to SYN the local-host, and
461 * b) we are already connected to the foreign host by
462 * the time it gets to accept(), so... We simply accept
463 * here and SYN the local-host.
464 */
465void
466tcp_connect(PNATState pData, struct socket *inso)
467{
468 struct socket *so;
469 struct sockaddr_in addr;
470 socklen_t addrlen = sizeof(struct sockaddr_in);
471 struct tcpcb *tp;
472 int s, opt;
473 int status;
474 socklen_t optlen;
475 static int cVerbose = 1;
476
477 LogFlowFunc(("ENTER: inso = %R[natsock]\n", inso));
478
479 if ( inso->so_laddr.s_addr == INADDR_ANY /* delayed port-forwarding? */
480 && pData->guest_addr_guess.s_addr == INADDR_ANY)
481 {
482 LogRel2(("NAT: port-forward: guest address unknown for %R[natsock]\n", inso));
483 closesocket(accept(inso->s, NULL, NULL));
484 if (inso->so_state & SS_FACCEPTONCE)
485 tcp_close(pData, sototcpcb(inso));
486 return;
487 }
488
489 /*
490 * If it's an SS_ACCEPTONCE socket, no need to socreate()
491 * another socket, just use the accept() socket.
492 */
493 if (inso->so_state & SS_FACCEPTONCE)
494 {
495 /* FACCEPTONCE already have a tcpcb */
496 so = inso;
497 }
498 else
499 {
500 if ((so = socreate()) == NULL)
501 {
502 /* If it failed, get rid of the pending connection */
503 closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen));
504 return;
505 }
506 if (tcp_attach(pData, so) < 0)
507 {
508 RTMemFree(so); /* NOT sofree */
509 return;
510 }
511 so->so_laddr = inso->so_laddr;
512 so->so_lport = inso->so_lport;
513 }
514
515 if (so->so_laddr.s_addr == INADDR_ANY)
516 {
517 LogRel2(("NAT: port-forward: using %RTnaipv4 for %R[natsock]\n",
518 pData->guest_addr_guess.s_addr, inso));
519 so->so_laddr = pData->guest_addr_guess;
520 }
521
522 (void) tcp_mss(pData, sototcpcb(so), 0);
523
524 fd_nonblock(inso->s);
525 if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0)
526 {
527 tcp_close(pData, sototcpcb(so)); /* This will sofree() as well */
528 return;
529 }
530 fd_nonblock(s);
531 opt = 1;
532 setsockopt(s, SOL_SOCKET, SO_REUSEADDR,(char *)&opt, sizeof(int));
533 opt = 1;
534 setsockopt(s, SOL_SOCKET, SO_OOBINLINE,(char *)&opt, sizeof(int));
535#if 0
536 opt = 1;
537 setsockopt(s, IPPROTO_TCP, TCP_NODELAY,(char *)&opt, sizeof(int));
538#endif
539
540 optlen = sizeof(int);
541 status = getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &optlen);
542 if (status < 0)
543 {
544 LogRel(("NAT: Error(%d) while getting RCV capacity\n", errno));
545 goto no_sockopt;
546 }
547 if (cVerbose > 0)
548 LogRel(("NAT: old socket rcv size: %dKB\n", opt / 1024));
549 /* @todo (r-vvl) make it configurable (via extra data) */
550 opt = pData->socket_rcv;
551 status = setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, sizeof(int));
552 if (status < 0)
553 {
554 LogRel(("NAT: Error(%d) while setting RCV capacity to (%d)\n", errno, opt));
555 goto no_sockopt;
556 }
557 optlen = sizeof(int);
558 status = getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&opt, &optlen);
559 if (status < 0)
560 {
561 LogRel(("NAT: Error(%d) while getting SND capacity\n", errno));
562 goto no_sockopt;
563 }
564 if (cVerbose > 0)
565 LogRel(("NAT: old socket snd size: %dKB\n", opt / 1024));
566 opt = pData->socket_rcv;
567 status = setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&opt, sizeof(int));
568 if (status < 0)
569 {
570 LogRel(("NAT: Error(%d) while setting SND capacity to (%d)\n", errno, opt));
571 goto no_sockopt;
572 }
573 if (cVerbose > 0)
574 cVerbose--;
575
576 no_sockopt:
577 so->so_fport = addr.sin_port;
578 so->so_faddr = addr.sin_addr;
579 /* Translate connections from localhost to the real hostname */
580 if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr)
581 so->so_faddr = alias_addr;
582
583 /* Close the accept() socket, set right state */
584 if (inso->so_state & SS_FACCEPTONCE)
585 {
586 closesocket(so->s); /* If we only accept once, close the accept() socket */
587 so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */
588 /* if it's not FACCEPTONCE, it's already NOFDREF */
589 }
590 so->s = s;
591
592 tp = sototcpcb(so);
593
594 tcp_template(tp);
595
596 /* Compute window scaling to request. */
597/* while (tp->request_r_scale < TCP_MAX_WINSHIFT
598 * && (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
599 * tp->request_r_scale++;
600 */
601
602/* soisconnecting(so); */ /* NOFDREF used instead */
603 tcpstat.tcps_connattempt++;
604
605 TCP_STATE_SWITCH_TO(tp, TCPS_SYN_SENT);
606 tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
607 tp->iss = tcp_iss;
608 tcp_iss += TCP_ISSINCR/2;
609 tcp_sendseqinit(tp);
610 tcp_output(pData, tp);
611}
612
613/*
614 * Attach a TCPCB to a socket.
615 */
616int
617tcp_attach(PNATState pData, struct socket *so)
618{
619 /* We're attaching already attached socket??? */
620 Assert(so->so_type == 0);
621 if ((so->so_tcpcb = tcp_newtcpcb(pData, so)) == NULL)
622 return -1;
623
624 SOCKET_LOCK_CREATE(so);
625 QSOCKET_LOCK(tcb);
626 insque(pData, so, &tcb);
627 NSOCK_INC();
628 QSOCKET_UNLOCK(tcb);
629 return 0;
630}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette