]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kpi_socket.c
xnu-792.12.6.tar.gz
[apple/xnu.git] / bsd / kern / kpi_socket.c
CommitLineData
91447636
A
1/*
2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
3 *
8ad349bb 4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
91447636 5 *
8ad349bb
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
14 * agreement.
15 *
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
18 * file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
91447636
A
29 */
30
31#define __KPI__
32#include <sys/kernel.h>
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <sys/socketvar.h>
36#include <sys/param.h>
37#include <sys/proc.h>
38#include <sys/errno.h>
39#include <sys/malloc.h>
40#include <sys/protosw.h>
41#include <sys/domain.h>
42#include <sys/mbuf.h>
43#include <sys/fcntl.h>
44#include <sys/filio.h>
45#include <sys/uio_internal.h>
46#include <kern/lock.h>
47
48extern void *memcpy(void *, const void *, size_t);
49extern int soclose_locked(struct socket *so);
50
51errno_t sock_send_internal(
52 socket_t sock,
53 const struct msghdr *msg,
54 mbuf_t data,
55 int flags,
56 size_t *sentlen);
57
58
59
60errno_t
61sock_accept(
62 socket_t sock,
63 struct sockaddr *from,
64 int fromlen,
65 int flags,
66 sock_upcall callback,
67 void* cookie,
68 socket_t *new_sock)
69{
70 struct sockaddr *sa;
71 struct socket *new_so;
72 lck_mtx_t *mutex_held;
73 int dosocklock;
74 errno_t error = 0;
75
76 if (sock == NULL || new_sock == NULL) return EINVAL;
77 socket_lock(sock, 1);
78 if ((sock->so_options & SO_ACCEPTCONN) == 0) {
79 socket_unlock(sock, 1);
80 return EINVAL;
81 }
82 if ((flags & ~(MSG_DONTWAIT)) != 0) {
83 socket_unlock(sock, 1);
84 return ENOTSUP;
85 }
86 if (((flags & MSG_DONTWAIT) != 0 || (sock->so_state & SS_NBIO) != 0) &&
87 sock->so_comp.tqh_first == NULL) {
88 socket_unlock(sock, 1);
89 return EWOULDBLOCK;
90 }
91
92 if (sock->so_proto->pr_getlock != NULL) {
93 mutex_held = (*sock->so_proto->pr_getlock)(sock, 0);
94 dosocklock = 1;
95 }
96 else {
97 mutex_held = sock->so_proto->pr_domain->dom_mtx;
98 dosocklock = 0;
99 }
100
101 while (TAILQ_EMPTY(&sock->so_comp) && sock->so_error == 0) {
102 if (sock->so_state & SS_CANTRCVMORE) {
103 sock->so_error = ECONNABORTED;
104 break;
105 }
106 error = msleep((caddr_t)&sock->so_timeo, mutex_held, PSOCK | PCATCH, "sock_accept", 0);
107 if (error) {
108 socket_unlock(sock, 1);
109 return (error);
110 }
111 }
112 if (sock->so_error) {
113 error = sock->so_error;
114 sock->so_error = 0;
115 socket_unlock(sock, 1);
116 return (error);
117 }
118
119 new_so = TAILQ_FIRST(&sock->so_comp);
120 TAILQ_REMOVE(&sock->so_comp, new_so, so_list);
121 sock->so_qlen--;
122 socket_unlock(sock, 1); /* release the head */
123
124 if (dosocklock) {
125 lck_mtx_assert(new_so->so_proto->pr_getlock(new_so, 0),
126 LCK_MTX_ASSERT_NOTOWNED);
127 socket_lock(new_so, 1);
128 }
129
130 new_so->so_state &= ~SS_COMP;
131 new_so->so_head = NULL;
132 soacceptlock(new_so, &sa, 0);
133
134 if (callback) {
135 new_so->so_upcall = callback;
136 new_so->so_upcallarg = cookie;
137 new_so->so_rcv.sb_flags |= SB_UPCALL;
138 }
139
140 if (sa && from)
141 {
142 if (fromlen > sa->sa_len) fromlen = sa->sa_len;
143 memcpy(from, sa, fromlen);
144 }
145 if (sa) FREE(sa, M_SONAME);
146 *new_sock = new_so;
147 if (dosocklock)
148 socket_unlock(new_so, 1);
149 return error;
150}
151
152errno_t
153sock_bind(
154 socket_t sock,
155 const struct sockaddr *to)
156{
157 if (sock == NULL || to == NULL) return EINVAL;
158
159 return sobind(sock, (struct sockaddr*)to);
160}
161
162errno_t
163sock_connect(
164 socket_t sock,
165 const struct sockaddr *to,
166 int flags)
167{
168 int error = 0;
169 lck_mtx_t *mutex_held;
170
171 if (sock == NULL || to == NULL) return EINVAL;
172
173 socket_lock(sock, 1);
174
175 if ((sock->so_state & SS_ISCONNECTING) &&
176 ((sock->so_state & SS_NBIO) != 0 ||
177 (flags & MSG_DONTWAIT) != 0)) {
178 socket_unlock(sock, 1);
179 return EALREADY;
180 }
181 error = soconnectlock(sock, (struct sockaddr*)to, 0);
182 if (!error) {
183 if ((sock->so_state & SS_ISCONNECTING) &&
184 ((sock->so_state & SS_NBIO) != 0 || (flags & MSG_DONTWAIT) != 0)) {
185 socket_unlock(sock, 1);
186 return EINPROGRESS;
187 }
188
189 if (sock->so_proto->pr_getlock != NULL)
190 mutex_held = (*sock->so_proto->pr_getlock)(sock, 0);
191 else
192 mutex_held = sock->so_proto->pr_domain->dom_mtx;
193
194 while ((sock->so_state & SS_ISCONNECTING) && sock->so_error == 0) {
195 error = msleep((caddr_t)&sock->so_timeo, mutex_held, PSOCK | PCATCH,
196 "sock_connect", 0);
197 if (error)
198 break;
199 }
200
201 if (error == 0) {
202 error = sock->so_error;
203 sock->so_error = 0;
204 }
205 }
206 else {
207 sock->so_state &= ~SS_ISCONNECTING;
208 }
209 socket_unlock(sock, 1);
210 return error;
211}
212
213errno_t
214sock_connectwait(
215 socket_t sock,
216 const struct timeval *tv)
217{
218 lck_mtx_t * mutex_held;
219 errno_t retval = 0;
220 struct timespec ts;
221
222 socket_lock(sock, 1);
223
224 // Check if we're already connected or if we've already errored out
225 if ((sock->so_state & SS_ISCONNECTING) == 0 || sock->so_error) {
226 if (sock->so_error) {
227 retval = sock->so_error;
228 sock->so_error = 0;
229 }
230 else {
231 if ((sock->so_state & SS_ISCONNECTED) != 0)
232 retval = 0;
233 else
234 retval = EINVAL;
235 }
236 goto done;
237 }
238
239 // copied translation from timeval to hertz from SO_RCVTIMEO handling
240 if (tv->tv_sec < 0 || tv->tv_sec > SHRT_MAX / hz ||
241 tv->tv_usec < 0 || tv->tv_usec >= 1000000) {
242 retval = EDOM;
243 goto done;
244 }
245
246 ts.tv_sec = tv->tv_sec;
247 ts.tv_nsec = (tv->tv_usec * NSEC_PER_USEC);
248 if ( (ts.tv_sec + (ts.tv_nsec/NSEC_PER_SEC))/100 > SHRT_MAX) {
249 retval = EDOM;
250 goto done;
251 }
252
253 if (sock->so_proto->pr_getlock != NULL)
254 mutex_held = (*sock->so_proto->pr_getlock)(sock, 0);
255 else
256 mutex_held = sock->so_proto->pr_domain->dom_mtx;
257
258 msleep((caddr_t)&sock->so_timeo, mutex_held, PSOCK, "sock_connectwait", &ts);
259
260 // Check if we're still waiting to connect
261 if ((sock->so_state & SS_ISCONNECTING) && sock->so_error == 0) {
262 retval = EINPROGRESS;
263 goto done;
264 }
265
266 if (sock->so_error) {
267 retval = sock->so_error;
268 sock->so_error = 0;
269 }
270
271done:
272 socket_unlock(sock, 1);
273 return retval;
274}
275
276errno_t
277sock_nointerrupt(
278 socket_t sock,
279 int on)
280{
281 socket_lock(sock, 1);
282
283 if (on) {
284 sock->so_rcv.sb_flags |= SB_NOINTR; // This isn't safe
285 sock->so_snd.sb_flags |= SB_NOINTR; // This isn't safe
286 }
287 else {
288 sock->so_rcv.sb_flags &= ~SB_NOINTR; // This isn't safe
289 sock->so_snd.sb_flags &= ~SB_NOINTR; // This isn't safe
290 }
291
292 socket_unlock(sock, 1);
293
294 return 0;
295}
296
297errno_t
298sock_getpeername(
299 socket_t sock,
300 struct sockaddr *peername,
301 int peernamelen)
302{
303 int error = 0;
304 struct sockaddr *sa = NULL;
305
306 if (sock == NULL || peername == NULL || peernamelen < 0) return EINVAL;
307 socket_lock(sock, 1);
308 if ((sock->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) {
309 socket_unlock(sock, 1);
310 return ENOTCONN;
311 }
312 error = sock->so_proto->pr_usrreqs->pru_peeraddr(sock, &sa);
313 if (!error)
314 {
315 if (peernamelen > sa->sa_len) peernamelen = sa->sa_len;
316 memcpy(peername, sa, peernamelen);
317 }
318 if (sa) FREE(sa, M_SONAME);
319 socket_unlock(sock, 1);
320 return error;
321}
322
323errno_t
324sock_getsockname(
325 socket_t sock,
326 struct sockaddr *sockname,
327 int socknamelen)
328{
329 int error = 0;
330 struct sockaddr *sa = NULL;
331
332 if (sock == NULL || sockname == NULL || socknamelen < 0) return EINVAL;
333 socket_lock(sock, 1);
334 error = sock->so_proto->pr_usrreqs->pru_sockaddr(sock, &sa);
335 if (!error)
336 {
337 if (socknamelen > sa->sa_len) socknamelen = sa->sa_len;
338 memcpy(sockname, sa, socknamelen);
339 }
340 if (sa) FREE(sa, M_SONAME);
341 socket_unlock(sock, 1);
342 return error;
343}
344
345errno_t
346sock_getsockopt(
347 socket_t sock,
348 int level,
349 int optname,
350 void *optval,
351 int *optlen)
352{
353 int error = 0;
354 struct sockopt sopt;
355
356 if (sock == NULL || optval == NULL || optlen == NULL) return EINVAL;
357 sopt.sopt_dir = SOPT_GET;
358 sopt.sopt_level = level;
359 sopt.sopt_name = optname;
360 sopt.sopt_val = CAST_USER_ADDR_T(optval);
361 sopt.sopt_valsize = *optlen;
362 sopt.sopt_p = NULL;
363 error = sogetopt(sock, &sopt); /* will lock socket */
364 if (error == 0) *optlen = sopt.sopt_valsize;
365 return error;
366}
367
368errno_t
369sock_ioctl(
370 socket_t sock,
371 unsigned long request,
372 void *argp)
373{
374 return soioctl(sock, request, argp, NULL); /* will lock socket */
375}
376
377errno_t
378sock_setsockopt(
379 socket_t sock,
380 int level,
381 int optname,
382 const void *optval,
383 int optlen)
384{
385 struct sockopt sopt;
386
387 if (sock == NULL || optval == NULL) return EINVAL;
388 sopt.sopt_dir = SOPT_SET;
389 sopt.sopt_level = level;
390 sopt.sopt_name = optname;
391 sopt.sopt_val = CAST_USER_ADDR_T(optval);
392 sopt.sopt_valsize = optlen;
393 sopt.sopt_p = NULL;
394 return sosetopt(sock, &sopt); /* will lock socket */
395}
396
397errno_t
398sock_listen(
399 socket_t sock,
400 int backlog)
401{
402 if (sock == NULL) return EINVAL;
403 return solisten(sock, backlog); /* will lock socket */
404}
405
406static errno_t
407sock_receive_internal(
408 socket_t sock,
409 struct msghdr *msg,
410 mbuf_t *data,
411 int flags,
412 size_t *recvdlen)
413{
414 uio_t auio;
415 struct mbuf *control = NULL;
416 int error = 0;
417 int length = 0;
418 struct sockaddr *fromsa;
419 char uio_buf[ UIO_SIZEOF((msg != NULL) ? msg->msg_iovlen : 0) ];
420
421 if (sock == NULL) return EINVAL;
422
423 auio = uio_createwithbuffer(((msg != NULL) ? msg->msg_iovlen : 0),
424 0, UIO_SYSSPACE, UIO_READ,
425 &uio_buf[0], sizeof(uio_buf));
426 if (msg && data == NULL) {
427 int i;
428 struct iovec_32 *tempp = (struct iovec_32 *) msg->msg_iov;
429
430 for (i = 0; i < msg->msg_iovlen; i++) {
431 uio_addiov(auio, CAST_USER_ADDR_T((tempp + i)->iov_base), (tempp + i)->iov_len);
432 }
433 if (uio_resid(auio) < 0) return EINVAL;
434 }
435 else {
436 uio_setresid(auio, (uio_resid(auio) + *recvdlen));
437 }
438 length = uio_resid(auio);
439
440 if (recvdlen)
441 *recvdlen = 0;
442
443 if (msg && msg->msg_control) {
444 if ((size_t)msg->msg_controllen < sizeof(struct cmsghdr)) return EINVAL;
445 if ((size_t)msg->msg_controllen > MLEN) return EINVAL;
446 control = m_get(M_NOWAIT, MT_CONTROL);
447 if (control == NULL) return ENOMEM;
448 memcpy(mtod(control, caddr_t), msg->msg_control, msg->msg_controllen);
449 control->m_len = msg->msg_controllen;
450 }
451
452 /* let pru_soreceive handle the socket locking */
453 error = sock->so_proto->pr_usrreqs->pru_soreceive(sock, &fromsa, auio,
454 data, control ? &control : NULL, &flags);
455 if (error) goto cleanup;
456
457 if (recvdlen)
458 *recvdlen = length - uio_resid(auio);
459 if (msg) {
460 msg->msg_flags = flags;
461
462 if (msg->msg_name)
463 {
464 int salen;
465 salen = msg->msg_namelen;
466 if (msg->msg_namelen > 0 && fromsa != 0)
467 {
468 salen = MIN(salen, fromsa->sa_len);
469 memcpy(msg->msg_name, fromsa,
470 msg->msg_namelen > fromsa->sa_len ? fromsa->sa_len : msg->msg_namelen);
471 }
472 }
473
474 if (msg->msg_control)
475 {
476 struct mbuf* m = control;
477 u_char* ctlbuf = msg->msg_control;
478 int clen = msg->msg_controllen;
479 msg->msg_controllen = 0;
480
481 while (m && clen > 0)
482 {
483 unsigned int tocopy;
484 if (clen >= m->m_len)
485 {
486 tocopy = m->m_len;
487 }
488 else
489 {
490 msg->msg_flags |= MSG_CTRUNC;
491 tocopy = clen;
492 }
493 memcpy(ctlbuf, mtod(m, caddr_t), tocopy);
494 ctlbuf += tocopy;
495 clen -= tocopy;
496 m = m->m_next;
497 }
498 msg->msg_controllen = (u_int32_t)ctlbuf - (u_int32_t)msg->msg_control;
499 }
500 }
501
502cleanup:
503 if (control) m_freem(control);
504 if (fromsa) FREE(fromsa, M_SONAME);
505 return error;
506}
507
508errno_t
509sock_receive(
510 socket_t sock,
511 struct msghdr *msg,
512 int flags,
513 size_t *recvdlen)
514{
515 if ((msg == NULL) ||
516 (msg->msg_iovlen < 1) ||
517 (msg->msg_iov[0].iov_len == 0) ||
518 (msg->msg_iov[0].iov_base == NULL))
519 return EINVAL;
520 return sock_receive_internal(sock, msg, NULL, flags, recvdlen);
521}
522
523errno_t
524sock_receivembuf(
525 socket_t sock,
526 struct msghdr *msg,
527 mbuf_t *data,
528 int flags,
529 size_t *recvlen)
530{
531 if (data == NULL || recvlen == 0 || *recvlen <= 0 || (msg &&
532 (msg->msg_iov != NULL || msg->msg_iovlen != 0)))
533 return EINVAL;
534 return sock_receive_internal(sock, msg, data, flags, recvlen);
535}
536
537errno_t
538sock_send_internal(
539 socket_t sock,
540 const struct msghdr *msg,
541 mbuf_t data,
542 int flags,
543 size_t *sentlen)
544{
545 uio_t auio = NULL;
546 struct mbuf *control = NULL;
547 int error = 0;
548 int datalen = 0;
549 char uio_buf[ UIO_SIZEOF((msg != NULL ? msg->msg_iovlen : 1)) ];
550
551 if (sock == NULL) {
552 error = EINVAL;
553 goto errorout;
554 }
555
556 if (data == 0 && msg != NULL) {
557 struct iovec_32 *tempp = (struct iovec_32 *) msg->msg_iov;
558
559 auio = uio_createwithbuffer(msg->msg_iovlen, 0, UIO_SYSSPACE, UIO_WRITE,
560 &uio_buf[0], sizeof(uio_buf));
561 if (tempp != NULL)
562 {
563 int i;
564
565 for (i = 0; i < msg->msg_iovlen; i++) {
566 uio_addiov(auio, CAST_USER_ADDR_T((tempp + i)->iov_base), (tempp + i)->iov_len);
567 }
568
569 if (uio_resid(auio) < 0) {
570 error = EINVAL;
571 goto errorout;
572 }
573 }
574 }
575
576 if (sentlen)
577 *sentlen = 0;
578
579 if (auio)
580 datalen = uio_resid(auio);
581 else
582 datalen = data->m_pkthdr.len;
583
584 if (msg && msg->msg_control)
585 {
586 if ((size_t)msg->msg_controllen < sizeof(struct cmsghdr)) return EINVAL;
587 if ((size_t)msg->msg_controllen > MLEN) return EINVAL;
588 control = m_get(M_NOWAIT, MT_CONTROL);
589 if (control == NULL) {
590 error = ENOMEM;
591 goto errorout;
592 }
593 memcpy(mtod(control, caddr_t), msg->msg_control, msg->msg_controllen);
594 control->m_len = msg->msg_controllen;
595 }
596
597 error = sock->so_proto->pr_usrreqs->pru_sosend(sock, msg ? (struct sockaddr*)msg->msg_name : 0,
598 auio, data, control, flags);
599 if (error == 0 && sentlen) {
600 if (auio)
601 *sentlen = datalen - uio_resid(auio);
602 else
603 *sentlen = datalen;
604 }
605
606 return error;
607
608/*
609 * In cases where we detect an error before returning, we need to
610 * free the mbuf chain if there is one. sosend (and pru_sosend) will
611 * free the mbuf chain if they encounter an error.
612 */
613errorout:
614 if (control)
615 m_freem(control);
616 if (data)
617 m_freem(data);
618 if (sentlen)
619 *sentlen = 0;
620 return error;
621}
622
623errno_t
624sock_send(
625 socket_t sock,
626 const struct msghdr *msg,
627 int flags,
628 size_t *sentlen)
629{
630 if (msg == NULL || msg->msg_iov == NULL || msg->msg_iovlen < 1)
631 return EINVAL;
632 return sock_send_internal(sock, msg, NULL, flags, sentlen);
633}
634
635errno_t
636sock_sendmbuf(
637 socket_t sock,
638 const struct msghdr *msg,
639 mbuf_t data,
640 int flags,
641 size_t *sentlen)
642{
643 if (data == NULL || (msg &&
644 (msg->msg_iov != NULL || msg->msg_iovlen != 0))) {
645 if (data)
646 m_freem(data);
647 return EINVAL;
648 }
649 return sock_send_internal(sock, msg, data, flags, sentlen);
650}
651
652errno_t
653sock_shutdown(
654 socket_t sock,
655 int how)
656{
657 if (sock == NULL) return EINVAL;
658 return soshutdown(sock, how);
659}
660
661typedef void (*so_upcall)(struct socket *sock, void* arg, int waitf);
662
663errno_t
664sock_socket(
665 int domain,
666 int type,
667 int protocol,
668 sock_upcall callback,
669 void* context,
670 socket_t *new_so)
671{
672 int error = 0;
673 if (new_so == NULL) return EINVAL;
674 /* socreate will create an initial so_count */
675 error = socreate(domain, new_so, type, protocol);
676 if (error == 0 && callback)
677 {
678 (*new_so)->so_rcv.sb_flags |= SB_UPCALL;
679 (*new_so)->so_upcall = (so_upcall)callback;
680 (*new_so)->so_upcallarg = context;
681 }
682 return error;
683}
684
685void
686sock_close(
687 socket_t sock)
688{
689 if (sock == NULL) return;
690 soclose(sock);
691}
692
693/* Do we want this to be APPLE_PRIVATE API?: YES (LD 12/23/04)*/
694void
695sock_retain(
696 socket_t sock)
697{
698 if (sock == NULL) return;
699 socket_lock(sock, 1);
700 sock->so_retaincnt++;
701 sock->so_usecount++; /* add extra reference for holding the socket */
702 socket_unlock(sock, 1);
703}
704
705/* Do we want this to be APPLE_PRIVATE API? */
706void
707sock_release(
708 socket_t sock)
709{
710 if (sock == NULL) return;
711 socket_lock(sock, 1);
712 sock->so_retaincnt--;
713 if (sock->so_retaincnt < 0)
714 panic("sock_release: negative retain count for sock=%x cnt=%x\n",
715 sock, sock->so_retaincnt);
716 if ((sock->so_retaincnt == 0) && (sock->so_usecount == 2))
717 soclose_locked(sock); /* close socket only if the FD is not holding it */
718 else
719 sock->so_usecount--; /* remove extra reference holding the socket */
720 socket_unlock(sock, 1);
721}
722
723errno_t
724sock_setpriv(
725 socket_t sock,
726 int on)
727{
728 if (sock == NULL) return EINVAL;
729 socket_lock(sock, 1);
730 if (on)
731 {
732 sock->so_state |= SS_PRIV;
733 }
734 else
735 {
736 sock->so_state &= ~SS_PRIV;
737 }
738 socket_unlock(sock, 1);
739 return 0;
740}
741
742int
743sock_isconnected(
744 socket_t sock)
745{
746 int retval;
747 socket_lock(sock, 1);
748 retval = (sock->so_state & SS_ISCONNECTED) != 0;
749 socket_unlock(sock, 1);
750 return (retval);
751}
752
753int
754sock_isnonblocking(
755 socket_t sock)
756{
757 int retval;
758 socket_lock(sock, 1);
759 retval = (sock->so_state & SS_NBIO) != 0;
760 socket_unlock(sock, 1);
761 return (retval);
762}
763
764errno_t
765sock_gettype(
766 socket_t sock,
767 int *outDomain,
768 int *outType,
769 int *outProtocol)
770{
771 socket_lock(sock, 1);
772 if (outDomain)
773 *outDomain = sock->so_proto->pr_domain->dom_family;
774 if (outType)
775 *outType = sock->so_type;
776 if (outProtocol)
777 *outProtocol = sock->so_proto->pr_protocol;
778 socket_unlock(sock, 1);
779 return 0;
780}