r4831: added udp support to our generic sockets library.
[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
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->fn_close) {
30                 sock->ops->fn_close(sock);
31         }
32         return 0;
33 }
34
35 static NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops,
36                                        struct socket_context **new_sock, 
37                                        enum socket_type type, uint32_t flags)
38 {
39         NTSTATUS status;
40
41         (*new_sock) = talloc(mem_ctx, struct socket_context);
42         if (!(*new_sock)) {
43                 return NT_STATUS_NO_MEMORY;
44         }
45
46         (*new_sock)->type = type;
47         (*new_sock)->state = SOCKET_STATE_UNDEFINED;
48         (*new_sock)->flags = flags;
49
50         (*new_sock)->fd = -1;
51
52         (*new_sock)->private_data = NULL;
53         (*new_sock)->ops = ops;
54
55         status = (*new_sock)->ops->fn_init((*new_sock));
56         if (!NT_STATUS_IS_OK(status)) {
57                 talloc_free(*new_sock);
58                 return status;
59         }
60
61         /* by enabling "testnonblock" mode, all socket receive and
62            send calls on non-blocking sockets will randomly recv/send
63            less data than requested */
64         if (!(flags & SOCKET_FLAG_BLOCK) &&
65             type == SOCKET_TYPE_STREAM &&
66             lp_parm_bool(-1, "socket", "testnonblock", False)) {
67                 (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
68         }
69
70         talloc_set_destructor(*new_sock, socket_destructor);
71
72         return NT_STATUS_OK;
73 }
74
75 NTSTATUS socket_create(const char *name, enum socket_type type, 
76                        struct socket_context **new_sock, uint32_t flags)
77 {
78         const struct socket_ops *ops;
79
80         ops = socket_getops_byname(name, type);
81         if (!ops) {
82                 return NT_STATUS_INVALID_PARAMETER;
83         }
84
85         return socket_create_with_ops(NULL, ops, new_sock, type, flags);
86 }
87
88 void socket_destroy(struct socket_context *sock)
89 {
90         /* the close is handled by the destructor */
91         talloc_free(sock);
92 }
93
94 NTSTATUS socket_connect(struct socket_context *sock,
95                         const char *my_address, int my_port,
96                         const char *server_address, int server_port,
97                         uint32_t flags)
98 {
99         if (sock->state != SOCKET_STATE_UNDEFINED) {
100                 return NT_STATUS_INVALID_PARAMETER;
101         }
102
103         if (!sock->ops->fn_connect) {
104                 return NT_STATUS_NOT_IMPLEMENTED;
105         }
106
107         return sock->ops->fn_connect(sock, my_address, my_port, server_address, server_port, flags);
108 }
109
110 NTSTATUS socket_connect_complete(struct socket_context *sock, uint32_t flags)
111 {
112         if (!sock->ops->fn_connect_complete) {
113                 return NT_STATUS_NOT_IMPLEMENTED;
114         }
115         return sock->ops->fn_connect_complete(sock, flags);
116 }
117
118 NTSTATUS socket_listen(struct socket_context *sock, const char *my_address, int port, int queue_size, uint32_t flags)
119 {
120         if (sock->state != SOCKET_STATE_UNDEFINED) {
121                 return NT_STATUS_INVALID_PARAMETER;
122         }
123
124         if (!sock->ops->fn_listen) {
125                 return NT_STATUS_NOT_IMPLEMENTED;
126         }
127
128         return sock->ops->fn_listen(sock, my_address, port, queue_size, flags);
129 }
130
131 NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
132 {
133         NTSTATUS status;
134
135         if (sock->type != SOCKET_TYPE_STREAM) {
136                 return NT_STATUS_INVALID_PARAMETER;
137         }
138
139         if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
140                 return NT_STATUS_INVALID_PARAMETER;
141         }
142
143         if (!sock->ops->fn_accept) {
144                 return NT_STATUS_NOT_IMPLEMENTED;
145         }
146
147         status = sock->ops->fn_accept(sock, new_sock);
148
149         if (NT_STATUS_IS_OK(status)) {
150                 talloc_set_destructor(*new_sock, socket_destructor);
151         }
152
153         return status;
154 }
155
156 NTSTATUS socket_recv(struct socket_context *sock, void *buf, 
157                      size_t wantlen, size_t *nread, uint32_t flags)
158 {
159         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
160             sock->state != SOCKET_STATE_SERVER_CONNECTED) {
161                 return NT_STATUS_INVALID_PARAMETER;
162         }
163
164         if (!sock->ops->fn_recv) {
165                 return NT_STATUS_NOT_IMPLEMENTED;
166         }
167
168         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) && wantlen > 1) {
169                 if (random() % 10 == 0) {
170                         *nread = 0;
171                         return STATUS_MORE_ENTRIES;
172                 }
173                 return sock->ops->fn_recv(sock, buf, 1+(random() % wantlen), nread, flags);
174         }
175
176         return sock->ops->fn_recv(sock, buf, wantlen, nread, flags);
177 }
178
179 NTSTATUS socket_recvfrom(struct socket_context *sock, void *buf, 
180                          size_t wantlen, size_t *nread, uint32_t flags,
181                          const char **src_addr, int *src_port)
182 {
183         if (sock->type != SOCKET_TYPE_DGRAM) {
184                 return NT_STATUS_INVALID_PARAMETER;
185         }
186
187         if (!sock->ops->fn_recvfrom) {
188                 return NT_STATUS_NOT_IMPLEMENTED;
189         }
190
191         return sock->ops->fn_recvfrom(sock, buf, wantlen, nread, flags, 
192                                       src_addr, src_port);
193 }
194
195 NTSTATUS socket_send(struct socket_context *sock, 
196                      const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
197 {
198         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
199             sock->state != SOCKET_STATE_SERVER_CONNECTED) {
200                 return NT_STATUS_INVALID_PARAMETER;
201         }
202
203         if (!sock->ops->fn_send) {
204                 return NT_STATUS_NOT_IMPLEMENTED;
205         }
206
207         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) && blob->length > 1) {
208                 DATA_BLOB blob2 = *blob;
209                 if (random() % 10 == 0) {
210                         *sendlen = 0;
211                         return STATUS_MORE_ENTRIES;
212                 }
213                 blob2.length = 1+(random() % blob2.length);
214                 return sock->ops->fn_send(sock, &blob2, sendlen, flags);
215         }
216
217         return sock->ops->fn_send(sock, blob, sendlen, flags);
218 }
219
220
221 NTSTATUS socket_sendto(struct socket_context *sock, 
222                        const DATA_BLOB *blob, size_t *sendlen, uint32_t flags,
223                        const char *dest_addr, int dest_port)
224 {
225         if (sock->type != SOCKET_TYPE_DGRAM) {
226                 return NT_STATUS_INVALID_PARAMETER;
227         }
228
229         if (sock->state == SOCKET_STATE_CLIENT_CONNECTED ||
230             sock->state == SOCKET_STATE_SERVER_CONNECTED) {
231                 return NT_STATUS_INVALID_PARAMETER;
232         }
233
234         if (!sock->ops->fn_sendto) {
235                 return NT_STATUS_NOT_IMPLEMENTED;
236         }
237
238         return sock->ops->fn_sendto(sock, blob, sendlen, flags, dest_addr, dest_port);
239 }
240
241 NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
242 {
243         if (!sock->ops->fn_set_option) {
244                 return NT_STATUS_NOT_IMPLEMENTED;
245         }
246
247         return sock->ops->fn_set_option(sock, option, val);
248 }
249
250 char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
251 {
252         if (!sock->ops->fn_get_peer_name) {
253                 return NULL;
254         }
255
256         return sock->ops->fn_get_peer_name(sock, mem_ctx);
257 }
258
259 char *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
260 {
261         if (!sock->ops->fn_get_peer_addr) {
262                 return NULL;
263         }
264
265         return sock->ops->fn_get_peer_addr(sock, mem_ctx);
266 }
267
268 int socket_get_peer_port(struct socket_context *sock)
269 {
270         if (!sock->ops->fn_get_peer_port) {
271                 return -1;
272         }
273
274         return sock->ops->fn_get_peer_port(sock);
275 }
276
277 char *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
278 {
279         if (!sock->ops->fn_get_my_addr) {
280                 return NULL;
281         }
282
283         return sock->ops->fn_get_my_addr(sock, mem_ctx);
284 }
285
286 int socket_get_my_port(struct socket_context *sock)
287 {
288         if (!sock->ops->fn_get_my_port) {
289                 return -1;
290         }
291
292         return sock->ops->fn_get_my_port(sock);
293 }
294
295 int socket_get_fd(struct socket_context *sock)
296 {
297         if (!sock->ops->fn_get_fd) {
298                 return -1;
299         }
300
301         return sock->ops->fn_get_fd(sock);
302 }
303
304 /*
305   call dup() on a socket, and close the old fd. This is used to change
306   the fd to the lowest available number, to make select() more
307   efficient (select speed depends on the maxiumum fd number passed to
308   it)
309 */
310 NTSTATUS socket_dup(struct socket_context *sock)
311 {
312         int fd;
313         if (sock->fd == -1) {
314                 return NT_STATUS_INVALID_HANDLE;
315         }
316         fd = dup(sock->fd);
317         if (fd == -1) {
318                 return map_nt_error_from_unix(errno);
319         }
320         close(sock->fd);
321         sock->fd = fd;
322         return NT_STATUS_OK;
323         
324 }
325
326 const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type)
327 {
328         if (strcmp("ip", name) == 0 || 
329             strcmp("ipv4", name) == 0) {
330                 return socket_ipv4_ops(type);
331         }
332
333 #if HAVE_SOCKET_IPV6
334         if (strcmp("ipv6", name) == 0) {
335                 if (lp_parm_bool(-1, "socket", "noipv6", False)) {
336                         DEBUG(3, ("IPv6 support was disabled in smb.conf"));
337                         return NULL;
338                 }
339                 return socket_ipv6_ops(type);
340         }
341 #endif
342
343         if (strcmp("unix", name) == 0) {
344                 return socket_unixdom_ops(type);
345         }
346
347         return NULL;
348 }