r23792: convert Samba4 to GPLv3
[garming/samba-autobuild/.git] / source / lib / socket / socket_ipv6.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Socket IPv6 functions
4    Copyright (C) Stefan Metzmacher 2004
5    Copyright (C) Jelmer Vernooij 2004
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "lib/socket/socket.h"
23 #include "system/filesys.h" /* needed for close() */
24 #include "system/network.h"
25
26 static struct in6_addr interpret_addr6(const char *name)
27 {
28         struct hostent *he;
29         
30         if (name == NULL) return in6addr_any;
31
32         if (strcasecmp(name, "localhost") == 0) {
33                 name = "::1";
34         }
35
36         he = gethostbyname2(name, PF_INET6);
37
38         if (he == NULL) return in6addr_any;
39
40         return *((struct in6_addr *)he->h_addr);
41 }
42
43 static NTSTATUS ipv6_tcp_init(struct socket_context *sock)
44 {
45         sock->fd = socket(PF_INET6, SOCK_STREAM, 0);
46         if (sock->fd == -1) {
47                 return map_nt_error_from_unix(errno);
48         }
49
50         sock->backend_name = "ipv6";
51
52         return NT_STATUS_OK;
53 }
54
55 static void ipv6_tcp_close(struct socket_context *sock)
56 {
57         close(sock->fd);
58 }
59
60 static NTSTATUS ipv6_tcp_connect_complete(struct socket_context *sock, uint32_t flags)
61 {
62         int error=0, ret;
63         socklen_t len = sizeof(error);
64
65         /* check for any errors that may have occurred - this is needed
66            for non-blocking connect */
67         ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
68         if (ret == -1) {
69                 return map_nt_error_from_unix(errno);
70         }
71         if (error != 0) {
72                 return map_nt_error_from_unix(error);
73         }
74
75         if (!(flags & SOCKET_FLAG_BLOCK)) {
76                 ret = set_blocking(sock->fd, False);
77                 if (ret == -1) {
78                         return map_nt_error_from_unix(errno);
79                 }
80         }
81
82         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
83
84         return NT_STATUS_OK;
85 }
86
87 static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
88                                  const struct socket_address *my_address,
89                                  const struct socket_address *srv_address,
90                                  uint32_t flags)
91 {
92         int ret;
93
94         if (my_address && my_address->sockaddr) {
95                 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
96                 if (ret == -1) {
97                         return map_nt_error_from_unix(errno);
98                 }
99         } else if (my_address) {
100                 struct in6_addr my_ip;
101                 my_ip = interpret_addr6(my_address->addr);
102
103                 if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_address->port != 0) {
104                         struct sockaddr_in6 my_addr;
105                         ZERO_STRUCT(my_addr);
106                         my_addr.sin6_addr       = my_ip;
107                         my_addr.sin6_port       = htons(my_address->port);
108                         my_addr.sin6_family     = PF_INET6;
109                         
110                         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
111                         if (ret == -1) {
112                                 return map_nt_error_from_unix(errno);
113                         }
114                 }
115         }
116
117         if (srv_address->sockaddr) {
118                 ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
119         } else {
120                 struct in6_addr srv_ip;
121                 struct sockaddr_in6 srv_addr;
122                 srv_ip = interpret_addr6(srv_address->addr);
123                 if (memcmp(&srv_ip, &in6addr_any, sizeof(srv_ip)) == 0) {
124                         return NT_STATUS_BAD_NETWORK_NAME;
125                 }
126                 
127                 ZERO_STRUCT(srv_addr);
128                 srv_addr.sin6_addr      = srv_ip;
129                 srv_addr.sin6_port      = htons(srv_address->port);
130                 srv_addr.sin6_family    = PF_INET6;
131                 
132                 ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
133         }
134         if (ret == -1) {
135                 return map_nt_error_from_unix(errno);
136         }
137
138         return ipv6_tcp_connect_complete(sock, flags);
139 }
140
141 static NTSTATUS ipv6_tcp_listen(struct socket_context *sock,
142                                 const struct socket_address *my_address,
143                                 int queue_size, uint32_t flags)
144 {
145         struct sockaddr_in6 my_addr;
146         struct in6_addr ip_addr;
147         int ret;
148
149         socket_set_option(sock, "SO_REUSEADDR=1", NULL);
150
151         if (my_address->sockaddr) {
152                 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
153         } else {
154                 ip_addr = interpret_addr6(my_address->addr);
155                 
156                 ZERO_STRUCT(my_addr);
157                 my_addr.sin6_addr       = ip_addr;
158                 my_addr.sin6_port       = htons(my_address->port);
159                 my_addr.sin6_family     = PF_INET6;
160                 
161                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
162         }
163
164         if (ret == -1) {
165                 return map_nt_error_from_unix(errno);
166         }
167
168         if (sock->type == SOCKET_TYPE_STREAM) {
169                 ret = listen(sock->fd, queue_size);
170                 if (ret == -1) {
171                         return map_nt_error_from_unix(errno);
172                 }
173         }
174
175         if (!(flags & SOCKET_FLAG_BLOCK)) {
176                 ret = set_blocking(sock->fd, False);
177                 if (ret == -1) {
178                         return map_nt_error_from_unix(errno);
179                 }
180         }
181
182         sock->state= SOCKET_STATE_SERVER_LISTEN;
183
184         return NT_STATUS_OK;
185 }
186
187 static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
188 {
189         struct sockaddr_in cli_addr;
190         socklen_t cli_addr_len = sizeof(cli_addr);
191         int new_fd;
192         
193         if (sock->type != SOCKET_TYPE_STREAM) {
194                 return NT_STATUS_INVALID_PARAMETER;
195         }
196
197
198         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
199         if (new_fd == -1) {
200                 return map_nt_error_from_unix(errno);
201         }
202
203         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
204                 int ret = set_blocking(new_fd, False);
205                 if (ret == -1) {
206                         close(new_fd);
207                         return map_nt_error_from_unix(errno);
208                 }
209         }
210
211         /* TODO: we could add a 'accept_check' hook here
212          *       which get the black/white lists via socket_set_accept_filter()
213          *       or something like that
214          *       --metze
215          */
216
217         (*new_sock) = talloc(NULL, struct socket_context);
218         if (!(*new_sock)) {
219                 close(new_fd);
220                 return NT_STATUS_NO_MEMORY;
221         }
222
223         /* copy the socket_context */
224         (*new_sock)->type               = sock->type;
225         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
226         (*new_sock)->flags              = sock->flags;
227
228         (*new_sock)->fd                 = new_fd;
229
230         (*new_sock)->private_data       = NULL;
231         (*new_sock)->ops                = sock->ops;
232         (*new_sock)->backend_name       = sock->backend_name;
233
234         return NT_STATUS_OK;
235 }
236
237 static NTSTATUS ipv6_tcp_recv(struct socket_context *sock, void *buf, 
238                               size_t wantlen, size_t *nread)
239 {
240         ssize_t gotlen;
241
242         *nread = 0;
243
244         gotlen = recv(sock->fd, buf, wantlen, 0);
245         if (gotlen == 0) {
246                 return NT_STATUS_END_OF_FILE;
247         } else if (gotlen == -1) {
248                 return map_nt_error_from_unix(errno);
249         }
250
251         *nread = gotlen;
252
253         return NT_STATUS_OK;
254 }
255
256 static NTSTATUS ipv6_recvfrom(struct socket_context *sock, void *buf, 
257                               size_t wantlen, size_t *nread, 
258                               TALLOC_CTX *addr_ctx, struct socket_address **_src)
259 {
260         ssize_t gotlen;
261         struct sockaddr_in6 *from_addr;
262         socklen_t from_len = sizeof(*from_addr);
263         struct socket_address *src;
264         struct hostent *he;
265         
266         src = talloc(addr_ctx, struct socket_address);
267         if (!src) {
268                 return NT_STATUS_NO_MEMORY;
269         }
270         
271         src->family = sock->backend_name;
272
273         from_addr = talloc(src, struct sockaddr_in6);
274         if (!from_addr) {
275                 talloc_free(src);
276                 return NT_STATUS_NO_MEMORY;
277         }
278
279         src->sockaddr = (struct sockaddr *)from_addr;
280
281         *nread = 0;
282
283         gotlen = recvfrom(sock->fd, buf, wantlen, 0, 
284                           src->sockaddr, &from_len);
285         if (gotlen == 0) {
286                 talloc_free(src);
287                 return NT_STATUS_END_OF_FILE;
288         } else if (gotlen == -1) {
289                 talloc_free(src);
290                 return map_nt_error_from_unix(errno);
291         }
292
293         src->sockaddrlen = from_len;
294
295         he = gethostbyaddr((void *)&from_addr->sin6_addr, sizeof(from_addr->sin6_addr), AF_INET6);
296         if (he == NULL) {
297                 talloc_free(src);
298                 return NT_STATUS_INTERNAL_ERROR;
299         }
300         src->addr = talloc_strdup(src, he->h_name);
301         if (src->addr == NULL) {
302                 talloc_free(src);
303                 return NT_STATUS_NO_MEMORY;
304         }
305         src->port = ntohs(from_addr->sin6_port);
306
307         *nread  = gotlen;
308         *_src   = src;
309         return NT_STATUS_OK;
310 }
311
312 static NTSTATUS ipv6_tcp_send(struct socket_context *sock, 
313                               const DATA_BLOB *blob, size_t *sendlen)
314 {
315         ssize_t len;
316
317         *sendlen = 0;
318
319         len = send(sock->fd, blob->data, blob->length, 0);
320         if (len == -1) {
321                 return map_nt_error_from_unix(errno);
322         }       
323
324         *sendlen = len;
325
326         return NT_STATUS_OK;
327 }
328
329 static NTSTATUS ipv6_sendto(struct socket_context *sock, 
330                             const DATA_BLOB *blob, size_t *sendlen, 
331                             const struct socket_address *dest_addr)
332 {
333         ssize_t len;
334
335         if (dest_addr->sockaddr) {
336                 len = sendto(sock->fd, blob->data, blob->length, 0, 
337                              dest_addr->sockaddr, dest_addr->sockaddrlen);
338         } else {
339                 struct sockaddr_in6 srv_addr;
340                 struct in6_addr addr;
341                 
342                 ZERO_STRUCT(srv_addr);
343                 addr                     = interpret_addr6(dest_addr->addr);
344                 if (addr.s6_addr == 0) {
345                         return NT_STATUS_HOST_UNREACHABLE;
346                 }
347                 srv_addr.sin6_addr = addr;
348                 srv_addr.sin6_port        = htons(dest_addr->port);
349                 srv_addr.sin6_family      = PF_INET6;
350                 
351                 *sendlen = 0;
352                 
353                 len = sendto(sock->fd, blob->data, blob->length, 0, 
354                              (struct sockaddr *)&srv_addr, sizeof(srv_addr));
355         }
356         if (len == -1) {
357                 return map_nt_error_from_unix(errno);
358         }       
359
360         *sendlen = len;
361
362         return NT_STATUS_OK;
363 }
364
365 static NTSTATUS ipv6_tcp_set_option(struct socket_context *sock, const char *option, const char *val)
366 {
367         set_socket_options(sock->fd, option);
368         return NT_STATUS_OK;
369 }
370
371 static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
372 {
373         struct sockaddr_in6 peer_addr;
374         socklen_t len = sizeof(peer_addr);
375         struct hostent *he;
376         int ret;
377
378         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
379         if (ret == -1) {
380                 return NULL;
381         }
382
383         he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
384         if (he == NULL) {
385                 return NULL;
386         }
387
388         return talloc_strdup(mem_ctx, he->h_name);
389 }
390
391 static struct socket_address *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
392 {
393         struct sockaddr_in6 *peer_addr;
394         socklen_t len = sizeof(*peer_addr);
395         struct socket_address *peer;
396         int ret;
397         char addr[128];
398         const char *addr_ret;
399         
400         peer = talloc(mem_ctx, struct socket_address);
401         if (!peer) {
402                 return NULL;
403         }
404         
405         peer->family = sock->backend_name;
406         peer_addr = talloc(peer, struct sockaddr_in6);
407         if (!peer_addr) {
408                 talloc_free(peer);
409                 return NULL;
410         }
411
412         peer->sockaddr = (struct sockaddr *)peer_addr;
413
414         ret = getpeername(sock->fd, peer->sockaddr, &len);
415         if (ret == -1) {
416                 talloc_free(peer);
417                 return NULL;
418         }
419
420         peer->sockaddrlen = len;
421
422         addr_ret = inet_ntop(AF_INET6, &peer_addr->sin6_addr, addr, sizeof(addr));
423         if (addr_ret == NULL) {
424                 talloc_free(peer);
425                 return NULL;
426         }
427
428         peer->addr = talloc_strdup(peer, addr_ret);
429         if (peer->addr == NULL) {
430                 talloc_free(peer);
431                 return NULL;
432         }
433
434         peer->port = ntohs(peer_addr->sin6_port);
435
436         return peer;
437 }
438
439 static struct socket_address *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
440 {
441         struct sockaddr_in6 *local_addr;
442         socklen_t len = sizeof(*local_addr);
443         struct socket_address *local;
444         int ret;
445         struct hostent *he;
446         
447         local = talloc(mem_ctx, struct socket_address);
448         if (!local) {
449                 return NULL;
450         }
451         
452         local->family = sock->backend_name;
453         local_addr = talloc(local, struct sockaddr_in6);
454         if (!local_addr) {
455                 talloc_free(local);
456                 return NULL;
457         }
458
459         local->sockaddr = (struct sockaddr *)local_addr;
460
461         ret = getsockname(sock->fd, local->sockaddr, &len);
462         if (ret == -1) {
463                 talloc_free(local);
464                 return NULL;
465         }
466
467         local->sockaddrlen = len;
468
469         he = gethostbyaddr((char *)&local_addr->sin6_addr, len, AF_INET6);
470
471         if (!he || !he->h_name) {
472                 talloc_free(local);
473                 return NULL;
474         }
475         
476         local->addr = talloc_strdup(mem_ctx, he->h_name);
477         if (!local->addr) {
478                 talloc_free(local);
479                 return NULL;
480         }
481         local->port = ntohs(local_addr->sin6_port);
482
483         return local;
484 }
485
486 static int ipv6_tcp_get_fd(struct socket_context *sock)
487 {
488         return sock->fd;
489 }
490
491 static NTSTATUS ipv6_pending(struct socket_context *sock, size_t *npending)
492 {
493         int value = 0;
494         if (ioctl(sock->fd, FIONREAD, &value) == 0) {
495                 *npending = value;
496                 return NT_STATUS_OK;
497         }
498         return map_nt_error_from_unix(errno);
499 }
500
501 static const struct socket_ops ipv6_tcp_ops = {
502         .name                   = "ipv6",
503         .fn_init                = ipv6_tcp_init,
504         .fn_connect             = ipv6_tcp_connect,
505         .fn_connect_complete    = ipv6_tcp_connect_complete,
506         .fn_listen              = ipv6_tcp_listen,
507         .fn_accept              = ipv6_tcp_accept,
508         .fn_recv                = ipv6_tcp_recv,
509         .fn_recvfrom    = ipv6_recvfrom,
510         .fn_sendto              = ipv6_sendto,
511         .fn_send                = ipv6_tcp_send,
512         .fn_close               = ipv6_tcp_close,
513         .fn_pending             = ipv6_pending,
514
515         .fn_set_option          = ipv6_tcp_set_option,
516
517         .fn_get_peer_name       = ipv6_tcp_get_peer_name,
518         .fn_get_peer_addr       = ipv6_tcp_get_peer_addr,
519         .fn_get_my_addr         = ipv6_tcp_get_my_addr,
520
521         .fn_get_fd              = ipv6_tcp_get_fd
522 };
523
524 const struct socket_ops *socket_ipv6_ops(enum socket_type type)
525 {
526         return &ipv6_tcp_ops;
527 }