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 3 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, see <http://www.gnu.org/licenses/>.
23 #include "lib/socket/socket.h"
24 #include "system/filesys.h"
25 #include "system/network.h"
26 #include "param/param.h"
28 bool testnonblock = false;
31 auto-close sockets on free
33 static int socket_destructor(struct socket_context *sock)
35 if (sock->ops->fn_close &&
36 !(sock->flags & SOCKET_FLAG_NOCLOSE)) {
37 sock->ops->fn_close(sock);
42 _PUBLIC_ NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
43 struct socket_context **new_sock,
44 enum socket_type type, uint32_t flags)
48 (*new_sock) = talloc(mem_ctx, struct socket_context);
50 return NT_STATUS_NO_MEMORY;
53 (*new_sock)->type = type;
54 (*new_sock)->state = SOCKET_STATE_UNDEFINED;
55 (*new_sock)->flags = flags;
59 (*new_sock)->private_data = NULL;
60 (*new_sock)->ops = ops;
61 (*new_sock)->backend_name = NULL;
63 status = (*new_sock)->ops->fn_init((*new_sock));
64 if (!NT_STATUS_IS_OK(status)) {
65 talloc_free(*new_sock);
69 /* by enabling "testnonblock" mode, all socket receive and
70 send calls on non-blocking sockets will randomly recv/send
71 less data than requested */
73 if (!(flags & SOCKET_FLAG_BLOCK) &&
74 type == SOCKET_TYPE_STREAM &&
76 (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
79 /* we don't do a connect() on dgram sockets, so need to set
80 non-blocking at socket create time */
81 if (!(flags & SOCKET_FLAG_BLOCK) && type == SOCKET_TYPE_DGRAM) {
82 set_blocking(socket_get_fd(*new_sock), false);
85 talloc_set_destructor(*new_sock, socket_destructor);
90 _PUBLIC_ NTSTATUS socket_create(const char *name, enum socket_type type,
91 struct socket_context **new_sock, uint32_t flags)
93 const struct socket_ops *ops;
95 ops = socket_getops_byname(name, type);
97 return NT_STATUS_INVALID_PARAMETER;
100 return socket_create_with_ops(NULL, ops, new_sock, type, flags);
103 _PUBLIC_ NTSTATUS socket_connect(struct socket_context *sock,
104 const struct socket_address *my_address,
105 const struct socket_address *server_address,
109 return NT_STATUS_CONNECTION_DISCONNECTED;
111 if (sock->state != SOCKET_STATE_UNDEFINED) {
112 return NT_STATUS_INVALID_PARAMETER;
115 if (!sock->ops->fn_connect) {
116 return NT_STATUS_NOT_IMPLEMENTED;
119 return sock->ops->fn_connect(sock, my_address, server_address, flags);
122 _PUBLIC_ NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
124 if (!sock->ops->fn_connect_complete) {
125 return NT_STATUS_NOT_IMPLEMENTED;
127 return sock->ops->fn_connect_complete(sock, flags);
130 _PUBLIC_ NTSTATUS socket_listen(struct socket_context *sock,
131 const struct socket_address *my_address,
132 int queue_size, uint32_t flags)
135 return NT_STATUS_CONNECTION_DISCONNECTED;
137 if (sock->state != SOCKET_STATE_UNDEFINED) {
138 return NT_STATUS_INVALID_PARAMETER;
141 if (!sock->ops->fn_listen) {
142 return NT_STATUS_NOT_IMPLEMENTED;
145 return sock->ops->fn_listen(sock, my_address, queue_size, flags);
148 _PUBLIC_ NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
153 return NT_STATUS_CONNECTION_DISCONNECTED;
155 if (sock->type != SOCKET_TYPE_STREAM) {
156 return NT_STATUS_INVALID_PARAMETER;
159 if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
160 return NT_STATUS_INVALID_PARAMETER;
163 if (!sock->ops->fn_accept) {
164 return NT_STATUS_NOT_IMPLEMENTED;
167 status = sock->ops->fn_accept(sock, new_sock);
169 if (NT_STATUS_IS_OK(status)) {
170 talloc_set_destructor(*new_sock, socket_destructor);
171 (*new_sock)->flags = 0;
177 _PUBLIC_ NTSTATUS socket_recv(struct socket_context *sock, void *buf,
178 size_t wantlen, size_t *nread)
181 return NT_STATUS_CONNECTION_DISCONNECTED;
183 if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
184 sock->state != SOCKET_STATE_SERVER_CONNECTED &&
185 sock->type != SOCKET_TYPE_DGRAM) {
186 return NT_STATUS_INVALID_PARAMETER;
189 if (!sock->ops->fn_recv) {
190 return NT_STATUS_NOT_IMPLEMENTED;
193 if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
196 if (random() % 10 == 0) {
198 return STATUS_MORE_ENTRIES;
200 return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
202 return sock->ops->fn_recv(sock, buf, wantlen, nread);
205 _PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
206 size_t wantlen, size_t *nread,
207 TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
210 return NT_STATUS_CONNECTION_DISCONNECTED;
212 if (sock->type != SOCKET_TYPE_DGRAM) {
213 return NT_STATUS_INVALID_PARAMETER;
216 if (!sock->ops->fn_recvfrom) {
217 return NT_STATUS_NOT_IMPLEMENTED;
220 return sock->ops->fn_recvfrom(sock, buf, wantlen, nread,
224 _PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
225 const DATA_BLOB *blob, size_t *sendlen)
228 return NT_STATUS_CONNECTION_DISCONNECTED;
230 if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
231 sock->state != SOCKET_STATE_SERVER_CONNECTED) {
232 return NT_STATUS_INVALID_PARAMETER;
235 if (!sock->ops->fn_send) {
236 return NT_STATUS_NOT_IMPLEMENTED;
239 if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
240 && blob->length > 1) {
241 DATA_BLOB blob2 = *blob;
242 if (random() % 10 == 0) {
244 return STATUS_MORE_ENTRIES;
246 /* The random size sends are incompatible with TLS and SASL
247 * sockets, which require re-sends to be consistant */
248 if (!(sock->flags & SOCKET_FLAG_ENCRYPT)) {
249 blob2.length = 1+(random() % blob2.length);
251 /* This is particularly stressful on buggy
252 * LDAP clients, that don't expect on LDAP
253 * packet in many SASL packets */
254 blob2.length = 1 + blob2.length/2;
256 return sock->ops->fn_send(sock, &blob2, sendlen);
258 return sock->ops->fn_send(sock, blob, sendlen);
262 _PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock,
263 const DATA_BLOB *blob, size_t *sendlen,
264 const struct socket_address *dest_addr)
267 return NT_STATUS_CONNECTION_DISCONNECTED;
269 if (sock->type != SOCKET_TYPE_DGRAM) {
270 return NT_STATUS_INVALID_PARAMETER;
273 if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
274 sock->state == SOCKET_STATE_SERVER_CONNECTED) {
275 return NT_STATUS_INVALID_PARAMETER;
278 if (!sock->ops->fn_sendto) {
279 return NT_STATUS_NOT_IMPLEMENTED;
282 return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
287 ask for the number of bytes in a pending incoming packet
289 _PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
292 return NT_STATUS_CONNECTION_DISCONNECTED;
294 if (!sock->ops->fn_pending) {
295 return NT_STATUS_NOT_IMPLEMENTED;
297 return sock->ops->fn_pending(sock, npending);
301 _PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
304 return NT_STATUS_CONNECTION_DISCONNECTED;
306 if (!sock->ops->fn_set_option) {
307 return NT_STATUS_NOT_IMPLEMENTED;
310 return sock->ops->fn_set_option(sock, option, val);
313 _PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
315 if (!sock->ops->fn_get_peer_name) {
319 return sock->ops->fn_get_peer_name(sock, mem_ctx);
322 _PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
324 if (!sock->ops->fn_get_peer_addr) {
328 return sock->ops->fn_get_peer_addr(sock, mem_ctx);
331 _PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
333 if (!sock->ops->fn_get_my_addr) {
337 return sock->ops->fn_get_my_addr(sock, mem_ctx);
340 _PUBLIC_ int socket_get_fd(struct socket_context *sock)
342 if (!sock->ops->fn_get_fd) {
346 return sock->ops->fn_get_fd(sock);
350 call dup() on a socket, and close the old fd. This is used to change
351 the fd to the lowest available number, to make select() more
352 efficient (select speed depends on the maxiumum fd number passed to
355 _PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
358 if (sock->fd == -1) {
359 return NT_STATUS_INVALID_HANDLE;
363 return map_nt_error_from_unix(errno);
371 /* Create a new socket_address. The type must match the socket type.
372 * The host parameter may be an IP or a hostname
375 _PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
380 struct socket_address *addr = talloc(mem_ctx, struct socket_address);
385 addr->family = family;
386 addr->addr = talloc_strdup(addr, host);
392 addr->sockaddr = NULL;
393 addr->sockaddrlen = 0;
398 /* Create a new socket_address. Copy the struct sockaddr into the new
399 * structure. Used for hooks in the kerberos libraries, where they
400 * supply only a struct sockaddr */
402 _PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
403 struct sockaddr *sockaddr,
406 struct socket_address *addr = talloc(mem_ctx, struct socket_address);
413 addr->sockaddr = (struct sockaddr *)talloc_memdup(addr, sockaddr, sockaddrlen);
414 if (!addr->sockaddr) {
418 addr->sockaddrlen = sockaddrlen;
422 _PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
424 extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
425 extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
426 extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
428 if (strcmp("ip", family) == 0 ||
429 strcmp("ipv4", family) == 0) {
430 return socket_ipv4_ops(type);
434 if (strcmp("ipv6", family) == 0) {
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 = (const char **)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);
551 set some flags on a socket
553 void socket_set_flags(struct socket_context *sock, unsigned flags)
555 sock->flags |= flags;