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 /* The returning of 0 and MORE_ENTRIES is incompatible
193 with TLS and SASL sockets, as there is not a
194 constant event source to re-trigger the reads */
196 if (!(sock->flags & SOCKET_FLAG_FAKE)) {
197 if (random() % 10 == 0) {
199 return STATUS_MORE_ENTRIES;
202 return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread);
204 return sock->ops->fn_recv(sock, buf, wantlen, nread);
207 _PUBLIC_ NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf,
208 size_t wantlen, size_t *nread,
209 TALLOC_CTX *mem_ctx, struct socket_address **src_addr)
212 return NT_STATUS_CONNECTION_DISCONNECTED;
214 if (sock->type != SOCKET_TYPE_DGRAM) {
215 return NT_STATUS_INVALID_PARAMETER;
218 if (!sock->ops->fn_recvfrom) {
219 return NT_STATUS_NOT_IMPLEMENTED;
222 return sock->ops->fn_recvfrom(sock, buf, wantlen, nread,
226 _PUBLIC_ NTSTATUS socket_send(struct socket_context *sock,
227 const DATA_BLOB *blob, size_t *sendlen)
230 return NT_STATUS_CONNECTION_DISCONNECTED;
232 if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
233 sock->state != SOCKET_STATE_SERVER_CONNECTED) {
234 return NT_STATUS_INVALID_PARAMETER;
237 if (!sock->ops->fn_send) {
238 return NT_STATUS_NOT_IMPLEMENTED;
241 if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK)
242 && blob->length > 1) {
243 if (random() % 10 == 0) {
245 return STATUS_MORE_ENTRIES;
247 /* The variable size sends are incompatilbe with TLS and SASL
248 * sockets, which require re-sends to be consistant */
249 if (!(sock->flags & SOCKET_FLAG_FAKE)) {
250 DATA_BLOB blob2 = *blob;
251 blob2.length = 1+(random() % blob2.length);
252 return sock->ops->fn_send(sock, &blob2, sendlen);
255 return sock->ops->fn_send(sock, blob, sendlen);
259 _PUBLIC_ NTSTATUS socket_sendto(struct socket_context *sock,
260 const DATA_BLOB *blob, size_t *sendlen,
261 const struct socket_address *dest_addr)
264 return NT_STATUS_CONNECTION_DISCONNECTED;
266 if (sock->type != SOCKET_TYPE_DGRAM) {
267 return NT_STATUS_INVALID_PARAMETER;
270 if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
271 sock->state == SOCKET_STATE_SERVER_CONNECTED) {
272 return NT_STATUS_INVALID_PARAMETER;
275 if (!sock->ops->fn_sendto) {
276 return NT_STATUS_NOT_IMPLEMENTED;
279 return sock->ops->fn_sendto(sock, blob, sendlen, dest_addr);
284 ask for the number of bytes in a pending incoming packet
286 _PUBLIC_ NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
289 return NT_STATUS_CONNECTION_DISCONNECTED;
291 if (!sock->ops->fn_pending) {
292 return NT_STATUS_NOT_IMPLEMENTED;
294 return sock->ops->fn_pending(sock, npending);
298 _PUBLIC_ NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
301 return NT_STATUS_CONNECTION_DISCONNECTED;
303 if (!sock->ops->fn_set_option) {
304 return NT_STATUS_NOT_IMPLEMENTED;
307 return sock->ops->fn_set_option(sock, option, val);
310 _PUBLIC_ char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
312 if (!sock->ops->fn_get_peer_name) {
316 return sock->ops->fn_get_peer_name(sock, mem_ctx);
319 _PUBLIC_ struct socket_address *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
321 if (!sock->ops->fn_get_peer_addr) {
325 return sock->ops->fn_get_peer_addr(sock, mem_ctx);
328 _PUBLIC_ struct socket_address *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
330 if (!sock->ops->fn_get_my_addr) {
334 return sock->ops->fn_get_my_addr(sock, mem_ctx);
337 _PUBLIC_ int socket_get_fd(struct socket_context *sock)
339 if (!sock->ops->fn_get_fd) {
343 return sock->ops->fn_get_fd(sock);
347 call dup() on a socket, and close the old fd. This is used to change
348 the fd to the lowest available number, to make select() more
349 efficient (select speed depends on the maxiumum fd number passed to
352 _PUBLIC_ NTSTATUS socket_dup(struct socket_context *sock)
355 if (sock->fd == -1) {
356 return NT_STATUS_INVALID_HANDLE;
360 return map_nt_error_from_unix(errno);
368 /* Create a new socket_address. The type must match the socket type.
369 * The host parameter may be an IP or a hostname
372 _PUBLIC_ struct socket_address *socket_address_from_strings(TALLOC_CTX *mem_ctx,
377 struct socket_address *addr = talloc(mem_ctx, struct socket_address);
382 addr->family = family;
383 addr->addr = talloc_strdup(addr, host);
389 addr->sockaddr = NULL;
390 addr->sockaddrlen = 0;
395 /* Create a new socket_address. Copy the struct sockaddr into the new
396 * structure. Used for hooks in the kerberos libraries, where they
397 * supply only a struct sockaddr */
399 _PUBLIC_ struct socket_address *socket_address_from_sockaddr(TALLOC_CTX *mem_ctx,
400 struct sockaddr *sockaddr,
403 struct socket_address *addr = talloc(mem_ctx, struct socket_address);
410 addr->sockaddr = talloc_memdup(addr, sockaddr, sockaddrlen);
411 if (!addr->sockaddr) {
415 addr->sockaddrlen = sockaddrlen;
419 _PUBLIC_ const struct socket_ops *socket_getops_byname(const char *family, enum socket_type type)
421 extern const struct socket_ops *socket_ipv4_ops(enum socket_type);
422 extern const struct socket_ops *socket_ipv6_ops(enum socket_type);
423 extern const struct socket_ops *socket_unixdom_ops(enum socket_type);
425 if (strcmp("ip", family) == 0 ||
426 strcmp("ipv4", family) == 0) {
427 return socket_ipv4_ops(type);
431 if (strcmp("ipv6", family) == 0) {
432 if (lp_parm_bool(-1, "socket", "noipv6", False)) {
433 DEBUG(3, ("IPv6 support was disabled in smb.conf"));
436 return socket_ipv6_ops(type);
440 if (strcmp("unix", family) == 0) {
441 return socket_unixdom_ops(type);
447 enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON};
449 static const struct {
455 } socket_options[] = {
456 {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL},
457 {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL},
458 {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL},
460 {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL},
462 #ifdef IPTOS_LOWDELAY
463 {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON},
465 #ifdef IPTOS_THROUGHPUT
466 {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON},
469 {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL},
472 {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT},
475 {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT},
478 {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT},
481 {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT},
484 {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT},
487 {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT},
493 Set user socket options.
495 _PUBLIC_ void set_socket_options(int fd, const char *options)
497 const char **options_list = str_list_make(NULL, options, " \t,");
503 for (j = 0; options_list[j]; j++) {
504 const char *tok = options_list[j];
508 BOOL got_value = False;
510 if ((p = strchr(tok,'='))) {
516 for (i=0;socket_options[i].name;i++)
517 if (strequal(socket_options[i].name,tok))
520 if (!socket_options[i].name) {
521 DEBUG(0,("Unknown socket option %s\n",tok));
525 switch (socket_options[i].opttype) {
528 ret = setsockopt(fd,socket_options[i].level,
529 socket_options[i].option,(char *)&value,sizeof(int));
534 DEBUG(0,("syntax error - %s does not take a value\n",tok));
537 int on = socket_options[i].value;
538 ret = setsockopt(fd,socket_options[i].level,
539 socket_options[i].option,(char *)&on,sizeof(int));
545 DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) ));
548 talloc_free(options_list);