Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Socket functions
4 : Copyright (C) Andrew Tridgell 1992-1998
5 : Copyright (C) Tim Potter 2000-2001
6 : Copyright (C) Stefan Metzmacher 2004
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include "includes.h"
23 : #include "lib/socket/socket.h"
24 : #include "system/filesys.h"
25 : #include "system/network.h"
26 : #include "param/param.h"
27 : #include "../lib/tsocket/tsocket.h"
28 : #include "lib/util/util_net.h"
29 :
30 : /*
31 : auto-close sockets on free
32 : */
33 421390 : static int socket_destructor(struct socket_context *sock)
34 : {
35 421390 : if (sock->ops->fn_close &&
36 421390 : !(sock->flags & SOCKET_FLAG_NOCLOSE)) {
37 121828 : sock->ops->fn_close(sock);
38 : }
39 421390 : return 0;
40 : }
41 :
42 211707 : _PUBLIC_ void socket_tevent_fd_close_fn(struct tevent_context *ev,
43 : struct tevent_fd *fde,
44 : int fd,
45 : void *private_data)
46 : {
47 : /* this might be the socket_wrapper swrap_close() */
48 211707 : close(fd);
49 211707 : }
50 :
51 115132 : _PUBLIC_ NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
52 : struct socket_context **new_sock,
53 : enum socket_type type, uint32_t flags)
54 : {
55 3128 : NTSTATUS status;
56 :
57 115132 : (*new_sock) = talloc(mem_ctx, struct socket_context);
58 115132 : if (!(*new_sock)) {
59 0 : return NT_STATUS_NO_MEMORY;
60 : }
61 :
62 115132 : (*new_sock)->type = type;
63 115132 : (*new_sock)->state = SOCKET_STATE_UNDEFINED;
64 115132 : (*new_sock)->flags = flags;
65 :
66 115132 : (*new_sock)->fd = -1;
67 :
68 115132 : (*new_sock)->private_data = NULL;
69 115132 : (*new_sock)->ops = ops;
70 115132 : (*new_sock)->backend_name = NULL;
71 :
72 115132 : status = (*new_sock)->ops->fn_init((*new_sock));
73 115132 : if (!NT_STATUS_IS_OK(status)) {
74 0 : talloc_free(*new_sock);
75 0 : return status;
76 : }
77 :
78 : /* by enabling "testnonblock" mode, all socket receive and
79 : send calls on non-blocking sockets will randomly recv/send
80 : less data than requested */
81 :
82 198596 : if (type == SOCKET_TYPE_STREAM &&
83 83464 : getenv("SOCKET_TESTNONBLOCK") != NULL) {
84 352 : (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
85 : }
86 :
87 : /* we don't do a connect() on dgram sockets, so need to set
88 : non-blocking at socket create time */
89 115132 : if (type == SOCKET_TYPE_DGRAM) {
90 31668 : set_blocking(socket_get_fd(*new_sock), false);
91 : }
92 :
93 115132 : talloc_set_destructor(*new_sock, socket_destructor);
94 :
95 115132 : return NT_STATUS_OK;
96 : }
97 :
98 115132 : _PUBLIC_ NTSTATUS socket_create(TALLOC_CTX *mem_ctx,
99 : const char *name, enum socket_type type,
100 : struct socket_context **new_sock, uint32_t flags)
101 : {
102 3128 : const struct socket_ops *ops;
103 :
104 115132 : ops = socket_getops_byname(name, type);
105 115132 : if (!ops) {
106 0 : return NT_STATUS_INVALID_PARAMETER;
107 : }
108 :
109 115132 : return socket_create_with_ops(mem_ctx, ops, new_sock, type, flags);
110 : }
111 :
112 102668 : _PUBLIC_ NTSTATUS socket_connect(struct socket_context *sock,
113 : const struct socket_address *my_address,
114 : const struct socket_address *server_address,
115 : uint32_t flags)
116 : {
117 102668 : if (sock == NULL) {
118 0 : return NT_STATUS_CONNECTION_DISCONNECTED;
119 : }
120 102668 : if (sock->state != SOCKET_STATE_UNDEFINED) {
121 0 : return NT_STATUS_INVALID_PARAMETER;
122 : }
123 :
124 102668 : if (!sock->ops->fn_connect) {
125 0 : return NT_STATUS_NOT_IMPLEMENTED;
126 : }
127 :
128 102668 : return sock->ops->fn_connect(sock, my_address, server_address, flags);
129 : }
130 :
131 451027 : _PUBLIC_ NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
132 : {
133 451027 : if (!sock->ops->fn_connect_complete) {
134 0 : return NT_STATUS_NOT_IMPLEMENTED;
135 : }
136 451027 : return sock->ops->fn_connect_complete(sock, flags);
137 : }
138 :
139 2889 : _PUBLIC_ NTSTATUS socket_listen(struct socket_context *sock,
140 : const struct socket_address *my_address,
141 : int queue_size, uint32_t flags)
142 : {
143 2889 : if (sock == NULL) {
144 0 : return NT_STATUS_CONNECTION_DISCONNECTED;
145 : }
146 2889 : if (sock->state != SOCKET_STATE_UNDEFINED) {
147 0 : return NT_STATUS_INVALID_PARAMETER;
148 : }
149 :
150 2889 : if (!sock->ops->fn_listen) {
151 0 : return NT_STATUS_NOT_IMPLEMENTED;
152 : }
153 :
154 2889 : return sock->ops->fn_listen(sock, my_address, queue_size, flags);
155 : }
156 :
157 157582 : _PUBLIC_ NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
158 : {
159 4554 : NTSTATUS status;
160 :
161 157582 : if (sock == NULL) {
162 0 : return NT_STATUS_CONNECTION_DISCONNECTED;
163 : }
164 157582 : if (sock->type != SOCKET_TYPE_STREAM) {
165 0 : return NT_STATUS_INVALID_PARAMETER;
166 : }
167 :
168 157582 : if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
169 0 : return NT_STATUS_INVALID_PARAMETER;
170 : }
171 :
172 157582 : if (!sock->ops->fn_accept) {
173 0 : return NT_STATUS_NOT_IMPLEMENTED;
174 : }
175 :
176 157582 : status = sock->ops->fn_accept(sock, new_sock);
177 :
178 157582 : if (NT_STATUS_IS_OK(status)) {
179 129087 : talloc_set_destructor(*new_sock, socket_destructor);
180 129087 : (*new_sock)->flags = 0;
181 : }
182 :
183 157582 : return status;
184 : }
185 :
186 1784800 : _PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf,
187 : size_t wantlen, size_t *nread)
188 : {
189 1784800 : if (sock == NULL) {
190 0 : return NT_STATUS_CONNECTION_DISCONNECTED;
191 : }
192 1784800 : if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
193 1712929 : sock->state != SOCKET_STATE_SERVER_CONNECTED &&
194 0 : sock->type != SOCKET_TYPE_DGRAM) {
195 0 : return NT_STATUS_INVALID_PARAMETER;
196 : }
197 :
198 1784800 : if (!sock->ops->fn_recv) {
199 0 : return NT_STATUS_NOT_IMPLEMENTED;
200 : }
201 :
202 1784800 : if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
203 829 : && wantlen > 1) {
204 :
205 787 : if (random() % 10 == 0) {
206 78 : *nread = 0;
207 78 : return STATUS_MORE_ENTRIES;
208 : }
209 709 : return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
210 : }
211 1784013 : return sock->ops->fn_recv(sock, buf, wantlen, nread);
212 : }
213 :
214 15171 : _PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
215 : size_t wantlen, size_t *nread,
216 : TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
217 : {
218 15171 : if (sock == NULL) {
219 0 : return NT_STATUS_CONNECTION_DISCONNECTED;
220 : }
221 15171 : if (sock->type != SOCKET_TYPE_DGRAM) {
222 0 : return NT_STATUS_INVALID_PARAMETER;
223 : }
224 :
225 15171 : if (!sock->ops->fn_recvfrom) {
226 0 : return NT_STATUS_NOT_IMPLEMENTED;
227 : }
228 :
229 15171 : return sock->ops->fn_recvfrom(sock, buf, wantlen, nread,
230 : mem_ctx, src_addr);
231 : }
232 :
233 9946661 : _PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
234 : const DATA_BLOB *blob, size_t *sendlen)
235 : {
236 9946661 : if (sock == NULL) {
237 0 : return NT_STATUS_CONNECTION_DISCONNECTED;
238 : }
239 9946661 : if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
240 910993 : sock->state != SOCKET_STATE_SERVER_CONNECTED) {
241 0 : return NT_STATUS_INVALID_PARAMETER;
242 : }
243 :
244 9946661 : if (!sock->ops->fn_send) {
245 0 : return NT_STATUS_NOT_IMPLEMENTED;
246 : }
247 :
248 9946661 : if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
249 596 : && blob->length > 1) {
250 553 : DATA_BLOB blob2 = *blob;
251 553 : if (random() % 10 == 0) {
252 60 : *sendlen = 0;
253 60 : return STATUS_MORE_ENTRIES;
254 : }
255 : /* The random size sends are incompatible with TLS and SASL
256 : * sockets, which require re-sends to be consistent */
257 493 : if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
258 493 : blob2.length = 1+(random() % blob2.length);
259 : } else {
260 : /* This is particularly stressful on buggy
261 : * LDAP clients, that don't expect on LDAP
262 : * packet in many SASL packets */
263 0 : blob2.length = 1 + blob2.length/2;
264 : }
265 493 : return sock->ops->fn_send(sock, &blob2, sendlen);
266 : }
267 9946108 : return sock->ops->fn_send(sock, blob, sendlen);
268 : }
269 :
270 :
271 14099 : _PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock,
272 : const DATA_BLOB *blob, size_t *sendlen,
273 : const struct socket_address *dest_addr)
274 : {
275 14099 : if (sock == NULL) {
276 0 : return NT_STATUS_CONNECTION_DISCONNECTED;
277 : }
278 14099 : if (sock->type != SOCKET_TYPE_DGRAM) {
279 0 : return NT_STATUS_INVALID_PARAMETER;
280 : }
281 :
282 14099 : if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
283 13979 : sock->state == SOCKET_STATE_SERVER_CONNECTED) {
284 0 : return NT_STATUS_INVALID_PARAMETER;
285 : }
286 :
287 14099 : if (!sock->ops->fn_sendto) {
288 0 : return NT_STATUS_NOT_IMPLEMENTED;
289 : }
290 :
291 14099 : return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
292 : }
293 :
294 :
295 : /*
296 : ask for the number of bytes in a pending incoming packet
297 : */
298 62960 : _PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
299 : {
300 62960 : if (sock == NULL) {
301 0 : return NT_STATUS_CONNECTION_DISCONNECTED;
302 : }
303 62960 : if (!sock->ops->fn_pending) {
304 0 : return NT_STATUS_NOT_IMPLEMENTED;
305 : }
306 62960 : return sock->ops->fn_pending(sock, npending);
307 : }
308 :
309 :
310 31329 : _PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
311 : {
312 31329 : if (sock == NULL) {
313 0 : return NT_STATUS_CONNECTION_DISCONNECTED;
314 : }
315 31329 : if (!sock->ops->fn_set_option) {
316 0 : return NT_STATUS_NOT_IMPLEMENTED;
317 : }
318 :
319 31329 : return sock->ops->fn_set_option(sock, option, val);
320 : }
321 :
322 0 : _PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
323 : {
324 0 : if (!sock->ops->fn_get_peer_name) {
325 0 : return NULL;
326 : }
327 :
328 0 : return sock->ops->fn_get_peer_name(sock, mem_ctx);
329 : }
330 :
331 147809 : _PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
332 : {
333 147809 : if (!sock->ops->fn_get_peer_addr) {
334 0 : return NULL;
335 : }
336 :
337 147809 : return sock->ops->fn_get_peer_addr(sock, mem_ctx);
338 : }
339 :
340 187676 : _PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
341 : {
342 187676 : if (!sock->ops->fn_get_my_addr) {
343 0 : return NULL;
344 : }
345 :
346 187676 : return sock->ops->fn_get_my_addr(sock, mem_ctx);
347 : }
348 :
349 289120 : _PUBLIC_ struct tsocket_address *socket_address_to_tsocket_address(TALLOC_CTX *mem_ctx,
350 : const struct socket_address *a)
351 : {
352 7048 : struct tsocket_address *r;
353 7048 : int ret;
354 :
355 289120 : if (!a) {
356 0 : return NULL;
357 : }
358 289120 : if (a->sockaddr) {
359 264430 : ret = tsocket_address_bsd_from_sockaddr(mem_ctx,
360 : a->sockaddr,
361 : a->sockaddrlen,
362 : &r);
363 : } else {
364 24690 : ret = tsocket_address_inet_from_strings(mem_ctx,
365 : a->family,
366 : a->addr,
367 : a->port,
368 : &r);
369 : }
370 :
371 289120 : if (ret != 0) {
372 0 : return NULL;
373 : }
374 :
375 289120 : return r;
376 : }
377 :
378 42428 : _PUBLIC_ void socket_address_set_port(struct socket_address *a,
379 : uint16_t port)
380 : {
381 42428 : if (a->sockaddr) {
382 0 : set_sockaddr_port(a->sockaddr, port);
383 : } else {
384 42428 : a->port = port;
385 : }
386 :
387 42428 : }
388 :
389 0 : _PUBLIC_ struct socket_address *tsocket_address_to_socket_address(TALLOC_CTX *mem_ctx,
390 : const struct tsocket_address *a)
391 : {
392 0 : ssize_t ret;
393 0 : struct sockaddr_storage ss;
394 0 : size_t sslen = sizeof(ss);
395 :
396 0 : ret = tsocket_address_bsd_sockaddr(a, (struct sockaddr *)(void *)&ss, sslen);
397 0 : if (ret < 0) {
398 0 : return NULL;
399 : }
400 :
401 0 : return socket_address_from_sockaddr(mem_ctx, (struct sockaddr *)(void *)&ss, ret);
402 : }
403 :
404 132215 : _PUBLIC_ struct tsocket_address *socket_get_remote_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
405 : {
406 3264 : struct socket_address *a;
407 3264 : struct tsocket_address *r;
408 :
409 132215 : a = socket_get_peer_addr(sock, mem_ctx);
410 132215 : if (a == NULL) {
411 0 : return NULL;
412 : }
413 :
414 132215 : r = socket_address_to_tsocket_address(mem_ctx, a);
415 132215 : talloc_free(a);
416 132215 : return r;
417 : }
418 :
419 132215 : _PUBLIC_ struct tsocket_address *socket_get_local_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
420 : {
421 3264 : struct socket_address *a;
422 3264 : struct tsocket_address *r;
423 :
424 132215 : a = socket_get_my_addr(sock, mem_ctx);
425 132215 : if (a == NULL) {
426 0 : return NULL;
427 : }
428 :
429 132215 : r = socket_address_to_tsocket_address(mem_ctx, a);
430 132215 : talloc_free(a);
431 132215 : return r;
432 : }
433 :
434 707076 : _PUBLIC_ int socket_get_fd(struct socket_context *sock)
435 : {
436 707076 : if (!sock->ops->fn_get_fd) {
437 0 : return -1;
438 : }
439 :
440 707076 : return sock->ops->fn_get_fd(sock);
441 : }
442 :
443 : /*
444 : call dup() on a socket, and close the old fd. This is used to change
445 : the fd to the lowest available number, to make select() more
446 : efficient (select speed depends on the maximum fd number passed to
447 : it)
448 : */
449 15593 : _PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
450 : {
451 0 : int fd;
452 15593 : if (sock->fd == -1) {
453 0 : return NT_STATUS_INVALID_HANDLE;
454 : }
455 15593 : fd = dup(sock->fd);
456 15593 : if (fd == -1) {
457 0 : return map_nt_error_from_unix_common(errno);
458 : }
459 15593 : close(sock->fd);
460 15593 : sock->fd = fd;
461 15593 : return NT_STATUS_OK;
462 :
463 : }
464 :
465 : /* Create a new socket_address. The type must match the socket type.
466 : * The host parameter may be an IP or a hostname
467 : */
468 :
469 130087 : _PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
470 : const char *family,
471 : const char *host,
472 : int port)
473 : {
474 130087 : struct socket_address *addr = talloc(mem_ctx, struct socket_address);
475 130087 : if (!addr) {
476 0 : return NULL;
477 : }
478 :
479 130087 : if (strcmp(family, "ip") == 0 && is_ipaddress_v6(host)) {
480 : /* leaving as "ip" would force IPv4 */
481 46507 : family = "ipv6";
482 : }
483 :
484 130087 : addr->family = family;
485 130087 : addr->addr = talloc_strdup(addr, host);
486 130087 : if (!addr->addr) {
487 0 : talloc_free(addr);
488 0 : return NULL;
489 : }
490 130087 : addr->port = port;
491 130087 : addr->sockaddr = NULL;
492 130087 : addr->sockaddrlen = 0;
493 :
494 130087 : return addr;
495 : }
496 :
497 : /* Create a new socket_address. Copy the struct sockaddr into the new
498 : * structure. Used for hooks in the kerberos libraries, where they
499 : * supply only a struct sockaddr */
500 :
501 47778 : _PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
502 : struct sockaddr *sockaddr,
503 : size_t sockaddrlen)
504 : {
505 47778 : struct socket_address *addr = talloc(mem_ctx, struct socket_address);
506 47778 : if (!addr) {
507 0 : return NULL;
508 : }
509 47778 : switch (sockaddr->sa_family) {
510 47778 : case AF_INET:
511 47778 : addr->family = "ipv4";
512 47778 : break;
513 : #ifdef HAVE_IPV6
514 0 : case AF_INET6:
515 0 : addr->family = "ipv6";
516 0 : break;
517 : #endif
518 0 : case AF_UNIX:
519 0 : addr->family = "unix";
520 0 : break;
521 : }
522 47778 : addr->addr = NULL;
523 47778 : addr->port = 0;
524 47778 : addr->sockaddr = (struct sockaddr *)talloc_memdup(addr, sockaddr, sockaddrlen);
525 47778 : if (!addr->sockaddr) {
526 0 : talloc_free(addr);
527 0 : return NULL;
528 : }
529 47778 : addr->sockaddrlen = sockaddrlen;
530 47778 : return addr;
531 : }
532 :
533 :
534 : /*
535 : Create a new socket_address from sockaddr_storage
536 : */
537 1279 : _PUBLIC_ struct socket_address *socket_address_from_sockaddr_storage(TALLOC_CTX *mem_ctx,
538 : const struct sockaddr_storage *sockaddr,
539 : uint16_t port)
540 : {
541 1279 : struct socket_address *addr = talloc_zero(mem_ctx, struct socket_address);
542 40 : char addr_str[INET6_ADDRSTRLEN+1];
543 40 : const char *str;
544 :
545 1279 : if (!addr) {
546 0 : return NULL;
547 : }
548 1279 : addr->port = port;
549 1279 : switch (sockaddr->ss_family) {
550 649 : case AF_INET:
551 649 : addr->family = "ipv4";
552 649 : break;
553 : #ifdef HAVE_IPV6
554 630 : case AF_INET6:
555 630 : addr->family = "ipv6";
556 630 : break;
557 : #endif
558 0 : default:
559 0 : talloc_free(addr);
560 0 : return NULL;
561 : }
562 :
563 1279 : str = print_sockaddr(addr_str, sizeof(addr_str), sockaddr);
564 1279 : if (str == NULL) {
565 0 : talloc_free(addr);
566 0 : return NULL;
567 : }
568 1279 : addr->addr = talloc_strdup(addr, str);
569 1279 : if (addr->addr == NULL) {
570 0 : talloc_free(addr);
571 0 : return NULL;
572 : }
573 :
574 1239 : return addr;
575 : }
576 :
577 : /* Copy a socket_address structure */
578 68470 : struct socket_address *socket_address_copy(TALLOC_CTX *mem_ctx,
579 : const struct socket_address *oaddr)
580 : {
581 68470 : struct socket_address *addr = talloc_zero(mem_ctx, struct socket_address);
582 68470 : if (!addr) {
583 0 : return NULL;
584 : }
585 68470 : addr->family = oaddr->family;
586 68470 : if (oaddr->addr) {
587 68470 : addr->addr = talloc_strdup(addr, oaddr->addr);
588 68470 : if (!addr->addr) {
589 0 : goto nomem;
590 : }
591 : }
592 68470 : addr->port = oaddr->port;
593 68470 : if (oaddr->sockaddr) {
594 1679 : addr->sockaddr = (struct sockaddr *)talloc_memdup(addr,
595 : oaddr->sockaddr,
596 : oaddr->sockaddrlen);
597 1679 : if (!addr->sockaddr) {
598 0 : goto nomem;
599 : }
600 1679 : addr->sockaddrlen = oaddr->sockaddrlen;
601 : }
602 :
603 67146 : return addr;
604 :
605 0 : nomem:
606 0 : talloc_free(addr);
607 0 : return NULL;
608 : }
609 :
610 115132 : _PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
611 : {
612 3128 : extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
613 3128 : extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
614 3128 : extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
615 :
616 115132 : if (strcmp("ip", family) == 0 ||
617 51918 : strcmp("ipv4", family) == 0) {
618 111693 : return socket_ipv4_ops(type);
619 : }
620 :
621 : #ifdef HAVE_IPV6
622 3439 : if (strcmp("ipv6", family) == 0) {
623 754 : return socket_ipv6_ops(type);
624 : }
625 : #endif
626 :
627 2685 : if (strcmp("unix", family) == 0) {
628 2685 : return socket_unixdom_ops(type);
629 : }
630 :
631 0 : return NULL;
632 : }
633 :
634 : /*
635 : set some flags on a socket
636 : */
637 137984 : void socket_set_flags(struct socket_context *sock, unsigned flags)
638 : {
639 137984 : sock->flags |= flags;
640 137984 : }
|