source: branches/samba-3.5.x/source4/lib/socket/socket.c

Last change on this file was 414, checked in by Herwig Bauernfeind, 15 years ago

Samba 3.5.0: Initial import

File size: 14.8 KB
Line 
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
28/*
29 auto-close sockets on free
30*/
31static int socket_destructor(struct socket_context *sock)
32{
33 if (sock->ops->fn_close &&
34 !(sock->flags & SOCKET_FLAG_NOCLOSE)) {
35 sock->ops->fn_close(sock);
36 }
37 return 0;
38}
39
40_PUBLIC_ void socket_tevent_fd_close_fn(struct tevent_context *ev,
41 struct tevent_fd *fde,
42 int fd,
43 void *private_data)
44{
45 /* this might be the socket_wrapper swrap_close() */
46 close(fd);
47}
48
49_PUBLIC_ NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
50 struct socket_context **new_sock,
51 enum socket_type type, uint32_t flags)
52{
53 NTSTATUS status;
54
55 (*new_sock) = talloc(mem_ctx, struct socket_context);
56 if (!(*new_sock)) {
57 return NT_STATUS_NO_MEMORY;
58 }
59
60 (*new_sock)->type = type;
61 (*new_sock)->state = SOCKET_STATE_UNDEFINED;
62 (*new_sock)->flags = flags;
63
64 (*new_sock)->fd = -1;
65
66 (*new_sock)->private_data = NULL;
67 (*new_sock)->ops = ops;
68 (*new_sock)->backend_name = NULL;
69
70 status = (*new_sock)->ops->fn_init((*new_sock));
71 if (!NT_STATUS_IS_OK(status)) {
72 talloc_free(*new_sock);
73 return status;
74 }
75
76 /* by enabling "testnonblock" mode, all socket receive and
77 send calls on non-blocking sockets will randomly recv/send
78 less data than requested */
79
80 if (!(flags & SOCKET_FLAG_BLOCK) &&
81 type == SOCKET_TYPE_STREAM &&
82 getenv("SOCKET_TESTNONBLOCK") != NULL) {
83 (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
84 }
85
86 /* we don't do a connect() on dgram sockets, so need to set
87 non-blocking at socket create time */
88 if (!(flags & SOCKET_FLAG_BLOCK) && type == SOCKET_TYPE_DGRAM) {
89 set_blocking(socket_get_fd(*new_sock), false);
90 }
91
92 talloc_set_destructor(*new_sock, socket_destructor);
93
94 return NT_STATUS_OK;
95}
96
97_PUBLIC_ NTSTATUS socket_create(const char *name, enum socket_type type,
98 struct socket_context **new_sock, uint32_t flags)
99{
100 const struct socket_ops *ops;
101
102 ops = socket_getops_byname(name, type);
103 if (!ops) {
104 return NT_STATUS_INVALID_PARAMETER;
105 }
106
107 return socket_create_with_ops(NULL, ops, new_sock, type, flags);
108}
109
110_PUBLIC_ NTSTATUS socket_connect(struct socket_context *sock,
111 const struct socket_address *my_address,
112 const struct socket_address *server_address,
113 uint32_t flags)
114{
115 if (sock == NULL) {
116 return NT_STATUS_CONNECTION_DISCONNECTED;
117 }
118 if (sock->state != SOCKET_STATE_UNDEFINED) {
119 return NT_STATUS_INVALID_PARAMETER;
120 }
121
122 if (!sock->ops->fn_connect) {
123 return NT_STATUS_NOT_IMPLEMENTED;
124 }
125
126 return sock->ops->fn_connect(sock, my_address, server_address, flags);
127}
128
129_PUBLIC_ NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
130{
131 if (!sock->ops->fn_connect_complete) {
132 return NT_STATUS_NOT_IMPLEMENTED;
133 }
134 return sock->ops->fn_connect_complete(sock, flags);
135}
136
137_PUBLIC_ NTSTATUS socket_listen(struct socket_context *sock,
138 const struct socket_address *my_address,
139 int queue_size, uint32_t flags)
140{
141 if (sock == NULL) {
142 return NT_STATUS_CONNECTION_DISCONNECTED;
143 }
144 if (sock->state != SOCKET_STATE_UNDEFINED) {
145 return NT_STATUS_INVALID_PARAMETER;
146 }
147
148 if (!sock->ops->fn_listen) {
149 return NT_STATUS_NOT_IMPLEMENTED;
150 }
151
152 return sock->ops->fn_listen(sock, my_address, queue_size, flags);
153}
154
155_PUBLIC_ NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
156{
157 NTSTATUS status;
158
159 if (sock == NULL) {
160 return NT_STATUS_CONNECTION_DISCONNECTED;
161 }
162 if (sock->type != SOCKET_TYPE_STREAM) {
163 return NT_STATUS_INVALID_PARAMETER;
164 }
165
166 if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
167 return NT_STATUS_INVALID_PARAMETER;
168 }
169
170 if (!sock->ops->fn_accept) {
171 return NT_STATUS_NOT_IMPLEMENTED;
172 }
173
174 status = sock->ops->fn_accept(sock, new_sock);
175
176 if (NT_STATUS_IS_OK(status)) {
177 talloc_set_destructor(*new_sock, socket_destructor);
178 (*new_sock)->flags = 0;
179 }
180
181 return status;
182}
183
184_PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf,
185 size_t wantlen, size_t *nread)
186{
187 if (sock == NULL) {
188 return NT_STATUS_CONNECTION_DISCONNECTED;
189 }
190 if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
191 sock->state != SOCKET_STATE_SERVER_CONNECTED &&
192 sock->type != SOCKET_TYPE_DGRAM) {
193 return NT_STATUS_INVALID_PARAMETER;
194 }
195
196 if (!sock->ops->fn_recv) {
197 return NT_STATUS_NOT_IMPLEMENTED;
198 }
199
200 if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
201 && wantlen > 1) {
202
203 if (random() % 10 == 0) {
204 *nread = 0;
205 return STATUS_MORE_ENTRIES;
206 }
207 return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
208 }
209 return sock->ops->fn_recv(sock, buf, wantlen, nread);
210}
211
212_PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
213 size_t wantlen, size_t *nread,
214 TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
215{
216 if (sock == NULL) {
217 return NT_STATUS_CONNECTION_DISCONNECTED;
218 }
219 if (sock->type != SOCKET_TYPE_DGRAM) {
220 return NT_STATUS_INVALID_PARAMETER;
221 }
222
223 if (!sock->ops->fn_recvfrom) {
224 return NT_STATUS_NOT_IMPLEMENTED;
225 }
226
227 return sock->ops->fn_recvfrom(sock, buf, wantlen, nread,
228 mem_ctx, src_addr);
229}
230
231_PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
232 const DATA_BLOB *blob, size_t *sendlen)
233{
234 if (sock == NULL) {
235 return NT_STATUS_CONNECTION_DISCONNECTED;
236 }
237 if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
238 sock->state != SOCKET_STATE_SERVER_CONNECTED) {
239 return NT_STATUS_INVALID_PARAMETER;
240 }
241
242 if (!sock->ops->fn_send) {
243 return NT_STATUS_NOT_IMPLEMENTED;
244 }
245
246 if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
247 && blob->length > 1) {
248 DATA_BLOB blob2 = *blob;
249 if (random() % 10 == 0) {
250 *sendlen = 0;
251 return STATUS_MORE_ENTRIES;
252 }
253 /* The random size sends are incompatible with TLS and SASL
254 * sockets, which require re-sends to be consistant */
255 if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
256 blob2.length = 1+(random() % blob2.length);
257 } else {
258 /* This is particularly stressful on buggy
259 * LDAP clients, that don't expect on LDAP
260 * packet in many SASL packets */
261 blob2.length = 1 + blob2.length/2;
262 }
263 return sock->ops->fn_send(sock, &blob2, sendlen);
264 }
265 return sock->ops->fn_send(sock, blob, sendlen);
266}
267
268
269_PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock,
270 const DATA_BLOB *blob, size_t *sendlen,
271 const struct socket_address *dest_addr)
272{
273 if (sock == NULL) {
274 return NT_STATUS_CONNECTION_DISCONNECTED;
275 }
276 if (sock->type != SOCKET_TYPE_DGRAM) {
277 return NT_STATUS_INVALID_PARAMETER;
278 }
279
280 if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
281 sock->state == SOCKET_STATE_SERVER_CONNECTED) {
282 return NT_STATUS_INVALID_PARAMETER;
283 }
284
285 if (!sock->ops->fn_sendto) {
286 return NT_STATUS_NOT_IMPLEMENTED;
287 }
288
289 return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
290}
291
292
293/*
294 ask for the number of bytes in a pending incoming packet
295*/
296_PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
297{
298 if (sock == NULL) {
299 return NT_STATUS_CONNECTION_DISCONNECTED;
300 }
301 if (!sock->ops->fn_pending) {
302 return NT_STATUS_NOT_IMPLEMENTED;
303 }
304 return sock->ops->fn_pending(sock, npending);
305}
306
307
308_PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
309{
310 if (sock == NULL) {
311 return NT_STATUS_CONNECTION_DISCONNECTED;
312 }
313 if (!sock->ops->fn_set_option) {
314 return NT_STATUS_NOT_IMPLEMENTED;
315 }
316
317 return sock->ops->fn_set_option(sock, option, val);
318}
319
320_PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
321{
322 if (!sock->ops->fn_get_peer_name) {
323 return NULL;
324 }
325
326 return sock->ops->fn_get_peer_name(sock, mem_ctx);
327}
328
329_PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
330{
331 if (!sock->ops->fn_get_peer_addr) {
332 return NULL;
333 }
334
335 return sock->ops->fn_get_peer_addr(sock, mem_ctx);
336}
337
338_PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
339{
340 if (!sock->ops->fn_get_my_addr) {
341 return NULL;
342 }
343
344 return sock->ops->fn_get_my_addr(sock, mem_ctx);
345}
346
347_PUBLIC_ int socket_get_fd(struct socket_context *sock)
348{
349 if (!sock->ops->fn_get_fd) {
350 return -1;
351 }
352
353 return sock->ops->fn_get_fd(sock);
354}
355
356/*
357 call dup() on a socket, and close the old fd. This is used to change
358 the fd to the lowest available number, to make select() more
359 efficient (select speed depends on the maxiumum fd number passed to
360 it)
361*/
362_PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
363{
364 int fd;
365 if (sock->fd == -1) {
366 return NT_STATUS_INVALID_HANDLE;
367 }
368 fd = dup(sock->fd);
369 if (fd == -1) {
370 return map_nt_error_from_unix(errno);
371 }
372 close(sock->fd);
373 sock->fd = fd;
374 return NT_STATUS_OK;
375
376}
377
378/* Create a new socket_address. The type must match the socket type.
379 * The host parameter may be an IP or a hostname
380 */
381
382_PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
383 const char *family,
384 const char *host,
385 int port)
386{
387 struct socket_address *addr = talloc(mem_ctx, struct socket_address);
388 if (!addr) {
389 return NULL;
390 }
391
392 addr->family = family;
393 addr->addr = talloc_strdup(addr, host);
394 if (!addr->addr) {
395 talloc_free(addr);
396 return NULL;
397 }
398 addr->port = port;
399 addr->sockaddr = NULL;
400 addr->sockaddrlen = 0;
401
402 return addr;
403}
404
405/* Create a new socket_address. Copy the struct sockaddr into the new
406 * structure. Used for hooks in the kerberos libraries, where they
407 * supply only a struct sockaddr */
408
409_PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
410 struct sockaddr *sockaddr,
411 size_t sockaddrlen)
412{
413 struct socket_address *addr = talloc(mem_ctx, struct socket_address);
414 if (!addr) {
415 return NULL;
416 }
417 addr->family = NULL;
418 addr->addr = NULL;
419 addr->port = 0;
420 addr->sockaddr = (struct sockaddr *)talloc_memdup(addr, sockaddr, sockaddrlen);
421 if (!addr->sockaddr) {
422 talloc_free(addr);
423 return NULL;
424 }
425 addr->sockaddrlen = sockaddrlen;
426 return addr;
427}
428
429/* Copy a socket_address structure */
430struct socket_address *socket_address_copy(TALLOC_CTX *mem_ctx,
431 const struct socket_address *oaddr)
432{
433 struct socket_address *addr = talloc_zero(mem_ctx, struct socket_address);
434 if (!addr) {
435 return NULL;
436 }
437 addr->family = oaddr->family;
438 if (oaddr->addr) {
439 addr->addr = talloc_strdup(addr, oaddr->addr);
440 if (!addr->addr) {
441 goto nomem;
442 }
443 }
444 addr->port = oaddr->port;
445 if (oaddr->sockaddr) {
446 addr->sockaddr = (struct sockaddr *)talloc_memdup(addr,
447 oaddr->sockaddr,
448 oaddr->sockaddrlen);
449 if (!addr->sockaddr) {
450 goto nomem;
451 }
452 addr->sockaddrlen = oaddr->sockaddrlen;
453 }
454
455 return addr;
456
457nomem:
458 talloc_free(addr);
459 return NULL;
460}
461
462_PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
463{
464 extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
465 extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
466 extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
467
468 if (strcmp("ip", family) == 0 ||
469 strcmp("ipv4", family) == 0) {
470 return socket_ipv4_ops(type);
471 }
472
473#if HAVE_IPV6
474 if (strcmp("ipv6", family) == 0) {
475 return socket_ipv6_ops(type);
476 }
477#endif
478
479 if (strcmp("unix", family) == 0) {
480 return socket_unixdom_ops(type);
481 }
482
483 return NULL;
484}
485
486enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
487
488static const struct {
489 const char *name;
490 int level;
491 int option;
492 int value;
493 int opttype;
494} socket_options[] = {
495 {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
496 {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
497 {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
498#ifdef TCP_NODELAY
499 {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
500#endif
501#ifdef IPTOS_LOWDELAY
502 {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
503#endif
504#ifdef IPTOS_THROUGHPUT
505 {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
506#endif
507#ifdef SO_REUSEPORT
508 {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL},
509#endif
510#ifdef SO_SNDBUF
511 {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
512#endif
513#ifdef SO_RCVBUF
514 {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
515#endif
516#ifdef SO_SNDLOWAT
517 {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
518#endif
519#ifdef SO_RCVLOWAT
520 {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
521#endif
522#ifdef SO_SNDTIMEO
523 {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
524#endif
525#ifdef SO_RCVTIMEO
526 {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
527#endif
528 {NULL,0,0,0,0}};
529
530
531/**
532 Set user socket options.
533**/
534_PUBLIC_ void set_socket_options(int fd, const char *options)
535{
536 const char **options_list = (const char **)str_list_make(NULL, options, " \t,");
537 int j;
538
539 if (!options_list)
540 return;
541
542 for (j = 0; options_list[j]; j++) {
543 const char *tok = options_list[j];
544 int ret=0,i;
545 int value = 1;
546 char *p;
547 bool got_value = false;
548
549 if ((p = strchr(tok,'='))) {
550 *p = 0;
551 value = atoi(p+1);
552 got_value = true;
553 }
554
555 for (i=0;socket_options[i].name;i++)
556 if (strequal(socket_options[i].name,tok))
557 break;
558
559 if (!socket_options[i].name) {
560 DEBUG(0,("Unknown socket option %s\n",tok));
561 continue;
562 }
563
564 switch (socket_options[i].opttype) {
565 case OPT_BOOL:
566 case OPT_INT:
567 ret = setsockopt(fd,socket_options[i].level,
568 socket_options[i].option,(char *)&value,sizeof(int));
569 break;
570
571 case OPT_ON:
572 if (got_value)
573 DEBUG(0,("syntax error - %s does not take a value\n",tok));
574
575 {
576 int on = socket_options[i].value;
577 ret = setsockopt(fd,socket_options[i].level,
578 socket_options[i].option,(char *)&on,sizeof(int));
579 }
580 break;
581 }
582
583 if (ret != 0)
584 DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
585 }
586
587 talloc_free(options_list);
588}
589
590/*
591 set some flags on a socket
592 */
593void socket_set_flags(struct socket_context *sock, unsigned flags)
594{
595 sock->flags |= flags;
596}
Note: See TracBrowser for help on using the repository browser.