db249522a2960a62d19fe63800b1a85c7330c917
[ira/wip.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 #include "lib/socket/socket.h"
23 #include "system/filesys.h"
24
25 /*
26   auto-close sockets on free
27 */
28 static int socket_destructor(void *ptr)
29 {
30         struct socket_context *sock = ptr;
31         if (sock->ops->fn_close) {
32                 sock->ops->fn_close(sock);
33         }
34         return 0;
35 }
36
37 static NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
38                                        struct socket_context **new_sock, 
39                                        enum socket_type type, uint32_t flags)
40 {
41         NTSTATUS status;
42
43         (*new_sock) = talloc(mem_ctx, struct socket_context);
44         if (!(*new_sock)) {
45                 return NT_STATUS_NO_MEMORY;
46         }
47
48         (*new_sock)->type = type;
49         (*new_sock)->state = SOCKET_STATE_UNDEFINED;
50         (*new_sock)->flags = flags;
51
52         (*new_sock)->fd = -1;
53
54         (*new_sock)->private_data = NULL;
55         (*new_sock)->ops = ops;
56
57         status = (*new_sock)->ops->fn_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             type == SOCKET_TYPE_STREAM &&
68             lp_parm_bool(-1, "socket", "testnonblock", False)) {
69                 (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
70         }
71
72         /* we don't do a connect() on dgram sockets, so need to set
73            non-blocking at socket create time */
74         if (!(flags & SOCKET_FLAG_BLOCK) && type == SOCKET_TYPE_DGRAM) {
75                 set_blocking(socket_get_fd(*new_sock), False);
76         }
77
78         talloc_set_destructor(*new_sock, socket_destructor);
79
80         return NT_STATUS_OK;
81 }
82
83 NTSTATUS socket_create(const char *name, enum socket_type type, 
84                        struct socket_context **new_sock, uint32_t flags)
85 {
86         const struct socket_ops *ops;
87
88         ops = socket_getops_byname(name, type);
89         if (!ops) {
90                 return NT_STATUS_INVALID_PARAMETER;
91         }
92
93         return socket_create_with_ops(NULL, ops, new_sock, type, flags);
94 }
95
96 NTSTATUS socket_connect(struct socket_context *sock,
97                         const char *my_address, int my_port,
98                         const char *server_address, int server_port,
99                         uint32_t flags)
100 {
101         if (sock == NULL) {
102                 return NT_STATUS_CONNECTION_DISCONNECTED;
103         }
104         if (sock->state != SOCKET_STATE_UNDEFINED) {
105                 return NT_STATUS_INVALID_PARAMETER;
106         }
107
108         if (!sock->ops->fn_connect) {
109                 return NT_STATUS_NOT_IMPLEMENTED;
110         }
111
112         return sock->ops->fn_connect(sock, my_address, my_port, server_address, server_port, flags);
113 }
114
115 NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
116 {
117         if (!sock->ops->fn_connect_complete) {
118                 return NT_STATUS_NOT_IMPLEMENTED;
119         }
120         return sock->ops->fn_connect_complete(sock, flags);
121 }
122
123 NTSTATUS socket_listen(struct socket_context *sock, const char *my_address, int port, int queue_size, uint32_t flags)
124 {
125         if (sock == NULL) {
126                 return NT_STATUS_CONNECTION_DISCONNECTED;
127         }
128         if (sock->state != SOCKET_STATE_UNDEFINED) {
129                 return NT_STATUS_INVALID_PARAMETER;
130         }
131
132         if (!sock->ops->fn_listen) {
133                 return NT_STATUS_NOT_IMPLEMENTED;
134         }
135
136         return sock->ops->fn_listen(sock, my_address, port, queue_size, flags);
137 }
138
139 NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
140 {
141         NTSTATUS status;
142
143         if (sock == NULL) {
144                 return NT_STATUS_CONNECTION_DISCONNECTED;
145         }
146         if (sock->type != SOCKET_TYPE_STREAM) {
147                 return NT_STATUS_INVALID_PARAMETER;
148         }
149
150         if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
151                 return NT_STATUS_INVALID_PARAMETER;
152         }
153
154         if (!sock->ops->fn_accept) {
155                 return NT_STATUS_NOT_IMPLEMENTED;
156         }
157
158         status = sock->ops->fn_accept(sock, new_sock);
159
160         if (NT_STATUS_IS_OK(status)) {
161                 talloc_set_destructor(*new_sock, socket_destructor);
162         }
163
164         return status;
165 }
166
167 NTSTATUS socket_recv(struct socket_context *sock, void *buf, 
168                      size_t wantlen, size_t *nread, uint32_t flags)
169 {
170         if (sock == NULL) {
171                 return NT_STATUS_CONNECTION_DISCONNECTED;
172         }
173         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
174             sock->state != SOCKET_STATE_SERVER_CONNECTED &&
175             sock->type  != SOCKET_TYPE_DGRAM) {
176                 return NT_STATUS_INVALID_PARAMETER;
177         }
178
179         if (!sock->ops->fn_recv) {
180                 return NT_STATUS_NOT_IMPLEMENTED;
181         }
182
183         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) && wantlen > 1) {
184                 if (random() % 10 == 0) {
185                         *nread = 0;
186                         return STATUS_MORE_ENTRIES;
187                 }
188                 return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread, flags);
189         }
190
191         return sock->ops->fn_recv(sock, buf, wantlen, nread, flags);
192 }
193
194 NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf, 
195                          size_t wantlen, size_t *nread, uint32_t flags,
196                          const char **src_addr, int *src_port)
197 {
198         if (sock == NULL) {
199                 return NT_STATUS_CONNECTION_DISCONNECTED;
200         }
201         if (sock->type != SOCKET_TYPE_DGRAM) {
202                 return NT_STATUS_INVALID_PARAMETER;
203         }
204
205         if (!sock->ops->fn_recvfrom) {
206                 return NT_STATUS_NOT_IMPLEMENTED;
207         }
208
209         return sock->ops->fn_recvfrom(sock, buf, wantlen, nread, flags, 
210                                       src_addr, src_port);
211 }
212
213 NTSTATUS socket_send(struct socket_context *sock, 
214                      const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
215 {
216         if (sock == NULL) {
217                 return NT_STATUS_CONNECTION_DISCONNECTED;
218         }
219         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
220             sock->state != SOCKET_STATE_SERVER_CONNECTED) {
221                 return NT_STATUS_INVALID_PARAMETER;
222         }
223
224         if (!sock->ops->fn_send) {
225                 return NT_STATUS_NOT_IMPLEMENTED;
226         }
227
228         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) && blob->length > 1) {
229                 DATA_BLOB blob2 = *blob;
230                 if (random() % 10 == 0) {
231                         *sendlen = 0;
232                         return STATUS_MORE_ENTRIES;
233                 }
234                 blob2.length = 1+(random() % blob2.length);
235                 return sock->ops->fn_send(sock, &blob2, sendlen, flags);
236         }
237
238         return sock->ops->fn_send(sock, blob, sendlen, flags);
239 }
240
241
242 NTSTATUS socket_sendto(struct socket_context *sock, 
243                        const DATA_BLOB *blob, size_t *sendlen, uint32_t flags,
244                        const char *dest_addr, int dest_port)
245 {
246         if (sock == NULL) {
247                 return NT_STATUS_CONNECTION_DISCONNECTED;
248         }
249         if (sock->type != SOCKET_TYPE_DGRAM) {
250                 return NT_STATUS_INVALID_PARAMETER;
251         }
252
253         if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
254             sock->state == SOCKET_STATE_SERVER_CONNECTED) {
255                 return NT_STATUS_INVALID_PARAMETER;
256         }
257
258         if (!sock->ops->fn_sendto) {
259                 return NT_STATUS_NOT_IMPLEMENTED;
260         }
261
262         return sock->ops->fn_sendto(sock, blob, sendlen, flags, dest_addr, dest_port);
263 }
264
265
266 /*
267   ask for the number of bytes in a pending incoming packet
268 */
269 NTSTATUS socket_pending(struct socket_context *sock, size_t *npending)
270 {
271         if (sock == NULL) {
272                 return NT_STATUS_CONNECTION_DISCONNECTED;
273         }
274         if (!sock->ops->fn_pending) {
275                 return NT_STATUS_NOT_IMPLEMENTED;
276         }
277         return sock->ops->fn_pending(sock, npending);
278 }
279
280
281 NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
282 {
283         if (sock == NULL) {
284                 return NT_STATUS_CONNECTION_DISCONNECTED;
285         }
286         if (!sock->ops->fn_set_option) {
287                 return NT_STATUS_NOT_IMPLEMENTED;
288         }
289
290         return sock->ops->fn_set_option(sock, option, val);
291 }
292
293 char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
294 {
295         if (!sock->ops->fn_get_peer_name) {
296                 return NULL;
297         }
298
299         return sock->ops->fn_get_peer_name(sock, mem_ctx);
300 }
301
302 char *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
303 {
304         if (!sock->ops->fn_get_peer_addr) {
305                 return NULL;
306         }
307
308         return sock->ops->fn_get_peer_addr(sock, mem_ctx);
309 }
310
311 int socket_get_peer_port(struct socket_context *sock)
312 {
313         if (!sock->ops->fn_get_peer_port) {
314                 return -1;
315         }
316
317         return sock->ops->fn_get_peer_port(sock);
318 }
319
320 char *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
321 {
322         if (!sock->ops->fn_get_my_addr) {
323                 return NULL;
324         }
325
326         return sock->ops->fn_get_my_addr(sock, mem_ctx);
327 }
328
329 int socket_get_my_port(struct socket_context *sock)
330 {
331         if (!sock->ops->fn_get_my_port) {
332                 return -1;
333         }
334
335         return sock->ops->fn_get_my_port(sock);
336 }
337
338 int socket_get_fd(struct socket_context *sock)
339 {
340         if (!sock->ops->fn_get_fd) {
341                 return -1;
342         }
343
344         return sock->ops->fn_get_fd(sock);
345 }
346
347 /*
348   call dup() on a socket, and close the old fd. This is used to change
349   the fd to the lowest available number, to make select() more
350   efficient (select speed depends on the maxiumum fd number passed to
351   it)
352 */
353 NTSTATUS socket_dup(struct socket_context *sock)
354 {
355         int fd;
356         if (sock->fd == -1) {
357                 return NT_STATUS_INVALID_HANDLE;
358         }
359         fd = dup(sock->fd);
360         if (fd == -1) {
361                 return map_nt_error_from_unix(errno);
362         }
363         close(sock->fd);
364         sock->fd = fd;
365         return NT_STATUS_OK;
366         
367 }
368
369 const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type)
370 {
371         extern const struct socket_ops *socket_ipv4_ops(enum socket_type );
372         extern const struct socket_ops *socket_ipv6_ops(enum socket_type );
373         extern const struct socket_ops *socket_unixdom_ops(enum socket_type );
374
375         if (strcmp("ip", name) == 0 || 
376             strcmp("ipv4", name) == 0) {
377                 return socket_ipv4_ops(type);
378         }
379
380 #if HAVE_SOCKET_IPV6
381         if (strcmp("ipv6", name) == 0) {
382                 if (lp_parm_bool(-1, "socket", "noipv6", False)) {
383                         DEBUG(3, ("IPv6 support was disabled in smb.conf"));
384                         return NULL;
385                 }
386                 return socket_ipv6_ops(type);
387         }
388 #endif
389
390         if (strcmp("unix", name) == 0) {
391                 return socket_unixdom_ops(type);
392         }
393
394         return NULL;
395 }