r4686: cerate a function to create a socket by specifying an socket_ops struct
[nivanova/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 NTSTATUS socket_create_with_ops(TALLOC_CTX *mem_ctx, const struct socket_ops *ops, struct socket_context **new_sock, uint32_t flags)
36 {
37         NTSTATUS status;
38
39         (*new_sock) = talloc(mem_ctx, struct socket_context);
40         if (!(*new_sock)) {
41                 return NT_STATUS_NO_MEMORY;
42         }
43
44         (*new_sock)->type = ops->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 = ops;
52
53         status = (*new_sock)->ops->fn_init((*new_sock));
54         if (!NT_STATUS_IS_OK(status)) {
55                 talloc_free(*new_sock);
56                 return status;
57         }
58
59         /* by enabling "testnonblock" mode, all socket receive and
60            send calls on non-blocking sockets will randomly recv/send
61            less data than requested */
62         if (!(flags & SOCKET_FLAG_BLOCK) &&
63             lp_parm_bool(-1, "socket", "testnonblock", False)) {
64                 (*new_sock)->flags |= SOCKET_FLAG_TESTNONBLOCK;
65         }
66
67         talloc_set_destructor(*new_sock, socket_destructor);
68
69         return NT_STATUS_OK;
70 }
71
72 NTSTATUS socket_create(const char *name, enum socket_type type, struct socket_context **new_sock, uint32_t flags)
73 {
74         const struct socket_ops *ops;
75
76         ops = socket_getops_byname(name, type);
77         if (!ops) {
78                 return NT_STATUS_INVALID_PARAMETER;
79         }
80
81         return socket_create_with_ops(NULL, ops, new_sock, flags);
82 }
83
84 void socket_destroy(struct socket_context *sock)
85 {
86         /* the close is handled by the destructor */
87         talloc_free(sock);
88 }
89
90 NTSTATUS socket_connect(struct socket_context *sock,
91                         const char *my_address, int my_port,
92                         const char *server_address, int server_port,
93                         uint32_t flags)
94 {
95         if (sock->type != SOCKET_TYPE_STREAM) {
96                 return NT_STATUS_INVALID_PARAMETER;
97         }
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_listen(struct socket_context *sock, const char *my_address, int port, int queue_size, uint32_t flags)
111 {
112         if (sock->type != SOCKET_TYPE_STREAM) {
113                 return NT_STATUS_INVALID_PARAMETER;
114         }
115
116         if (sock->state != SOCKET_STATE_UNDEFINED) {
117                 return NT_STATUS_INVALID_PARAMETER;
118         }
119
120         if (!sock->ops->fn_listen) {
121                 return NT_STATUS_NOT_IMPLEMENTED;
122         }
123
124         return sock->ops->fn_listen(sock, my_address, port, queue_size, flags);
125 }
126
127 NTSTATUS socket_accept(struct socket_context *sock, struct socket_context **new_sock)
128 {
129         NTSTATUS status;
130
131         if (sock->type != SOCKET_TYPE_STREAM) {
132                 return NT_STATUS_INVALID_PARAMETER;
133         }
134
135         if (sock->state != SOCKET_STATE_SERVER_LISTEN) {
136                 return NT_STATUS_INVALID_PARAMETER;
137         }
138
139         if (!sock->ops->fn_accept) {
140                 return NT_STATUS_NOT_IMPLEMENTED;
141         }
142
143         status = sock->ops->fn_accept(sock, new_sock);
144
145         if (NT_STATUS_IS_OK(status)) {
146                 talloc_set_destructor(*new_sock, socket_destructor);
147         }
148
149         return status;
150 }
151
152 NTSTATUS socket_recv(struct socket_context *sock, void *buf, 
153                      size_t wantlen, size_t *nread, uint32_t flags)
154 {
155         if (sock->type != SOCKET_TYPE_STREAM) {
156                 return NT_STATUS_INVALID_PARAMETER;
157         }
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_send(struct socket_context *sock, 
180                      const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
181 {
182         if (sock->type != SOCKET_TYPE_STREAM) {
183                 return NT_STATUS_INVALID_PARAMETER;
184         }
185
186         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
187             sock->state != SOCKET_STATE_SERVER_CONNECTED) {
188                 return NT_STATUS_INVALID_PARAMETER;
189         }
190
191         if (!sock->ops->fn_send) {
192                 return NT_STATUS_NOT_IMPLEMENTED;
193         }
194
195         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) && blob->length > 1) {
196                 DATA_BLOB blob2 = *blob;
197                 if (random() % 10 == 0) {
198                         *sendlen = 0;
199                         return STATUS_MORE_ENTRIES;
200                 }
201                 blob2.length = 1+(random() % blob2.length);
202                 return sock->ops->fn_send(sock, &blob2, sendlen, flags);
203         }
204
205         return sock->ops->fn_send(sock, blob, sendlen, flags);
206 }
207
208 NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
209 {
210         if (!sock->ops->fn_set_option) {
211                 return NT_STATUS_NOT_IMPLEMENTED;
212         }
213
214         return sock->ops->fn_set_option(sock, option, val);
215 }
216
217 char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
218 {
219         if (!sock->ops->fn_get_peer_name) {
220                 return NULL;
221         }
222
223         return sock->ops->fn_get_peer_name(sock, mem_ctx);
224 }
225
226 char *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
227 {
228         if (!sock->ops->fn_get_peer_addr) {
229                 return NULL;
230         }
231
232         return sock->ops->fn_get_peer_addr(sock, mem_ctx);
233 }
234
235 int socket_get_peer_port(struct socket_context *sock)
236 {
237         if (!sock->ops->fn_get_peer_port) {
238                 return -1;
239         }
240
241         return sock->ops->fn_get_peer_port(sock);
242 }
243
244 char *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
245 {
246         if (!sock->ops->fn_get_my_addr) {
247                 return NULL;
248         }
249
250         return sock->ops->fn_get_my_addr(sock, mem_ctx);
251 }
252
253 int socket_get_my_port(struct socket_context *sock)
254 {
255         if (!sock->ops->fn_get_my_port) {
256                 return -1;
257         }
258
259         return sock->ops->fn_get_my_port(sock);
260 }
261
262 int socket_get_fd(struct socket_context *sock)
263 {
264         if (!sock->ops->fn_get_fd) {
265                 return -1;
266         }
267
268         return sock->ops->fn_get_fd(sock);
269 }
270
271 /*
272   call dup() on a socket, and close the old fd. This is used to change
273   the fd to the lowest available number, to make select() more
274   efficient (select speed depends on the maxiumum fd number passed to
275   it)
276 */
277 NTSTATUS socket_dup(struct socket_context *sock)
278 {
279         int fd;
280         if (sock->fd == -1) {
281                 return NT_STATUS_INVALID_HANDLE;
282         }
283         fd = dup(sock->fd);
284         if (fd == -1) {
285                 return map_nt_error_from_unix(errno);
286         }
287         close(sock->fd);
288         sock->fd = fd;
289         return NT_STATUS_OK;
290         
291 }
292
293 const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type)
294 {
295         if (strcmp("ip", name) == 0 || 
296             strcmp("ipv4", name) == 0) {
297                 return socket_ipv4_ops();
298         }
299
300 #if HAVE_SOCKET_IPV6
301         if (strcmp("ipv6", name) == 0) {
302                 if (lp_parm_bool(-1, "socket", "noipv6", False)) {
303                         DEBUG(3, ("IPv6 support was disabled in smb.conf"));
304                         return NULL;
305                 }
306                 return socket_ipv6_ops();
307         }
308 #endif
309
310         if (strcmp("unix", name) == 0) {
311                 return socket_unixdom_ops();
312         }
313
314         return NULL;
315 }