r3356: in the standard process model we need to make sure we close all
[samba.git] / source4 / lib / socket / socket.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Socket functions
4    Copyright (C) Stefan Metzmacher 2004
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 /*
24   auto-close sockets on free
25 */
26 static int socket_destructor(void *ptr)
27 {
28         struct socket_context *sock = ptr;
29         if (sock->ops->close) {
30                 sock->ops->close(sock);
31         }
32         return 0;
33 }
34
35 NTSTATUS socket_create(const char *name, enum socket_type type, struct socket_context **new_sock, uint32_t flags)
36 {
37         NTSTATUS status;
38
39         (*new_sock) = talloc_p(NULL, struct socket_context);
40         if (!(*new_sock)) {
41                 return NT_STATUS_NO_MEMORY;
42         }
43
44         (*new_sock)->type = type;
45         (*new_sock)->state = SOCKET_STATE_UNDEFINED;
46         (*new_sock)->flags = flags;
47
48         (*new_sock)->fd = -1;
49
50         (*new_sock)->private_data = NULL;
51         (*new_sock)->ops = socket_getops_byname(name, type);
52         if (!(*new_sock)->ops) {
53                 talloc_free(*new_sock);
54                 return NT_STATUS_INVALID_PARAMETER;
55         }
56
57         status = (*new_sock)->ops->init((*new_sock));
58         if (!NT_STATUS_IS_OK(status)) {
59                 talloc_free(*new_sock);
60                 return status;
61         }
62
63         /* by enabling "testnonblock" mode, all socket receive and
64            send calls on non-blocking sockets will randomly recv/send
65            less data than requested */
66         if (!(flags & SOCKET_FLAG_BLOCK) &&
67             lp_parm_bool(-1, "socket", "testnonblock", False)) {
68                 (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
69         }
70
71         talloc_set_destructor(*new_sock, socket_destructor);
72
73         return NT_STATUS_OK;
74 }
75
76 void socket_destroy(struct socket_context *sock)
77 {
78         /* the close is handled by the destructor */
79         talloc_free(sock);
80 }
81
82 NTSTATUS socket_connect(struct socket_context *sock,
83                         const char *my_address, int my_port,
84                         const char *server_address, int server_port,
85                         uint32_t flags)
86 {
87         if (sock->type != SOCKET_TYPE_STREAM) {
88                 return NT_STATUS_INVALID_PARAMETER;
89         }
90
91         if (sock->state != SOCKET_STATE_UNDEFINED) {
92                 return NT_STATUS_INVALID_PARAMETER;
93         }
94
95         if (!sock->ops->connect) {
96                 return NT_STATUS_NOT_IMPLEMENTED;
97         }
98
99         return sock->ops->connect(sock, my_address, my_port, server_address, server_port, flags);
100 }
101
102 NTSTATUS socket_listen(struct socket_context *sock, const char *my_address, int port, int queue_size, uint32_t flags)
103 {
104         if (sock->type != SOCKET_TYPE_STREAM) {
105                 return NT_STATUS_INVALID_PARAMETER;
106         }
107
108         if (sock->state != SOCKET_STATE_UNDEFINED) {
109                 return NT_STATUS_INVALID_PARAMETER;
110         }
111
112         if (!sock->ops->listen) {
113                 return NT_STATUS_NOT_IMPLEMENTED;
114         }
115
116         return sock->ops->listen(sock, my_address, port, queue_size, flags);
117 }
118
119 NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
120 {
121         NTSTATUS status;
122
123         if (sock->type != SOCKET_TYPE_STREAM) {
124                 return NT_STATUS_INVALID_PARAMETER;
125         }
126
127         if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
128                 return NT_STATUS_INVALID_PARAMETER;
129         }
130
131         if (!sock->ops->accept) {
132                 return NT_STATUS_NOT_IMPLEMENTED;
133         }
134
135         status = sock->ops->accept(sock, new_sock);
136
137         if (NT_STATUS_IS_OK(status)) {
138                 talloc_set_destructor(*new_sock, socket_destructor);
139         }
140
141         return status;
142 }
143
144 NTSTATUS socket_recv(struct socket_context *sock, void *buf, 
145                      size_t wantlen, size_t *nread, uint32_t flags)
146 {
147         if (sock->type != SOCKET_TYPE_STREAM) {
148                 return NT_STATUS_INVALID_PARAMETER;
149         }
150
151         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
152             sock->state != SOCKET_STATE_SERVER_CONNECTED) {
153                 return NT_STATUS_INVALID_PARAMETER;
154         }
155
156         if (!sock->ops->recv) {
157                 return NT_STATUS_NOT_IMPLEMENTED;
158         }
159
160         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) && wantlen > 1) {
161                 if (random() % 10 == 0) {
162                         *nread = 0;
163                         return STATUS_MORE_ENTRIES;
164                 }
165                 return sock->ops->recv(sock, buf, 1+(random() % wantlen), nread, flags);
166         }
167
168         return sock->ops->recv(sock, buf, wantlen, nread, flags);
169 }
170
171 NTSTATUS socket_send(struct socket_context *sock, 
172                      const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
173 {
174         if (sock->type != SOCKET_TYPE_STREAM) {
175                 return NT_STATUS_INVALID_PARAMETER;
176         }
177
178         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
179             sock->state != SOCKET_STATE_SERVER_CONNECTED) {
180                 return NT_STATUS_INVALID_PARAMETER;
181         }
182
183         if (!sock->ops->send) {
184                 return NT_STATUS_NOT_IMPLEMENTED;
185         }
186
187         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) && blob->length > 1) {
188                 DATA_BLOB blob2 = *blob;
189                 if (random() % 10 == 0) {
190                         *sendlen = 0;
191                         return STATUS_MORE_ENTRIES;
192                 }
193                 blob2.length = 1+(random() % blob2.length);
194                 return sock->ops->send(sock, &blob2, sendlen, flags);
195         }
196
197         return sock->ops->send(sock, blob, sendlen, flags);
198 }
199
200 NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
201 {
202         if (!sock->ops->set_option) {
203                 return NT_STATUS_NOT_IMPLEMENTED;
204         }
205
206         return sock->ops->set_option(sock, option, val);
207 }
208
209 char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
210 {
211         if (!sock->ops->get_peer_name) {
212                 return NULL;
213         }
214
215         return sock->ops->get_peer_name(sock, mem_ctx);
216 }
217
218 char *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
219 {
220         if (!sock->ops->get_peer_addr) {
221                 return NULL;
222         }
223
224         return sock->ops->get_peer_addr(sock, mem_ctx);
225 }
226
227 int socket_get_peer_port(struct socket_context *sock)
228 {
229         if (!sock->ops->get_peer_port) {
230                 return -1;
231         }
232
233         return sock->ops->get_peer_port(sock);
234 }
235
236 char *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
237 {
238         if (!sock->ops->get_my_addr) {
239                 return NULL;
240         }
241
242         return sock->ops->get_my_addr(sock, mem_ctx);
243 }
244
245 int socket_get_my_port(struct socket_context *sock)
246 {
247         if (!sock->ops->get_my_port) {
248                 return -1;
249         }
250
251         return sock->ops->get_my_port(sock);
252 }
253
254 int socket_get_fd(struct socket_context *sock)
255 {
256         if (!sock->ops->get_fd) {
257                 return -1;
258         }
259
260         return sock->ops->get_fd(sock);
261 }
262
263 /*
264   call dup() on a socket, and close the old fd. This is used to change
265   the fd to the lowest available number, to make select() more
266   efficient (select speed depends on the maxiumum fd number passed to
267   it)
268 */
269 NTSTATUS socket_dup(struct socket_context *sock)
270 {
271         int fd;
272         if (sock->fd == -1) {
273                 return NT_STATUS_INVALID_HANDLE;
274         }
275         fd = dup(sock->fd);
276         if (fd == -1) {
277                 return map_nt_error_from_unix(errno);
278         }
279         close(sock->fd);
280         sock->fd = fd;
281         return NT_STATUS_OK;
282         
283 }
284
285 const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type)
286 {
287         if (strcmp("ip", name) == 0 || 
288             strcmp("ipv4", name) == 0) {
289                 return socket_ipv4_ops();
290         }
291
292 #if HAVE_SOCKET_IPV6
293         if (strcmp("ipv6", name) == 0) {
294                 if (lp_parm_bool(-1, "socket", "noipv6", False)) {
295                         DEBUG(3, ("IPv6 support was disabled in smb.conf"));
296                         return NULL;
297                 }
298                 return socket_ipv6_ops();
299         }
300 #endif
301
302         if (strcmp("unix", name) == 0) {
303                 return socket_unixdom_ops();
304         }
305
306         return NULL;
307 }