s3-libads: use ldap_init_fd() to initialize a ldap session if possible
[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-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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "lib/socket/socket.h"
25 #include "system/network.h"
26 #include "system/filesys.h"
27
28 _PUBLIC_ const struct socket_ops *socket_unixdom_ops(enum socket_type type);
29
30
31 /*
32   approximate errno mapping
33 */
34 static NTSTATUS unixdom_error(int ernum)
35 {
36         return map_nt_error_from_unix_common(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_common(errno);
57         }
58         sock->private_data = NULL;
59
60         sock->backend_name = "unix";
61
62         smb_set_close_on_exec(sock->fd);
63
64         return NT_STATUS_OK;
65 }
66
67 static void unixdom_close(struct socket_context *sock)
68 {
69         close(sock->fd);
70 }
71
72 static NTSTATUS unixdom_connect_complete(struct socket_context *sock, uint32_t flags)
73 {
74         int error=0, ret;
75         socklen_t len = sizeof(error);
76
77         /* check for any errors that may have occurred - this is needed
78            for non-blocking connect */
79         ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
80         if (ret == -1) {
81                 return map_nt_error_from_unix_common(errno);
82         }
83         if (error != 0) {
84                 return map_nt_error_from_unix_common(error);
85         }
86
87         ret = set_blocking(sock->fd, false);
88         if (ret == -1) {
89                 return map_nt_error_from_unix_common(errno);
90         }
91
92         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
93
94         return NT_STATUS_OK;
95 }
96
97 static NTSTATUS unixdom_connect(struct socket_context *sock,
98                                 const struct socket_address *my_address, 
99                                 const struct socket_address *srv_address, 
100                                 uint32_t flags)
101 {
102         int ret;
103
104         if (srv_address->sockaddr) {
105                 ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
106         } else {
107                 struct sockaddr_un srv_addr;
108                 if (strlen(srv_address->addr)+1 > sizeof(srv_addr.sun_path)) {
109                         return NT_STATUS_OBJECT_PATH_INVALID;
110                 }
111                 
112                 ZERO_STRUCT(srv_addr);
113                 srv_addr.sun_family = AF_UNIX;
114                 snprintf(srv_addr.sun_path, sizeof(srv_addr.sun_path), "%s", srv_address->addr);
115
116                 ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
117         }
118         if (ret == -1) {
119                 return unixdom_error(errno);
120         }
121
122         return unixdom_connect_complete(sock, flags);
123 }
124
125 static NTSTATUS unixdom_listen(struct socket_context *sock,
126                                const struct socket_address *my_address, 
127                                int queue_size, uint32_t flags)
128 {
129         struct sockaddr_un my_addr;
130         int ret;
131
132         /* delete if it already exists */
133         if (my_address->addr) {
134                 unlink(my_address->addr);
135         }
136
137         if (my_address->sockaddr) {
138                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
139         } else if (my_address->addr == NULL) {
140                 return NT_STATUS_INVALID_PARAMETER;
141         } else {
142                 if (strlen(my_address->addr)+1 > sizeof(my_addr.sun_path)) {
143                         return NT_STATUS_OBJECT_PATH_INVALID;
144                 }
145                 
146                 
147                 ZERO_STRUCT(my_addr);
148                 my_addr.sun_family = AF_UNIX;
149                 snprintf(my_addr.sun_path, sizeof(my_addr.sun_path), "%s", my_address->addr);
150
151                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
152         }
153         if (ret == -1) {
154                 return unixdom_error(errno);
155         }
156
157         if (sock->type == SOCKET_TYPE_STREAM) {
158                 ret = listen(sock->fd, queue_size);
159                 if (ret == -1) {
160                         return unixdom_error(errno);
161                 }
162         }
163
164         ret = set_blocking(sock->fd, false);
165         if (ret == -1) {
166                 return unixdom_error(errno);
167         }
168
169         sock->state = SOCKET_STATE_SERVER_LISTEN;
170         sock->private_data = (void *)talloc_strdup(sock, my_address->addr);
171
172         return NT_STATUS_OK;
173 }
174
175 static NTSTATUS unixdom_accept(struct socket_context *sock, 
176                                struct socket_context **new_sock)
177 {
178         struct sockaddr_un cli_addr;
179         socklen_t cli_addr_len = sizeof(cli_addr);
180         int new_fd, ret;
181
182         if (sock->type != SOCKET_TYPE_STREAM) {
183                 return NT_STATUS_INVALID_PARAMETER;
184         }
185
186         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
187         if (new_fd == -1) {
188                 return unixdom_error(errno);
189         }
190
191         ret = set_blocking(new_fd, false);
192         if (ret == -1) {
193                 close(new_fd);
194                 return map_nt_error_from_unix_common(errno);
195         }
196
197         smb_set_close_on_exec(new_fd);
198
199         (*new_sock) = talloc(NULL, struct socket_context);
200         if (!(*new_sock)) {
201                 close(new_fd);
202                 return NT_STATUS_NO_MEMORY;
203         }
204
205         /* copy the socket_context */
206         (*new_sock)->type               = sock->type;
207         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
208         (*new_sock)->flags              = sock->flags;
209
210         (*new_sock)->fd                 = new_fd;
211
212         (*new_sock)->private_data       = NULL;
213         (*new_sock)->ops                = sock->ops;
214         (*new_sock)->backend_name       = sock->backend_name;
215
216         return NT_STATUS_OK;
217 }
218
219 static NTSTATUS unixdom_recv(struct socket_context *sock, void *buf, 
220                              size_t wantlen, size_t *nread)
221 {
222         ssize_t gotlen;
223
224         *nread = 0;
225
226         gotlen = recv(sock->fd, buf, wantlen, 0);
227         if (gotlen == 0) {
228                 return NT_STATUS_END_OF_FILE;
229         } else if (gotlen == -1) {
230                 return unixdom_error(errno);
231         }
232
233         *nread = gotlen;
234
235         return NT_STATUS_OK;
236 }
237
238 static NTSTATUS unixdom_send(struct socket_context *sock,
239                              const DATA_BLOB *blob, size_t *sendlen)
240 {
241         ssize_t len;
242
243         *sendlen = 0;
244
245         len = send(sock->fd, blob->data, blob->length, 0);
246         if (len == -1) {
247                 return unixdom_error(errno);
248         }       
249
250         *sendlen = len;
251
252         return NT_STATUS_OK;
253 }
254
255
256 static NTSTATUS unixdom_sendto(struct socket_context *sock, 
257                                const DATA_BLOB *blob, size_t *sendlen, 
258                                const struct socket_address *dest)
259 {
260         struct sockaddr_un srv_addr;
261         const struct sockaddr *sa;
262         socklen_t sa_len;
263         ssize_t len;
264
265         *sendlen = 0;
266
267         if (dest->sockaddr) {
268                 sa = dest->sockaddr;
269                 sa_len = dest->sockaddrlen;
270         } else {
271                 if (strlen(dest->addr)+1 > sizeof(srv_addr.sun_path)) {
272                         return NT_STATUS_OBJECT_PATH_INVALID;
273                 }
274
275                 ZERO_STRUCT(srv_addr);
276                 srv_addr.sun_family = AF_UNIX;
277                 snprintf(srv_addr.sun_path, sizeof(srv_addr.sun_path), "%s",
278                          dest->addr);
279                 sa = (struct sockaddr *) &srv_addr;
280                 sa_len = sizeof(srv_addr);
281         }
282
283         len = sendto(sock->fd, blob->data, blob->length, 0, sa, sa_len);
284
285         /* retry once */
286         if (len == -1 && errno == EMSGSIZE) {
287                 /* round up in 1K increments */
288                 int bufsize = ((blob->length + 1023) & (~1023));
289                 if (setsockopt(sock->fd, SOL_SOCKET, SO_SNDBUF, &bufsize,
290                                sizeof(bufsize)) == -1)
291                 {
292                         return map_nt_error_from_unix_common(EMSGSIZE);
293                 }
294                 len = sendto(sock->fd, blob->data, blob->length, 0, sa, sa_len);
295         }
296
297         if (len == -1) {
298                 return map_nt_error_from_unix_common(errno);
299         }       
300
301         *sendlen = len;
302
303         return NT_STATUS_OK;
304 }
305
306
307 static NTSTATUS unixdom_set_option(struct socket_context *sock, 
308                                    const char *option, const char *val)
309 {
310         return NT_STATUS_OK;
311 }
312
313 static char *unixdom_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
314 {
315         return talloc_strdup(mem_ctx, "LOCAL/unixdom");
316 }
317
318 static struct socket_address *unixdom_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
319 {
320         struct sockaddr_un *peer_addr;
321         socklen_t len = sizeof(*peer_addr);
322         struct socket_address *peer;
323         int ret;
324
325         peer = talloc(mem_ctx, struct socket_address);
326         if (!peer) {
327                 return NULL;
328         }
329         
330         peer->family = sock->backend_name;
331         peer_addr = talloc(peer, struct sockaddr_un);
332         if (!peer_addr) {
333                 talloc_free(peer);
334                 return NULL;
335         }
336
337         peer->sockaddr = (struct sockaddr *)peer_addr;
338
339         ret = getpeername(sock->fd, peer->sockaddr, &len);
340         if (ret == -1) {
341                 talloc_free(peer);
342                 return NULL;
343         }
344
345         peer->sockaddrlen = len;
346
347         peer->port = 0;
348         peer->addr = talloc_strdup(peer, "LOCAL/unixdom");
349         if (!peer->addr) {
350                 talloc_free(peer);
351                 return NULL;
352         }
353
354         return peer;
355 }
356
357 static struct socket_address *unixdom_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
358 {
359         struct sockaddr_un *local_addr;
360         socklen_t len = sizeof(*local_addr);
361         struct socket_address *local;
362         int ret;
363         
364         local = talloc(mem_ctx, struct socket_address);
365         if (!local) {
366                 return NULL;
367         }
368         
369         local->family = sock->backend_name;
370         local_addr = talloc(local, struct sockaddr_un);
371         if (!local_addr) {
372                 talloc_free(local);
373                 return NULL;
374         }
375
376         local->sockaddr = (struct sockaddr *)local_addr;
377
378         ret = getsockname(sock->fd, local->sockaddr, &len);
379         if (ret == -1) {
380                 talloc_free(local);
381                 return NULL;
382         }
383
384         local->sockaddrlen = len;
385
386         local->port = 0;
387         local->addr = talloc_strdup(local, "LOCAL/unixdom");
388         if (!local->addr) {
389                 talloc_free(local);
390                 return NULL;
391         }
392
393         return local;
394 }
395
396 static int unixdom_get_fd(struct socket_context *sock)
397 {
398         return sock->fd;
399 }
400
401 static NTSTATUS unixdom_pending(struct socket_context *sock, size_t *npending)
402 {
403         int value = 0;
404         if (ioctl(sock->fd, FIONREAD, &value) == 0) {
405                 *npending = value;
406                 return NT_STATUS_OK;
407         }
408         return map_nt_error_from_unix_common(errno);
409 }
410
411 static const struct socket_ops unixdom_ops = {
412         .name                   = "unix",
413         .fn_init                = unixdom_init,
414         .fn_connect             = unixdom_connect,
415         .fn_connect_complete    = unixdom_connect_complete,
416         .fn_listen              = unixdom_listen,
417         .fn_accept              = unixdom_accept,
418         .fn_recv                = unixdom_recv,
419         .fn_send                = unixdom_send,
420         .fn_sendto              = unixdom_sendto,
421         .fn_close               = unixdom_close,
422         .fn_pending             = unixdom_pending,
423
424         .fn_set_option          = unixdom_set_option,
425
426         .fn_get_peer_name       = unixdom_get_peer_name,
427         .fn_get_peer_addr       = unixdom_get_peer_addr,
428         .fn_get_my_addr         = unixdom_get_my_addr,
429
430         .fn_get_fd              = unixdom_get_fd
431 };
432
433 _PUBLIC_ const struct socket_ops *socket_unixdom_ops(enum socket_type type)
434 {
435         return &unixdom_ops;
436 }