2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) Tim Potter 2000-2001
6 Copyright (C) Stefan Metzmacher 2004
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 2 of the License, or
11 (at your option) any later version.
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.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "lib/socket/socket.h"
25 #include "system/filesys.h"
26 #include "system/network.h"
29 auto-close sockets on free
31 static int socket_destructor(struct socket_context *sock)
33 if (sock->ops->fn_close) {
34 sock->ops->fn_close(sock);
39 _PUBLIC_ NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
40 struct socket_context **new_sock,
41 enum socket_type type, uint32_t flags)
45 (*new_sock) = talloc(mem_ctx, struct socket_context);
47 return NT_STATUS_NO_MEMORY;
50 (*new_sock)->type = type;
51 (*new_sock)->state = SOCKET_STATE_UNDEFINED;
52 (*new_sock)->flags = flags;
56 (*new_sock)->private_data = NULL;
57 (*new_sock)->ops = ops;
58 (*new_sock)->backend_name = NULL;
60 status = (*new_sock)->ops->fn_init((*new_sock));
61 if (!NT_STATUS_IS_OK(status)) {
62 talloc_free(*new_sock);
66 /* by enabling "testnonblock" mode, all socket receive and
67 send calls on non-blocking sockets will randomly recv/send
68 less data than requested */
70 if (!(flags & SOCKET_FLAG_BLOCK) &&
71 type == SOCKET_TYPE_STREAM &&
72 lp_parm_bool(-1, "socket", "testnonblock", False)) {
73 (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
76 /* we don't do a connect() on dgram sockets, so need to set
77 non-blocking at socket create time */
78 if (!(flags & SOCKET_FLAG_BLOCK) && type == SOCKET_TYPE_DGRAM) {
79 set_blocking(socket_get_fd(*new_sock), False);
82 talloc_set_destructor(*new_sock, socket_destructor);
87 _PUBLIC_ NTSTATUS socket_create(const char *name, enum socket_type type,
88 struct socket_context **new_sock, uint32_t flags)
90 const struct socket_ops *ops;
92 ops = socket_getops_byname(name, type);
94 return NT_STATUS_INVALID_PARAMETER;
97 return socket_create_with_ops(NULL, ops, new_sock, type, flags);
100 _PUBLIC_ NTSTATUS socket_connect(struct socket_context *sock,
101 const struct socket_address *my_address,
102 const struct socket_address *server_address,
106 return NT_STATUS_CONNECTION_DISCONNECTED;
108 if (sock->state != SOCKET_STATE_UNDEFINED) {
109 return NT_STATUS_INVALID_PARAMETER;
112 if (!sock->ops->fn_connect) {
113 return NT_STATUS_NOT_IMPLEMENTED;
116 return sock->ops->fn_connect(sock, my_address, server_address, flags);
119 _PUBLIC_ NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
121 if (!sock->ops->fn_connect_complete) {
122 return NT_STATUS_NOT_IMPLEMENTED;
124 return sock->ops->fn_connect_complete(sock, flags);
127 _PUBLIC_ NTSTATUS socket_listen(struct socket_context *sock,
128 const struct socket_address *my_address,
129 int queue_size, uint32_t flags)
132 return NT_STATUS_CONNECTION_DISCONNECTED;
134 if (sock->state != SOCKET_STATE_UNDEFINED) {
135 return NT_STATUS_INVALID_PARAMETER;
138 if (!sock->ops->fn_listen) {
139 return NT_STATUS_NOT_IMPLEMENTED;
142 return sock->ops->fn_listen(sock, my_address, queue_size, flags);
145 _PUBLIC_ NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
150 return NT_STATUS_CONNECTION_DISCONNECTED;
152 if (sock->type != SOCKET_TYPE_STREAM) {
153 return NT_STATUS_INVALID_PARAMETER;
156 if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
157 return NT_STATUS_INVALID_PARAMETER;
160 if (!sock->ops->fn_accept) {
161 return NT_STATUS_NOT_IMPLEMENTED;
164 status = sock->ops->fn_accept(sock, new_sock);
166 if (NT_STATUS_IS_OK(status)) {
167 talloc_set_destructor(*new_sock, socket_destructor);
173 _PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf,
174 size_t wantlen, size_t *nread)
177 return NT_STATUS_CONNECTION_DISCONNECTED;
179 if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
180 sock->state != SOCKET_STATE_SERVER_CONNECTED &&
181 sock->type != SOCKET_TYPE_DGRAM) {
182 return NT_STATUS_INVALID_PARAMETER;
185 if (!sock->ops->fn_recv) {
186 return NT_STATUS_NOT_IMPLEMENTED;
189 if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
192 if (random() % 10 == 0) {
194 return STATUS_MORE_ENTRIES;
196 return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
198 return sock->ops->fn_recv(sock, buf, wantlen, nread);
201 _PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
202 size_t wantlen, size_t *nread,
203 TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
206 return NT_STATUS_CONNECTION_DISCONNECTED;
208 if (sock->type != SOCKET_TYPE_DGRAM) {
209 return NT_STATUS_INVALID_PARAMETER;
212 if (!sock->ops->fn_recvfrom) {
213 return NT_STATUS_NOT_IMPLEMENTED;
216 return sock->ops->fn_recvfrom(sock, buf, wantlen, nread,
220 _PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
221 const DATA_BLOB *blob, size_t *sendlen)
224 return NT_STATUS_CONNECTION_DISCONNECTED;
226 if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
227 sock->state != SOCKET_STATE_SERVER_CONNECTED) {
228 return NT_STATUS_INVALID_PARAMETER;
231 if (!sock->ops->fn_send) {
232 return NT_STATUS_NOT_IMPLEMENTED;
235 if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
236 && blob->length > 1) {
237 DATA_BLOB blob2 = *blob;
238 if (random() % 10 == 0) {
240 return STATUS_MORE_ENTRIES;
242 /* The random size sends are incompatible with TLS and SASL
243 * sockets, which require re-sends to be consistant */
244 if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
245 blob2.length = 1+(random() % blob2.length);
247 /* This is particularly stressful on buggy
248 * LDAP clients, that don't expect on LDAP
249 * packet in many SASL packets */
250 blob2.length = 1 + blob2.length/2;
252 return sock->ops->fn_send(sock, &blob2, sendlen);
254 return sock->ops->fn_send(sock, blob, sendlen);
258 _PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock,
259 const DATA_BLOB *blob, size_t *sendlen,
260 const struct socket_address *dest_addr)
263 return NT_STATUS_CONNECTION_DISCONNECTED;
265 if (sock->type != SOCKET_TYPE_DGRAM) {
266 return NT_STATUS_INVALID_PARAMETER;
269 if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
270 sock->state == SOCKET_STATE_SERVER_CONNECTED) {
271 return NT_STATUS_INVALID_PARAMETER;
274 if (!sock->ops->fn_sendto) {
275 return NT_STATUS_NOT_IMPLEMENTED;
278 return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
283 ask for the number of bytes in a pending incoming packet
285 _PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
288 return NT_STATUS_CONNECTION_DISCONNECTED;
290 if (!sock->ops->fn_pending) {
291 return NT_STATUS_NOT_IMPLEMENTED;
293 return sock->ops->fn_pending(sock, npending);
297 _PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
300 return NT_STATUS_CONNECTION_DISCONNECTED;
302 if (!sock->ops->fn_set_option) {
303 return NT_STATUS_NOT_IMPLEMENTED;
306 return sock->ops->fn_set_option(sock, option, val);
309 _PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
311 if (!sock->ops->fn_get_peer_name) {
315 return sock->ops->fn_get_peer_name(sock, mem_ctx);
318 _PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
320 if (!sock->ops->fn_get_peer_addr) {
324 return sock->ops->fn_get_peer_addr(sock, mem_ctx);
327 _PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
329 if (!sock->ops->fn_get_my_addr) {
333 return sock->ops->fn_get_my_addr(sock, mem_ctx);
336 _PUBLIC_ int socket_get_fd(struct socket_context *sock)
338 if (!sock->ops->fn_get_fd) {
342 return sock->ops->fn_get_fd(sock);
346 call dup() on a socket, and close the old fd. This is used to change
347 the fd to the lowest available number, to make select() more
348 efficient (select speed depends on the maxiumum fd number passed to
351 _PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
354 if (sock->fd == -1) {
355 return NT_STATUS_INVALID_HANDLE;
359 return map_nt_error_from_unix(errno);
367 /* Create a new socket_address. The type must match the socket type.
368 * The host parameter may be an IP or a hostname
371 _PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
376 struct socket_address *addr = talloc(mem_ctx, struct socket_address);
381 addr->family = family;
382 addr->addr = talloc_strdup(addr, host);
388 addr->sockaddr = NULL;
389 addr->sockaddrlen = 0;
394 /* Create a new socket_address. Copy the struct sockaddr into the new
395 * structure. Used for hooks in the kerberos libraries, where they
396 * supply only a struct sockaddr */
398 _PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
399 struct sockaddr *sockaddr,
402 struct socket_address *addr = talloc(mem_ctx, struct socket_address);
409 addr->sockaddr = talloc_memdup(addr, sockaddr, sockaddrlen);
410 if (!addr->sockaddr) {
414 addr->sockaddrlen = sockaddrlen;
418 _PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
420 extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
421 extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
422 extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
424 if (strcmp("ip", family) == 0 ||
425 strcmp("ipv4", family) == 0) {
426 return socket_ipv4_ops(type);
430 if (strcmp("ipv6", family) == 0) {
431 if (lp_parm_bool(-1, "socket", "noipv6", False)) {
432 DEBUG(3, ("IPv6 support was disabled in smb.conf"));
435 return socket_ipv6_ops(type);
439 if (strcmp("unix", family) == 0) {
440 return socket_unixdom_ops(type);
446 enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
448 static const struct {
454 } socket_options[] = {
455 {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
456 {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
457 {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
459 {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
461 #ifdef IPTOS_LOWDELAY
462 {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
464 #ifdef IPTOS_THROUGHPUT
465 {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
468 {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL},
471 {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
474 {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
477 {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
480 {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
483 {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
486 {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
492 Set user socket options.
494 _PUBLIC_ void set_socket_options(int fd, const char *options)
496 const char **options_list = str_list_make(NULL, options, " \t,");
502 for (j = 0; options_list[j]; j++) {
503 const char *tok = options_list[j];
507 BOOL got_value = False;
509 if ((p = strchr(tok,'='))) {
515 for (i=0;socket_options[i].name;i++)
516 if (strequal(socket_options[i].name,tok))
519 if (!socket_options[i].name) {
520 DEBUG(0,("Unknown socket option %s\n",tok));
524 switch (socket_options[i].opttype) {
527 ret = setsockopt(fd,socket_options[i].level,
528 socket_options[i].option,(char *)&value,sizeof(int));
533 DEBUG(0,("syntax error - %s does not take a value\n",tok));
536 int on = socket_options[i].value;
537 ret = setsockopt(fd,socket_options[i].level,
538 socket_options[i].option,(char *)&on,sizeof(int));
544 DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
547 talloc_free(options_list);