r3013: added support for unix domain sockets in the generic socket library. I
[bbaumbach/samba-autobuild/.git] / source4 / lib / socket / socket_unix.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    unix domain socket functions
5
6    Copyright (C) Stefan Metzmacher 2004
7    Copyright (C) Andrew Tridgell 2004
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 static NTSTATUS unixdom_init(struct socket_context *sock)
27 {
28         sock->fd = socket(PF_UNIX, SOCK_STREAM, 0);
29         if (sock->fd == -1) {
30                 return NT_STATUS_INSUFFICIENT_RESOURCES;
31         }
32
33         return NT_STATUS_OK;
34 }
35
36 static void unixdom_close(struct socket_context *sock)
37 {
38         close(sock->fd);
39 }
40
41 static NTSTATUS unixdom_connect(struct socket_context *sock,
42                                 const char *my_address, int my_port,
43                                 const char *srv_address, int srv_port,
44                                 uint32_t flags)
45 {
46         struct sockaddr_un srv_addr;
47         int ret;
48
49         if (strlen(srv_address)+1 > sizeof(srv_addr.sun_path)) {
50                 return NT_STATUS_INVALID_PARAMETER;
51         }
52
53         ZERO_STRUCT(srv_addr);
54         srv_addr.sun_family = AF_UNIX;
55         strncpy(srv_addr.sun_path, srv_address, sizeof(srv_addr.sun_path));
56
57         if (!(flags & SOCKET_FLAG_BLOCK)) {
58                 ret = set_blocking(sock->fd, False);
59                 if (ret == -1) {
60                         return NT_STATUS_INVALID_PARAMETER;
61                 }
62         }
63
64         ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
65         if (ret == -1) {
66                 return NT_STATUS_CONNECTION_REFUSED;
67         }
68
69         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
70
71         return NT_STATUS_OK;
72 }
73
74 static NTSTATUS unixdom_listen(struct socket_context *sock,
75                                const char *my_address, int port,
76                                int queue_size, uint32_t flags)
77 {
78         struct sockaddr_un my_addr;
79         int ret;
80
81         if (strlen(my_address)+1 > sizeof(my_addr.sun_path)) {
82                 return NT_STATUS_INVALID_PARAMETER;
83         }
84
85         ZERO_STRUCT(my_addr);
86         my_addr.sun_family = AF_UNIX;
87         strncpy(my_addr.sun_path, my_address, sizeof(my_addr.sun_path));
88
89         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
90         if (ret == -1) {
91                 return NT_STATUS_UNSUCCESSFUL;
92         }
93
94         ret = listen(sock->fd, queue_size);
95         if (ret == -1) {
96                 return NT_STATUS_INSUFFICIENT_RESOURCES;
97         }
98
99         if (!(flags & SOCKET_FLAG_BLOCK)) {
100                 ret = set_blocking(sock->fd, False);
101                 if (ret == -1) {
102                         return NT_STATUS_INVALID_PARAMETER;
103                 }
104         }
105
106         sock->state = SOCKET_STATE_SERVER_LISTEN;
107
108         return NT_STATUS_OK;
109 }
110
111 static NTSTATUS unixdom_accept(struct socket_context *sock, 
112                                struct socket_context **new_sock, 
113                                uint32_t flags)
114 {
115         struct sockaddr_un cli_addr;
116         socklen_t cli_addr_len = sizeof(cli_addr);
117         int new_fd;
118
119         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
120         if (new_fd == -1) {
121                 return NT_STATUS_INSUFFICIENT_RESOURCES;
122         }
123
124         (*new_sock) = talloc_p(NULL, struct socket_context);
125         if (!(*new_sock)) {
126                 close(new_fd);
127                 return NT_STATUS_NO_MEMORY;
128         }
129
130         /* copy the socket_context */
131         (*new_sock)->type               = sock->type;
132         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
133         (*new_sock)->flags              = flags;
134
135         (*new_sock)->fd                 = new_fd;
136
137         (*new_sock)->private_data       = NULL;
138         (*new_sock)->ops                = sock->ops;
139
140         return NT_STATUS_OK;
141 }
142
143 static NTSTATUS unixdom_recv(struct socket_context *sock, TALLOC_CTX *mem_ctx,
144                              DATA_BLOB *blob, size_t wantlen, uint32_t flags)
145 {
146         ssize_t gotlen;
147         void *buf;
148         int flgs = 0;
149
150         buf = talloc(mem_ctx, wantlen);
151         if (!buf) {
152                 return NT_STATUS_NO_MEMORY;
153         }
154
155         /* TODO: we need to map all flags here */
156         if (flags & SOCKET_FLAG_PEEK) {
157                 flgs |= MSG_PEEK;
158         }
159
160         if (!(flags & SOCKET_FLAG_BLOCK)) {
161                 flgs |= MSG_DONTWAIT;
162         }
163
164         if (flags & SOCKET_FLAG_BLOCK) {
165                 flgs |= MSG_WAITALL;
166         }
167
168         gotlen = recv(sock->fd, buf, wantlen, flgs);
169         if (gotlen == 0) {
170                 talloc_free(buf);
171                 return NT_STATUS_END_OF_FILE;
172         } else if (gotlen == -1) {
173                 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
174                 switch (errno) {
175                         case EBADF:
176                         case ENOTCONN:
177                         case ENOTSOCK:
178                         case EFAULT:
179                         case EINVAL:
180                                 status = NT_STATUS_INVALID_PARAMETER;
181                                 break;
182                         case EAGAIN:
183                         case EINTR:
184                                 status = STATUS_MORE_ENTRIES;
185                                 break;
186                         case ECONNREFUSED:
187                                 status = NT_STATUS_CONNECTION_REFUSED;
188                                 break;
189                 }
190                 talloc_free(buf);
191                 return status;
192         }
193
194         blob->length = gotlen;
195         blob->data = talloc_realloc(mem_ctx, buf, gotlen);
196         if (!blob->data) {
197                 return NT_STATUS_NO_MEMORY;
198         }
199
200         return NT_STATUS_OK;
201 }
202
203 static NTSTATUS unixdom_send(struct socket_context *sock, TALLOC_CTX *mem_ctx,
204                              const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
205 {
206         ssize_t len;
207         int flgs = 0;
208
209         /* TODO: we need to map all flags here */
210         if (!(flags & SOCKET_FLAG_BLOCK)) {
211                 flgs |= MSG_DONTWAIT;
212         }
213
214         len = send(sock->fd, blob->data, blob->length, flgs);
215         if (len == -1) {
216                 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
217                 switch (errno) {
218                         case EBADF:
219                         case ENOTSOCK:
220                         case EFAULT:
221                         case EINVAL:
222                                 status = NT_STATUS_INVALID_PARAMETER;
223                                 break;
224                         case EMSGSIZE:
225                                 status = NT_STATUS_INVALID_BUFFER_SIZE;
226                                 break;
227                         case EAGAIN:
228                         /*case EWOULDBLOCK: this is an alis of EAGAIN --metze */
229                         case EINTR:
230                                 *sendlen = 0;
231                                 status = STATUS_MORE_ENTRIES;
232                                 break;
233                         case ENOBUFS:
234                                 status = NT_STATUS_FOOBAR;
235                                 break;
236                         case ENOMEM:
237                                 status = NT_STATUS_NO_MEMORY;
238                                 break;
239                         case EPIPE:
240                                 status = NT_STATUS_CONNECTION_DISCONNECTED;
241                                 break;
242                 }
243                 return status;
244         }       
245
246         *sendlen = len;
247
248         return NT_STATUS_OK;
249 }
250
251 static NTSTATUS unixdom_set_option(struct socket_context *sock, 
252                                    const char *option, const char *val)
253 {
254         set_socket_options(sock->fd, option);
255         return NT_STATUS_OK;
256 }
257
258 static char *unixdom_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
259 {
260         return talloc_strdup(mem_ctx, "LOCAL/unixdom");
261 }
262
263 static char *unixdom_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
264 {
265         return talloc_strdup(mem_ctx, "LOCAL/unixdom");
266 }
267
268 static int unixdom_get_peer_port(struct socket_context *sock)
269 {
270         return 0;
271 }
272
273 static char *unixdom_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
274 {
275         return talloc_strdup(mem_ctx, "LOCAL/unixdom");
276 }
277
278 static int unixdom_get_my_port(struct socket_context *sock)
279 {
280         return 0;
281 }
282
283 static int unixdom_get_fd(struct socket_context *sock)
284 {
285         return sock->fd;
286 }
287
288 static const struct socket_ops unixdom_ops = {
289         .name           = "unix",
290         .type           = SOCKET_TYPE_STREAM,
291
292         .init           = unixdom_init,
293         .connect        = unixdom_connect,
294         .listen         = unixdom_listen,
295         .accept         = unixdom_accept,
296         .recv           = unixdom_recv,
297         .send           = unixdom_send,
298         .close          = unixdom_close,
299
300         .set_option     = unixdom_set_option,
301
302         .get_peer_name  = unixdom_get_peer_name,
303         .get_peer_addr  = unixdom_get_peer_addr,
304         .get_peer_port  = unixdom_get_peer_port,
305         .get_my_addr    = unixdom_get_my_addr,
306         .get_my_port    = unixdom_get_my_port,
307
308         .get_fd         = unixdom_get_fd
309 };
310
311 const struct socket_ops *socket_unixdom_ops(void)
312 {
313         return &unixdom_ops;
314 }
315
316 NTSTATUS socket_unixdom_init(void)
317 {
318         return NT_STATUS_OK;
319 }