r24939: Integrate IPv4 and IPv6 modules (a lot of code can be shared).
[nivanova/samba-autobuild/.git] / source4 / lib / socket / socket_ip.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Socket IPv4/IPv6 functions
5
6    Copyright (C) Stefan Metzmacher 2004
7    Copyright (C) Andrew Tridgell 2004-2005
8    Copyright (C) Jelmer Vernooij 2004
9    
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14    
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19    
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "lib/socket/socket.h"
27 #include "system/network.h"
28
29 static NTSTATUS ipv4_init(struct socket_context *sock)
30 {
31         int type;
32
33         switch (sock->type) {
34         case SOCKET_TYPE_STREAM:
35                 type = SOCK_STREAM;
36                 break;
37         case SOCKET_TYPE_DGRAM:
38                 type = SOCK_DGRAM;
39                 break;
40         default:
41                 return NT_STATUS_INVALID_PARAMETER;
42         }
43
44         sock->fd = socket(PF_INET, type, 0);
45         if (sock->fd == -1) {
46                 return map_nt_error_from_unix(errno);
47         }
48
49         sock->backend_name = "ipv4";
50
51         return NT_STATUS_OK;
52 }
53
54 static void ip_close(struct socket_context *sock)
55 {
56         close(sock->fd);
57 }
58
59 static NTSTATUS ip_connect_complete(struct socket_context *sock, uint32_t flags)
60 {
61         int error=0, ret;
62         socklen_t len = sizeof(error);
63
64         /* check for any errors that may have occurred - this is needed
65            for non-blocking connect */
66         ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
67         if (ret == -1) {
68                 return map_nt_error_from_unix(errno);
69         }
70         if (error != 0) {
71                 return map_nt_error_from_unix(error);
72         }
73
74         if (!(flags & SOCKET_FLAG_BLOCK)) {
75                 ret = set_blocking(sock->fd, False);
76                 if (ret == -1) {
77                         return map_nt_error_from_unix(errno);
78                 }
79         }
80
81         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
82
83         return NT_STATUS_OK;
84 }
85
86
87 static NTSTATUS ipv4_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         struct sockaddr_in srv_addr;
93         struct ipv4_addr my_ip;
94         struct ipv4_addr srv_ip;
95         int ret;
96
97         if (my_address && my_address->sockaddr) {
98                 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
99                 if (ret == -1) {
100                         return map_nt_error_from_unix(errno);
101                 }
102         } else if (my_address) {
103                 my_ip = interpret_addr2(my_address->addr);
104                 
105                 if (my_ip.addr != 0 || my_address->port != 0) {
106                         struct sockaddr_in my_addr;
107                         ZERO_STRUCT(my_addr);
108 #ifdef HAVE_SOCK_SIN_LEN
109                         my_addr.sin_len         = sizeof(my_addr);
110 #endif
111                         my_addr.sin_addr.s_addr = my_ip.addr;
112                         my_addr.sin_port        = htons(my_address->port);
113                         my_addr.sin_family      = PF_INET;
114                         
115                         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
116                         if (ret == -1) {
117                                 return map_nt_error_from_unix(errno);
118                         }
119                 }
120         }
121
122         if (srv_address->sockaddr) {
123                 ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
124                 if (ret == -1) {
125                         return map_nt_error_from_unix(errno);
126                 }
127         } else {
128                 srv_ip = interpret_addr2(srv_address->addr);
129                 if (!srv_ip.addr) {
130                         return NT_STATUS_BAD_NETWORK_NAME;
131                 }
132                 
133                 ZERO_STRUCT(srv_addr);
134 #ifdef HAVE_SOCK_SIN_LEN
135                 srv_addr.sin_len        = sizeof(srv_addr);
136 #endif
137                 srv_addr.sin_addr.s_addr= srv_ip.addr;
138                 srv_addr.sin_port       = htons(srv_address->port);
139                 srv_addr.sin_family     = PF_INET;
140
141                 ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
142                 if (ret == -1) {
143                         return map_nt_error_from_unix(errno);
144                 }
145         }
146
147         return ip_connect_complete(sock, flags);
148 }
149
150
151 /*
152   note that for simplicity of the API, socket_listen() is also
153   use for DGRAM sockets, but in reality only a bind() is done
154 */
155 static NTSTATUS ipv4_listen(struct socket_context *sock,
156                             const struct socket_address *my_address, 
157                             int queue_size, uint32_t flags)
158 {
159         struct sockaddr_in my_addr;
160         struct ipv4_addr ip_addr;
161         int ret;
162
163         socket_set_option(sock, "SO_REUSEADDR=1", NULL);
164
165         if (my_address->sockaddr) {
166                 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
167         } else {
168                 ip_addr = interpret_addr2(my_address->addr);
169                 
170                 ZERO_STRUCT(my_addr);
171 #ifdef HAVE_SOCK_SIN_LEN
172                 my_addr.sin_len         = sizeof(my_addr);
173 #endif
174                 my_addr.sin_addr.s_addr = ip_addr.addr;
175                 my_addr.sin_port        = htons(my_address->port);
176                 my_addr.sin_family      = PF_INET;
177                 
178                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
179         }
180
181         if (ret == -1) {
182                 return map_nt_error_from_unix(errno);
183         }
184
185         if (sock->type == SOCKET_TYPE_STREAM) {
186                 ret = listen(sock->fd, queue_size);
187                 if (ret == -1) {
188                         return map_nt_error_from_unix(errno);
189                 }
190         }
191
192         if (!(flags & SOCKET_FLAG_BLOCK)) {
193                 ret = set_blocking(sock->fd, False);
194                 if (ret == -1) {
195                         return map_nt_error_from_unix(errno);
196                 }
197         }
198
199         sock->state= SOCKET_STATE_SERVER_LISTEN;
200
201         return NT_STATUS_OK;
202 }
203
204 static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context **new_sock)
205 {
206         struct sockaddr_in cli_addr;
207         socklen_t cli_addr_len = sizeof(cli_addr);
208         int new_fd;
209
210         if (sock->type != SOCKET_TYPE_STREAM) {
211                 return NT_STATUS_INVALID_PARAMETER;
212         }
213
214         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
215         if (new_fd == -1) {
216                 return map_nt_error_from_unix(errno);
217         }
218
219         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
220                 int ret = set_blocking(new_fd, False);
221                 if (ret == -1) {
222                         close(new_fd);
223                         return map_nt_error_from_unix(errno);
224                 }
225         }
226
227         /* TODO: we could add a 'accept_check' hook here
228          *       which get the black/white lists via socket_set_accept_filter()
229          *       or something like that
230          *       --metze
231          */
232
233         (*new_sock) = talloc(NULL, struct socket_context);
234         if (!(*new_sock)) {
235                 close(new_fd);
236                 return NT_STATUS_NO_MEMORY;
237         }
238
239         /* copy the socket_context */
240         (*new_sock)->type               = sock->type;
241         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
242         (*new_sock)->flags              = sock->flags;
243
244         (*new_sock)->fd                 = new_fd;
245
246         (*new_sock)->private_data       = NULL;
247         (*new_sock)->ops                = sock->ops;
248         (*new_sock)->backend_name       = sock->backend_name;
249
250         return NT_STATUS_OK;
251 }
252
253 static NTSTATUS ip_recv(struct socket_context *sock, void *buf, 
254                               size_t wantlen, size_t *nread)
255 {
256         ssize_t gotlen;
257
258         *nread = 0;
259
260         gotlen = recv(sock->fd, buf, wantlen, 0);
261         if (gotlen == 0) {
262                 return NT_STATUS_END_OF_FILE;
263         } else if (gotlen == -1) {
264                 return map_nt_error_from_unix(errno);
265         }
266
267         *nread = gotlen;
268
269         return NT_STATUS_OK;
270 }
271
272
273 static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf, 
274                               size_t wantlen, size_t *nread, 
275                               TALLOC_CTX *addr_ctx, struct socket_address **_src)
276 {
277         ssize_t gotlen;
278         struct sockaddr_in *from_addr;
279         socklen_t from_len = sizeof(*from_addr);
280         struct socket_address *src;
281         const char *addr;
282         
283         src = talloc(addr_ctx, struct socket_address);
284         if (!src) {
285                 return NT_STATUS_NO_MEMORY;
286         }
287         
288         src->family = sock->backend_name;
289
290         from_addr = talloc(src, struct sockaddr_in);
291         if (!from_addr) {
292                 talloc_free(src);
293                 return NT_STATUS_NO_MEMORY;
294         }
295
296         src->sockaddr = (struct sockaddr *)from_addr;
297
298         *nread = 0;
299
300         gotlen = recvfrom(sock->fd, buf, wantlen, 0, 
301                           src->sockaddr, &from_len);
302         if (gotlen == 0) {
303                 talloc_free(src);
304                 return NT_STATUS_END_OF_FILE;
305         } else if (gotlen == -1) {
306                 talloc_free(src);
307                 return map_nt_error_from_unix(errno);
308         }
309
310         src->sockaddrlen = from_len;
311
312         addr = inet_ntoa(from_addr->sin_addr);
313         if (addr == NULL) {
314                 talloc_free(src);
315                 return NT_STATUS_INTERNAL_ERROR;
316         }
317         src->addr = talloc_strdup(src, addr);
318         if (src->addr == NULL) {
319                 talloc_free(src);
320                 return NT_STATUS_NO_MEMORY;
321         }
322         src->port = ntohs(from_addr->sin_port);
323
324         *nread  = gotlen;
325         *_src   = src;
326         return NT_STATUS_OK;
327 }
328
329 static NTSTATUS ip_send(struct socket_context *sock, 
330                               const DATA_BLOB *blob, size_t *sendlen)
331 {
332         ssize_t len;
333
334         *sendlen = 0;
335
336         len = send(sock->fd, blob->data, blob->length, 0);
337         if (len == -1) {
338                 return map_nt_error_from_unix(errno);
339         }       
340
341         *sendlen = len;
342
343         return NT_STATUS_OK;
344 }
345
346 static NTSTATUS ipv4_sendto(struct socket_context *sock, 
347                             const DATA_BLOB *blob, size_t *sendlen, 
348                             const struct socket_address *dest_addr)
349 {
350         ssize_t len;
351
352         if (dest_addr->sockaddr) {
353                 len = sendto(sock->fd, blob->data, blob->length, 0, 
354                              dest_addr->sockaddr, dest_addr->sockaddrlen);
355         } else {
356                 struct sockaddr_in srv_addr;
357                 struct ipv4_addr addr;
358                 
359                 ZERO_STRUCT(srv_addr);
360 #ifdef HAVE_SOCK_SIN_LEN
361                 srv_addr.sin_len         = sizeof(srv_addr);
362 #endif
363                 addr                     = interpret_addr2(dest_addr->addr);
364                 if (addr.addr == 0) {
365                         return NT_STATUS_HOST_UNREACHABLE;
366                 }
367                 srv_addr.sin_addr.s_addr = addr.addr;
368                 srv_addr.sin_port        = htons(dest_addr->port);
369                 srv_addr.sin_family      = PF_INET;
370                 
371                 *sendlen = 0;
372                 
373                 len = sendto(sock->fd, blob->data, blob->length, 0, 
374                              (struct sockaddr *)&srv_addr, sizeof(srv_addr));
375         }
376         if (len == -1) {
377                 return map_nt_error_from_unix(errno);
378         }       
379
380         *sendlen = len;
381
382         return NT_STATUS_OK;
383 }
384
385 static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
386 {
387         set_socket_options(sock->fd, option);
388         return NT_STATUS_OK;
389 }
390
391 static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
392 {
393         struct sockaddr_in peer_addr;
394         socklen_t len = sizeof(peer_addr);
395         struct hostent *he;
396         int ret;
397
398         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
399         if (ret == -1) {
400                 return NULL;
401         }
402
403         he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
404         if (he == NULL) {
405                 return NULL;
406         }
407
408         return talloc_strdup(mem_ctx, he->h_name);
409 }
410
411 static struct socket_address *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
412 {
413         struct sockaddr_in *peer_addr;
414         socklen_t len = sizeof(*peer_addr);
415         const char *addr;
416         struct socket_address *peer;
417         int ret;
418         
419         peer = talloc(mem_ctx, struct socket_address);
420         if (!peer) {
421                 return NULL;
422         }
423         
424         peer->family = sock->backend_name;
425         peer_addr = talloc(peer, struct sockaddr_in);
426         if (!peer_addr) {
427                 talloc_free(peer);
428                 return NULL;
429         }
430
431         peer->sockaddr = (struct sockaddr *)peer_addr;
432
433         ret = getpeername(sock->fd, peer->sockaddr, &len);
434         if (ret == -1) {
435                 talloc_free(peer);
436                 return NULL;
437         }
438
439         peer->sockaddrlen = len;
440
441         addr = inet_ntoa(peer_addr->sin_addr);
442         if (addr == NULL) {
443                 talloc_free(peer);
444                 return NULL;
445         }
446         peer->addr = talloc_strdup(peer, addr);
447         if (!peer->addr) {
448                 talloc_free(peer);
449                 return NULL;
450         }
451         peer->port = ntohs(peer_addr->sin_port);
452
453         return peer;
454 }
455
456 static struct socket_address *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
457 {
458         struct sockaddr_in *local_addr;
459         socklen_t len = sizeof(*local_addr);
460         const char *addr;
461         struct socket_address *local;
462         int ret;
463         
464         local = talloc(mem_ctx, struct socket_address);
465         if (!local) {
466                 return NULL;
467         }
468         
469         local->family = sock->backend_name;
470         local_addr = talloc(local, struct sockaddr_in);
471         if (!local_addr) {
472                 talloc_free(local);
473                 return NULL;
474         }
475
476         local->sockaddr = (struct sockaddr *)local_addr;
477
478         ret = getsockname(sock->fd, local->sockaddr, &len);
479         if (ret == -1) {
480                 talloc_free(local);
481                 return NULL;
482         }
483
484         local->sockaddrlen = len;
485
486         addr = inet_ntoa(local_addr->sin_addr);
487         if (addr == NULL) {
488                 talloc_free(local);
489                 return NULL;
490         }
491         local->addr = talloc_strdup(local, addr);
492         if (!local->addr) {
493                 talloc_free(local);
494                 return NULL;
495         }
496         local->port = ntohs(local_addr->sin_port);
497
498         return local;
499 }
500 static int ip_get_fd(struct socket_context *sock)
501 {
502         return sock->fd;
503 }
504
505 static NTSTATUS ip_pending(struct socket_context *sock, size_t *npending)
506 {
507         int value = 0;
508         if (ioctl(sock->fd, FIONREAD, &value) == 0) {
509                 *npending = value;
510                 return NT_STATUS_OK;
511         }
512         return map_nt_error_from_unix(errno);
513 }
514
515 static const struct socket_ops ipv4_ops = {
516         .name                   = "ipv4",
517         .fn_init                = ipv4_init,
518         .fn_connect             = ipv4_connect,
519         .fn_connect_complete    = ip_connect_complete,
520         .fn_listen              = ipv4_listen,
521         .fn_accept              = ipv4_accept,
522         .fn_recv                = ip_recv,
523         .fn_recvfrom            = ipv4_recvfrom,
524         .fn_send                = ip_send,
525         .fn_sendto              = ipv4_sendto,
526         .fn_pending             = ip_pending,
527         .fn_close               = ip_close,
528
529         .fn_set_option          = ipv4_set_option,
530
531         .fn_get_peer_name       = ipv4_get_peer_name,
532         .fn_get_peer_addr       = ipv4_get_peer_addr,
533         .fn_get_my_addr         = ipv4_get_my_addr,
534         .fn_get_fd              = ip_get_fd
535 };
536
537 const struct socket_ops *socket_ipv4_ops(enum socket_type type)
538 {
539         return &ipv4_ops;
540 }
541
542 #if HAVE_IPV6
543
544 static struct in6_addr interpret_addr6(const char *name)
545 {
546         struct hostent *he;
547         
548         if (name == NULL) return in6addr_any;
549
550         if (strcasecmp(name, "localhost") == 0) {
551                 name = "::1";
552         }
553
554         he = gethostbyname2(name, PF_INET6);
555
556         if (he == NULL) return in6addr_any;
557
558         return *((struct in6_addr *)he->h_addr);
559 }
560
561 static NTSTATUS ipv6_tcp_init(struct socket_context *sock)
562 {
563         sock->fd = socket(PF_INET6, SOCK_STREAM, 0);
564         if (sock->fd == -1) {
565                 return map_nt_error_from_unix(errno);
566         }
567
568         sock->backend_name = "ipv6";
569
570         return NT_STATUS_OK;
571 }
572
573 static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
574                                  const struct socket_address *my_address,
575                                  const struct socket_address *srv_address,
576                                  uint32_t flags)
577 {
578         int ret;
579
580         if (my_address && my_address->sockaddr) {
581                 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
582                 if (ret == -1) {
583                         return map_nt_error_from_unix(errno);
584                 }
585         } else if (my_address) {
586                 struct in6_addr my_ip;
587                 my_ip = interpret_addr6(my_address->addr);
588
589                 if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_address->port != 0) {
590                         struct sockaddr_in6 my_addr;
591                         ZERO_STRUCT(my_addr);
592                         my_addr.sin6_addr       = my_ip;
593                         my_addr.sin6_port       = htons(my_address->port);
594                         my_addr.sin6_family     = PF_INET6;
595                         
596                         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
597                         if (ret == -1) {
598                                 return map_nt_error_from_unix(errno);
599                         }
600                 }
601         }
602
603         if (srv_address->sockaddr) {
604                 ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
605         } else {
606                 struct in6_addr srv_ip;
607                 struct sockaddr_in6 srv_addr;
608                 srv_ip = interpret_addr6(srv_address->addr);
609                 if (memcmp(&srv_ip, &in6addr_any, sizeof(srv_ip)) == 0) {
610                         return NT_STATUS_BAD_NETWORK_NAME;
611                 }
612                 
613                 ZERO_STRUCT(srv_addr);
614                 srv_addr.sin6_addr      = srv_ip;
615                 srv_addr.sin6_port      = htons(srv_address->port);
616                 srv_addr.sin6_family    = PF_INET6;
617                 
618                 ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
619         }
620         if (ret == -1) {
621                 return map_nt_error_from_unix(errno);
622         }
623
624         return ip_connect_complete(sock, flags);
625 }
626
627 static NTSTATUS ipv6_tcp_listen(struct socket_context *sock,
628                                 const struct socket_address *my_address,
629                                 int queue_size, uint32_t flags)
630 {
631         struct sockaddr_in6 my_addr;
632         struct in6_addr ip_addr;
633         int ret;
634
635         socket_set_option(sock, "SO_REUSEADDR=1", NULL);
636
637         if (my_address->sockaddr) {
638                 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
639         } else {
640                 ip_addr = interpret_addr6(my_address->addr);
641                 
642                 ZERO_STRUCT(my_addr);
643                 my_addr.sin6_addr       = ip_addr;
644                 my_addr.sin6_port       = htons(my_address->port);
645                 my_addr.sin6_family     = PF_INET6;
646                 
647                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
648         }
649
650         if (ret == -1) {
651                 return map_nt_error_from_unix(errno);
652         }
653
654         if (sock->type == SOCKET_TYPE_STREAM) {
655                 ret = listen(sock->fd, queue_size);
656                 if (ret == -1) {
657                         return map_nt_error_from_unix(errno);
658                 }
659         }
660
661         if (!(flags & SOCKET_FLAG_BLOCK)) {
662                 ret = set_blocking(sock->fd, False);
663                 if (ret == -1) {
664                         return map_nt_error_from_unix(errno);
665                 }
666         }
667
668         sock->state= SOCKET_STATE_SERVER_LISTEN;
669
670         return NT_STATUS_OK;
671 }
672
673 static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
674 {
675         struct sockaddr_in cli_addr;
676         socklen_t cli_addr_len = sizeof(cli_addr);
677         int new_fd;
678         
679         if (sock->type != SOCKET_TYPE_STREAM) {
680                 return NT_STATUS_INVALID_PARAMETER;
681         }
682
683
684         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
685         if (new_fd == -1) {
686                 return map_nt_error_from_unix(errno);
687         }
688
689         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
690                 int ret = set_blocking(new_fd, False);
691                 if (ret == -1) {
692                         close(new_fd);
693                         return map_nt_error_from_unix(errno);
694                 }
695         }
696
697         /* TODO: we could add a 'accept_check' hook here
698          *       which get the black/white lists via socket_set_accept_filter()
699          *       or something like that
700          *       --metze
701          */
702
703         (*new_sock) = talloc(NULL, struct socket_context);
704         if (!(*new_sock)) {
705                 close(new_fd);
706                 return NT_STATUS_NO_MEMORY;
707         }
708
709         /* copy the socket_context */
710         (*new_sock)->type               = sock->type;
711         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
712         (*new_sock)->flags              = sock->flags;
713
714         (*new_sock)->fd                 = new_fd;
715
716         (*new_sock)->private_data       = NULL;
717         (*new_sock)->ops                = sock->ops;
718         (*new_sock)->backend_name       = sock->backend_name;
719
720         return NT_STATUS_OK;
721 }
722
723 static NTSTATUS ipv6_recvfrom(struct socket_context *sock, void *buf, 
724                               size_t wantlen, size_t *nread, 
725                               TALLOC_CTX *addr_ctx, struct socket_address **_src)
726 {
727         ssize_t gotlen;
728         struct sockaddr_in6 *from_addr;
729         socklen_t from_len = sizeof(*from_addr);
730         struct socket_address *src;
731         struct hostent *he;
732         
733         src = talloc(addr_ctx, struct socket_address);
734         if (!src) {
735                 return NT_STATUS_NO_MEMORY;
736         }
737         
738         src->family = sock->backend_name;
739
740         from_addr = talloc(src, struct sockaddr_in6);
741         if (!from_addr) {
742                 talloc_free(src);
743                 return NT_STATUS_NO_MEMORY;
744         }
745
746         src->sockaddr = (struct sockaddr *)from_addr;
747
748         *nread = 0;
749
750         gotlen = recvfrom(sock->fd, buf, wantlen, 0, 
751                           src->sockaddr, &from_len);
752         if (gotlen == 0) {
753                 talloc_free(src);
754                 return NT_STATUS_END_OF_FILE;
755         } else if (gotlen == -1) {
756                 talloc_free(src);
757                 return map_nt_error_from_unix(errno);
758         }
759
760         src->sockaddrlen = from_len;
761
762         he = gethostbyaddr((void *)&from_addr->sin6_addr, sizeof(from_addr->sin6_addr), AF_INET6);
763         if (he == NULL) {
764                 talloc_free(src);
765                 return NT_STATUS_INTERNAL_ERROR;
766         }
767         src->addr = talloc_strdup(src, he->h_name);
768         if (src->addr == NULL) {
769                 talloc_free(src);
770                 return NT_STATUS_NO_MEMORY;
771         }
772         src->port = ntohs(from_addr->sin6_port);
773
774         *nread  = gotlen;
775         *_src   = src;
776         return NT_STATUS_OK;
777 }
778
779 static NTSTATUS ipv6_sendto(struct socket_context *sock, 
780                             const DATA_BLOB *blob, size_t *sendlen, 
781                             const struct socket_address *dest_addr)
782 {
783         ssize_t len;
784
785         if (dest_addr->sockaddr) {
786                 len = sendto(sock->fd, blob->data, blob->length, 0, 
787                              dest_addr->sockaddr, dest_addr->sockaddrlen);
788         } else {
789                 struct sockaddr_in6 srv_addr;
790                 struct in6_addr addr;
791                 
792                 ZERO_STRUCT(srv_addr);
793                 addr                     = interpret_addr6(dest_addr->addr);
794                 if (addr.s6_addr == 0) {
795                         return NT_STATUS_HOST_UNREACHABLE;
796                 }
797                 srv_addr.sin6_addr = addr;
798                 srv_addr.sin6_port        = htons(dest_addr->port);
799                 srv_addr.sin6_family      = PF_INET6;
800                 
801                 *sendlen = 0;
802                 
803                 len = sendto(sock->fd, blob->data, blob->length, 0, 
804                              (struct sockaddr *)&srv_addr, sizeof(srv_addr));
805         }
806         if (len == -1) {
807                 return map_nt_error_from_unix(errno);
808         }       
809
810         *sendlen = len;
811
812         return NT_STATUS_OK;
813 }
814
815 static NTSTATUS ipv6_tcp_set_option(struct socket_context *sock, const char *option, const char *val)
816 {
817         set_socket_options(sock->fd, option);
818         return NT_STATUS_OK;
819 }
820
821 static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
822 {
823         struct sockaddr_in6 peer_addr;
824         socklen_t len = sizeof(peer_addr);
825         struct hostent *he;
826         int ret;
827
828         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
829         if (ret == -1) {
830                 return NULL;
831         }
832
833         he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
834         if (he == NULL) {
835                 return NULL;
836         }
837
838         return talloc_strdup(mem_ctx, he->h_name);
839 }
840
841 static struct socket_address *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
842 {
843         struct sockaddr_in6 *peer_addr;
844         socklen_t len = sizeof(*peer_addr);
845         struct socket_address *peer;
846         int ret;
847         char addr[128];
848         const char *addr_ret;
849         
850         peer = talloc(mem_ctx, struct socket_address);
851         if (!peer) {
852                 return NULL;
853         }
854         
855         peer->family = sock->backend_name;
856         peer_addr = talloc(peer, struct sockaddr_in6);
857         if (!peer_addr) {
858                 talloc_free(peer);
859                 return NULL;
860         }
861
862         peer->sockaddr = (struct sockaddr *)peer_addr;
863
864         ret = getpeername(sock->fd, peer->sockaddr, &len);
865         if (ret == -1) {
866                 talloc_free(peer);
867                 return NULL;
868         }
869
870         peer->sockaddrlen = len;
871
872         addr_ret = inet_ntop(AF_INET6, &peer_addr->sin6_addr, addr, sizeof(addr));
873         if (addr_ret == NULL) {
874                 talloc_free(peer);
875                 return NULL;
876         }
877
878         peer->addr = talloc_strdup(peer, addr_ret);
879         if (peer->addr == NULL) {
880                 talloc_free(peer);
881                 return NULL;
882         }
883
884         peer->port = ntohs(peer_addr->sin6_port);
885
886         return peer;
887 }
888
889 static struct socket_address *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
890 {
891         struct sockaddr_in6 *local_addr;
892         socklen_t len = sizeof(*local_addr);
893         struct socket_address *local;
894         int ret;
895         struct hostent *he;
896         
897         local = talloc(mem_ctx, struct socket_address);
898         if (!local) {
899                 return NULL;
900         }
901         
902         local->family = sock->backend_name;
903         local_addr = talloc(local, struct sockaddr_in6);
904         if (!local_addr) {
905                 talloc_free(local);
906                 return NULL;
907         }
908
909         local->sockaddr = (struct sockaddr *)local_addr;
910
911         ret = getsockname(sock->fd, local->sockaddr, &len);
912         if (ret == -1) {
913                 talloc_free(local);
914                 return NULL;
915         }
916
917         local->sockaddrlen = len;
918
919         he = gethostbyaddr((char *)&local_addr->sin6_addr, len, AF_INET6);
920
921         if (!he || !he->h_name) {
922                 talloc_free(local);
923                 return NULL;
924         }
925         
926         local->addr = talloc_strdup(mem_ctx, he->h_name);
927         if (!local->addr) {
928                 talloc_free(local);
929                 return NULL;
930         }
931         local->port = ntohs(local_addr->sin6_port);
932
933         return local;
934 }
935
936 static const struct socket_ops ipv6_tcp_ops = {
937         .name                   = "ipv6",
938         .fn_init                = ipv6_tcp_init,
939         .fn_connect             = ipv6_tcp_connect,
940         .fn_connect_complete    = ip_connect_complete,
941         .fn_listen              = ipv6_tcp_listen,
942         .fn_accept              = ipv6_tcp_accept,
943         .fn_recv                = ip_recv,
944         .fn_recvfrom            = ipv6_recvfrom,
945         .fn_sendto              = ipv6_sendto,
946         .fn_send                = ip_send,
947         .fn_close               = ip_close,
948         .fn_pending             = ip_pending,
949
950         .fn_set_option          = ipv6_tcp_set_option,
951
952         .fn_get_peer_name       = ipv6_tcp_get_peer_name,
953         .fn_get_peer_addr       = ipv6_tcp_get_peer_addr,
954         .fn_get_my_addr         = ipv6_tcp_get_my_addr,
955
956         .fn_get_fd              = ip_get_fd
957 };
958
959 const struct socket_ops *socket_ipv6_ops(enum socket_type type)
960 {
961         return &ipv6_tcp_ops;
962 }
963
964 #endif