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