r6562: added support for datagram unix domain sockets in the socket library
[samba.git] / source / 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-2005
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 #include "lib/socket/socket.h"
26 #include "system/network.h"
27 #include "system/filesys.h"
28
29
30
31 /*
32   approximate errno mapping
33 */
34 static NTSTATUS unixdom_error(int ernum)
35 {
36         return map_nt_error_from_unix(ernum);
37 }
38
39 static NTSTATUS unixdom_init(struct socket_context *sock)
40 {
41         int type;
42
43         switch (sock->type) {
44         case SOCKET_TYPE_STREAM:
45                 type = SOCK_STREAM;
46                 break;
47         case SOCKET_TYPE_DGRAM:
48                 type = SOCK_DGRAM;
49                 break;
50         default:
51                 return NT_STATUS_INVALID_PARAMETER;
52         }
53
54         sock->fd = socket(PF_UNIX, type, 0);
55         if (sock->fd == -1) {
56                 return map_nt_error_from_unix(errno);
57         }
58         sock->private_data = NULL;
59
60         return NT_STATUS_OK;
61 }
62
63 static void unixdom_close(struct socket_context *sock)
64 {
65         close(sock->fd);
66 }
67
68 static NTSTATUS unixdom_connect_complete(struct socket_context *sock, uint32_t flags)
69 {
70         int error=0, ret;
71         socklen_t len = sizeof(error);
72
73         /* check for any errors that may have occurred - this is needed
74            for non-blocking connect */
75         ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
76         if (ret == -1) {
77                 return map_nt_error_from_unix(errno);
78         }
79         if (error != 0) {
80                 return map_nt_error_from_unix(error);
81         }
82
83         if (!(flags & SOCKET_FLAG_BLOCK)) {
84                 ret = set_blocking(sock->fd, False);
85                 if (ret == -1) {
86                         return map_nt_error_from_unix(errno);
87                 }
88         }
89
90         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
91
92         return NT_STATUS_OK;
93 }
94
95 static NTSTATUS unixdom_connect(struct socket_context *sock,
96                                 const char *my_address, int my_port,
97                                 const char *srv_address, int srv_port,
98                                 uint32_t flags)
99 {
100         struct sockaddr_un srv_addr;
101         int ret;
102
103         if (strlen(srv_address)+1 > sizeof(srv_addr.sun_path)) {
104                 return NT_STATUS_INVALID_PARAMETER;
105         }
106
107         ZERO_STRUCT(srv_addr);
108         srv_addr.sun_family = AF_UNIX;
109         strncpy(srv_addr.sun_path, srv_address, sizeof(srv_addr.sun_path));
110
111         ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
112         if (ret == -1) {
113                 return unixdom_error(errno);
114         }
115
116         return unixdom_connect_complete(sock, flags);
117 }
118
119 static NTSTATUS unixdom_listen(struct socket_context *sock,
120                                const char *my_address, int port,
121                                int queue_size, uint32_t flags)
122 {
123         struct sockaddr_un my_addr;
124         int ret;
125
126         if (strlen(my_address)+1 > sizeof(my_addr.sun_path)) {
127                 return NT_STATUS_INVALID_PARAMETER;
128         }
129
130         /* delete if it already exists */
131         unlink(my_address);
132
133         ZERO_STRUCT(my_addr);
134         my_addr.sun_family = AF_UNIX;
135         strncpy(my_addr.sun_path, my_address, sizeof(my_addr.sun_path));
136
137         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
138         if (ret == -1) {
139                 return unixdom_error(errno);
140         }
141
142         if (sock->type == SOCKET_TYPE_STREAM) {
143                 ret = listen(sock->fd, queue_size);
144                 if (ret == -1) {
145                         return unixdom_error(errno);
146                 }
147         }
148
149         if (!(flags & SOCKET_FLAG_BLOCK)) {
150                 ret = set_blocking(sock->fd, False);
151                 if (ret == -1) {
152                         return unixdom_error(errno);
153                 }
154         }
155
156         sock->state = SOCKET_STATE_SERVER_LISTEN;
157         sock->private_data = (void *)talloc_strdup(sock, my_address);
158
159         return NT_STATUS_OK;
160 }
161
162 static NTSTATUS unixdom_accept(struct socket_context *sock, 
163                                struct socket_context **new_sock)
164 {
165         struct sockaddr_un cli_addr;
166         socklen_t cli_addr_len = sizeof(cli_addr);
167         int new_fd;
168
169         if (sock->type != SOCKET_TYPE_STREAM) {
170                 return NT_STATUS_INVALID_PARAMETER;
171         }
172
173         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
174         if (new_fd == -1) {
175                 return unixdom_error(errno);
176         }
177
178         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
179                 int ret = set_blocking(new_fd, False);
180                 if (ret == -1) {
181                         close(new_fd);
182                         return map_nt_error_from_unix(errno);
183                 }
184         }
185
186         (*new_sock) = talloc(NULL, struct socket_context);
187         if (!(*new_sock)) {
188                 close(new_fd);
189                 return NT_STATUS_NO_MEMORY;
190         }
191
192         /* copy the socket_context */
193         (*new_sock)->type               = sock->type;
194         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
195         (*new_sock)->flags              = sock->flags;
196
197         (*new_sock)->fd                 = new_fd;
198
199         (*new_sock)->private_data       = NULL;
200         (*new_sock)->ops                = sock->ops;
201
202         return NT_STATUS_OK;
203 }
204
205 static NTSTATUS unixdom_recv(struct socket_context *sock, void *buf, 
206                              size_t wantlen, size_t *nread, uint32_t flags)
207 {
208         ssize_t gotlen;
209         int flgs = 0;
210
211         /* TODO: we need to map all flags here */
212         if (flags & SOCKET_FLAG_PEEK) {
213                 flgs |= MSG_PEEK;
214         }
215
216         if (flags & SOCKET_FLAG_BLOCK) {
217                 flgs |= MSG_WAITALL;
218         }
219
220         *nread = 0;
221
222         gotlen = recv(sock->fd, buf, wantlen, flgs);
223         if (gotlen == 0) {
224                 return NT_STATUS_END_OF_FILE;
225         } else if (gotlen == -1) {
226                 return unixdom_error(errno);
227         }
228
229         *nread = gotlen;
230
231         return NT_STATUS_OK;
232 }
233
234 static NTSTATUS unixdom_send(struct socket_context *sock,
235                              const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
236 {
237         ssize_t len;
238         int flgs = 0;
239
240         *sendlen = 0;
241
242         len = send(sock->fd, blob->data, blob->length, flgs);
243         if (len == -1) {
244                 return unixdom_error(errno);
245         }       
246
247         *sendlen = len;
248
249         return NT_STATUS_OK;
250 }
251
252 static NTSTATUS unixdom_set_option(struct socket_context *sock, 
253                                    const char *option, const char *val)
254 {
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         .fn_init                = unixdom_init,
291         .fn_connect             = unixdom_connect,
292         .fn_connect_complete    = unixdom_connect_complete,
293         .fn_listen              = unixdom_listen,
294         .fn_accept              = unixdom_accept,
295         .fn_recv                = unixdom_recv,
296         .fn_send                = unixdom_send,
297         .fn_close               = unixdom_close,
298
299         .fn_set_option          = unixdom_set_option,
300
301         .fn_get_peer_name       = unixdom_get_peer_name,
302         .fn_get_peer_addr       = unixdom_get_peer_addr,
303         .fn_get_peer_port       = unixdom_get_peer_port,
304         .fn_get_my_addr         = unixdom_get_my_addr,
305         .fn_get_my_port         = unixdom_get_my_port,
306
307         .fn_get_fd              = unixdom_get_fd
308 };
309
310 const struct socket_ops *socket_unixdom_ops(enum socket_type type)
311 {
312         return &unixdom_ops;
313 }