r3314: added a option "socket:testnonblock" to the generic socket code. If
[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->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                 return sock->ops->recv(sock, buf, 1+(random() % (wantlen-1)), nread, flags);
162         }
163
164         return sock->ops->recv(sock, buf, wantlen, nread, flags);
165 }
166
167 NTSTATUS socket_send(struct socket_context *sock, 
168                      const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
169 {
170         if (sock->type != SOCKET_TYPE_STREAM) {
171                 return NT_STATUS_INVALID_PARAMETER;
172         }
173
174         if (sock->state != SOCKET_STATE_CLIENT_CONNECTED &&
175             sock->state != SOCKET_STATE_SERVER_CONNECTED) {
176                 return NT_STATUS_INVALID_PARAMETER;
177         }
178
179         if (!sock->ops->send) {
180                 return NT_STATUS_NOT_IMPLEMENTED;
181         }
182
183         if ((sock->flags & SOCKET_FLAG_TESTNONBLOCK) && blob->length > 1) {
184                 DATA_BLOB blob2 = *blob;
185                 blob2.length = 1+(random() % (blob2.length-1));
186                 return sock->ops->send(sock, &blob2, sendlen, flags);
187         }
188
189         return sock->ops->send(sock, blob, sendlen, flags);
190 }
191
192 NTSTATUS socket_set_option(struct socket_context *sock, const char *option, const char *val)
193 {
194         if (!sock->ops->set_option) {
195                 return NT_STATUS_NOT_IMPLEMENTED;
196         }
197
198         return sock->ops->set_option(sock, option, val);
199 }
200
201 char *socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
202 {
203         if (!sock->ops->get_peer_name) {
204                 return NULL;
205         }
206
207         return sock->ops->get_peer_name(sock, mem_ctx);
208 }
209
210 char *socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
211 {
212         if (!sock->ops->get_peer_addr) {
213                 return NULL;
214         }
215
216         return sock->ops->get_peer_addr(sock, mem_ctx);
217 }
218
219 int socket_get_peer_port(struct socket_context *sock)
220 {
221         if (!sock->ops->get_peer_port) {
222                 return -1;
223         }
224
225         return sock->ops->get_peer_port(sock);
226 }
227
228 char *socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
229 {
230         if (!sock->ops->get_my_addr) {
231                 return NULL;
232         }
233
234         return sock->ops->get_my_addr(sock, mem_ctx);
235 }
236
237 int socket_get_my_port(struct socket_context *sock)
238 {
239         if (!sock->ops->get_my_port) {
240                 return -1;
241         }
242
243         return sock->ops->get_my_port(sock);
244 }
245
246 int socket_get_fd(struct socket_context *sock)
247 {
248         if (!sock->ops->get_fd) {
249                 return -1;
250         }
251
252         return sock->ops->get_fd(sock);
253 }
254
255 const struct socket_ops *socket_getops_byname(const char *name, enum socket_type type)
256 {
257         if (strcmp("ip", name) == 0 || 
258             strcmp("ipv4", name) == 0) {
259                 return socket_ipv4_ops();
260         }
261
262         if (strcmp("unix", name) == 0) {
263                 return socket_unixdom_ops();
264         }
265
266         return NULL;
267 }