8d561be55fa34714c5fe314ca30d8aee9d5febc7
[bbaumbach/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
535         .fn_get_fd              = ip_get_fd
536 };
537
538 const struct socket_ops *socket_ipv4_ops(enum socket_type type)
539 {
540         return &ipv4_ops;
541 }
542
543 #if HAVE_IPV6
544
545 static struct in6_addr interpret_addr6(const char *name)
546 {
547         struct hostent *he;
548         
549         if (name == NULL) return in6addr_any;
550
551         if (strcasecmp(name, "localhost") == 0) {
552                 name = "::1";
553         }
554
555         he = gethostbyname2(name, PF_INET6);
556
557         if (he == NULL) return in6addr_any;
558
559         return *((struct in6_addr *)he->h_addr);
560 }
561
562 static NTSTATUS ipv6_init(struct socket_context *sock)
563 {
564         int type;
565
566         switch (sock->type) {
567         case SOCKET_TYPE_STREAM:
568                 type = SOCK_STREAM;
569                 break;
570         case SOCKET_TYPE_DGRAM:
571                 type = SOCK_DGRAM;
572                 break;
573         default:
574                 return NT_STATUS_INVALID_PARAMETER;
575         }
576
577         sock->fd = socket(PF_INET6, type, 0);
578         if (sock->fd == -1) {
579                 return map_nt_error_from_unix(errno);
580         }
581
582         sock->backend_name = "ipv6";
583
584         return NT_STATUS_OK;
585 }
586
587 static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
588                                  const struct socket_address *my_address,
589                                  const struct socket_address *srv_address,
590                                  uint32_t flags)
591 {
592         int ret;
593
594         if (my_address && my_address->sockaddr) {
595                 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
596                 if (ret == -1) {
597                         return map_nt_error_from_unix(errno);
598                 }
599         } else if (my_address) {
600                 struct in6_addr my_ip;
601                 my_ip = interpret_addr6(my_address->addr);
602
603                 if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_address->port != 0) {
604                         struct sockaddr_in6 my_addr;
605                         ZERO_STRUCT(my_addr);
606                         my_addr.sin6_addr       = my_ip;
607                         my_addr.sin6_port       = htons(my_address->port);
608                         my_addr.sin6_family     = PF_INET6;
609                         
610                         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
611                         if (ret == -1) {
612                                 return map_nt_error_from_unix(errno);
613                         }
614                 }
615         }
616
617         if (srv_address->sockaddr) {
618                 ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
619         } else {
620                 struct in6_addr srv_ip;
621                 struct sockaddr_in6 srv_addr;
622                 srv_ip = interpret_addr6(srv_address->addr);
623                 if (memcmp(&srv_ip, &in6addr_any, sizeof(srv_ip)) == 0) {
624                         return NT_STATUS_BAD_NETWORK_NAME;
625                 }
626                 
627                 ZERO_STRUCT(srv_addr);
628                 srv_addr.sin6_addr      = srv_ip;
629                 srv_addr.sin6_port      = htons(srv_address->port);
630                 srv_addr.sin6_family    = PF_INET6;
631                 
632                 ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
633         }
634         if (ret == -1) {
635                 return map_nt_error_from_unix(errno);
636         }
637
638         return ip_connect_complete(sock, flags);
639 }
640
641 static NTSTATUS ipv6_listen(struct socket_context *sock,
642                                 const struct socket_address *my_address,
643                                 int queue_size, uint32_t flags)
644 {
645         struct sockaddr_in6 my_addr;
646         struct in6_addr ip_addr;
647         int ret;
648
649         socket_set_option(sock, "SO_REUSEADDR=1", NULL);
650
651         if (my_address->sockaddr) {
652                 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
653         } else {
654                 ip_addr = interpret_addr6(my_address->addr);
655                 
656                 ZERO_STRUCT(my_addr);
657                 my_addr.sin6_addr       = ip_addr;
658                 my_addr.sin6_port       = htons(my_address->port);
659                 my_addr.sin6_family     = PF_INET6;
660                 
661                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
662         }
663
664         if (ret == -1) {
665                 return map_nt_error_from_unix(errno);
666         }
667
668         if (sock->type == SOCKET_TYPE_STREAM) {
669                 ret = listen(sock->fd, queue_size);
670                 if (ret == -1) {
671                         return map_nt_error_from_unix(errno);
672                 }
673         }
674
675         if (!(flags & SOCKET_FLAG_BLOCK)) {
676                 ret = set_blocking(sock->fd, False);
677                 if (ret == -1) {
678                         return map_nt_error_from_unix(errno);
679                 }
680         }
681
682         sock->state= SOCKET_STATE_SERVER_LISTEN;
683
684         return NT_STATUS_OK;
685 }
686
687 static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
688 {
689         struct sockaddr_in cli_addr;
690         socklen_t cli_addr_len = sizeof(cli_addr);
691         int new_fd;
692         
693         if (sock->type != SOCKET_TYPE_STREAM) {
694                 return NT_STATUS_INVALID_PARAMETER;
695         }
696
697         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
698         if (new_fd == -1) {
699                 return map_nt_error_from_unix(errno);
700         }
701
702         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
703                 int ret = set_blocking(new_fd, False);
704                 if (ret == -1) {
705                         close(new_fd);
706                         return map_nt_error_from_unix(errno);
707                 }
708         }
709
710         /* TODO: we could add a 'accept_check' hook here
711          *       which get the black/white lists via socket_set_accept_filter()
712          *       or something like that
713          *       --metze
714          */
715
716         (*new_sock) = talloc(NULL, struct socket_context);
717         if (!(*new_sock)) {
718                 close(new_fd);
719                 return NT_STATUS_NO_MEMORY;
720         }
721
722         /* copy the socket_context */
723         (*new_sock)->type               = sock->type;
724         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
725         (*new_sock)->flags              = sock->flags;
726
727         (*new_sock)->fd                 = new_fd;
728
729         (*new_sock)->private_data       = NULL;
730         (*new_sock)->ops                = sock->ops;
731         (*new_sock)->backend_name       = sock->backend_name;
732
733         return NT_STATUS_OK;
734 }
735
736 static NTSTATUS ipv6_recvfrom(struct socket_context *sock, void *buf, 
737                               size_t wantlen, size_t *nread, 
738                               TALLOC_CTX *addr_ctx, struct socket_address **_src)
739 {
740         ssize_t gotlen;
741         struct sockaddr_in6 *from_addr;
742         socklen_t from_len = sizeof(*from_addr);
743         struct socket_address *src;
744         char addrstring[INET6_ADDRSTRLEN];
745         
746         src = talloc(addr_ctx, struct socket_address);
747         if (!src) {
748                 return NT_STATUS_NO_MEMORY;
749         }
750         
751         src->family = sock->backend_name;
752
753         from_addr = talloc(src, struct sockaddr_in6);
754         if (!from_addr) {
755                 talloc_free(src);
756                 return NT_STATUS_NO_MEMORY;
757         }
758
759         src->sockaddr = (struct sockaddr *)from_addr;
760
761         *nread = 0;
762
763         gotlen = recvfrom(sock->fd, buf, wantlen, 0, 
764                           src->sockaddr, &from_len);
765         if (gotlen == 0) {
766                 talloc_free(src);
767                 return NT_STATUS_END_OF_FILE;
768         } else if (gotlen == -1) {
769                 talloc_free(src);
770                 return map_nt_error_from_unix(errno);
771         }
772
773         src->sockaddrlen = from_len;
774
775         if (inet_ntop(AF_INET6, &from_addr->sin6_addr, addrstring, sizeof(addrstring)) == NULL) {
776                 DEBUG(0, ("Unable to convert address to string: %s\n", strerror(errno)));
777                 talloc_free(src);
778                 return NT_STATUS_INTERNAL_ERROR;
779         }
780
781         src->addr = talloc_strdup(src, addrstring);
782         if (src->addr == NULL) {
783                 talloc_free(src);
784                 return NT_STATUS_NO_MEMORY;
785         }
786         src->port = ntohs(from_addr->sin6_port);
787
788         *nread  = gotlen;
789         *_src   = src;
790         return NT_STATUS_OK;
791 }
792
793 static NTSTATUS ipv6_sendto(struct socket_context *sock, 
794                             const DATA_BLOB *blob, size_t *sendlen, 
795                             const struct socket_address *dest_addr)
796 {
797         ssize_t len;
798
799         if (dest_addr->sockaddr) {
800                 len = sendto(sock->fd, blob->data, blob->length, 0, 
801                              dest_addr->sockaddr, dest_addr->sockaddrlen);
802         } else {
803                 struct sockaddr_in6 srv_addr;
804                 struct in6_addr addr;
805                 
806                 ZERO_STRUCT(srv_addr);
807                 addr                     = interpret_addr6(dest_addr->addr);
808                 if (addr.s6_addr == 0) {
809                         return NT_STATUS_HOST_UNREACHABLE;
810                 }
811                 srv_addr.sin6_addr = addr;
812                 srv_addr.sin6_port        = htons(dest_addr->port);
813                 srv_addr.sin6_family      = PF_INET6;
814                 
815                 *sendlen = 0;
816                 
817                 len = sendto(sock->fd, blob->data, blob->length, 0, 
818                              (struct sockaddr *)&srv_addr, sizeof(srv_addr));
819         }
820         if (len == -1) {
821                 return map_nt_error_from_unix(errno);
822         }       
823
824         *sendlen = len;
825
826         return NT_STATUS_OK;
827 }
828
829 static NTSTATUS ipv6_set_option(struct socket_context *sock, const char *option, const char *val)
830 {
831         set_socket_options(sock->fd, option);
832         return NT_STATUS_OK;
833 }
834
835 static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
836 {
837         struct sockaddr_in6 peer_addr;
838         socklen_t len = sizeof(peer_addr);
839         struct hostent *he;
840         int ret;
841
842         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
843         if (ret == -1) {
844                 return NULL;
845         }
846
847         he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
848         if (he == NULL) {
849                 return NULL;
850         }
851
852         return talloc_strdup(mem_ctx, he->h_name);
853 }
854
855 static struct socket_address *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
856 {
857         struct sockaddr_in6 *peer_addr;
858         socklen_t len = sizeof(*peer_addr);
859         struct socket_address *peer;
860         int ret;
861         char addr[128];
862         const char *addr_ret;
863         
864         peer = talloc(mem_ctx, struct socket_address);
865         if (!peer) {
866                 return NULL;
867         }
868         
869         peer->family = sock->backend_name;
870         peer_addr = talloc(peer, struct sockaddr_in6);
871         if (!peer_addr) {
872                 talloc_free(peer);
873                 return NULL;
874         }
875
876         peer->sockaddr = (struct sockaddr *)peer_addr;
877
878         ret = getpeername(sock->fd, peer->sockaddr, &len);
879         if (ret == -1) {
880                 talloc_free(peer);
881                 return NULL;
882         }
883
884         peer->sockaddrlen = len;
885
886         addr_ret = inet_ntop(AF_INET6, &peer_addr->sin6_addr, addr, sizeof(addr));
887         if (addr_ret == NULL) {
888                 talloc_free(peer);
889                 return NULL;
890         }
891
892         peer->addr = talloc_strdup(peer, addr_ret);
893         if (peer->addr == NULL) {
894                 talloc_free(peer);
895                 return NULL;
896         }
897
898         peer->port = ntohs(peer_addr->sin6_port);
899
900         return peer;
901 }
902
903 static struct socket_address *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
904 {
905         struct sockaddr_in6 *local_addr;
906         socklen_t len = sizeof(*local_addr);
907         struct socket_address *local;
908         int ret;
909         char addrstring[INET6_ADDRSTRLEN];
910         
911         local = talloc(mem_ctx, struct socket_address);
912         if (!local) {
913                 return NULL;
914         }
915         
916         local->family = sock->backend_name;
917         local_addr = talloc(local, struct sockaddr_in6);
918         if (!local_addr) {
919                 talloc_free(local);
920                 return NULL;
921         }
922
923         local->sockaddr = (struct sockaddr *)local_addr;
924
925         ret = getsockname(sock->fd, local->sockaddr, &len);
926         if (ret == -1) {
927                 talloc_free(local);
928                 return NULL;
929         }
930
931         local->sockaddrlen = len;
932
933         if (inet_ntop(AF_INET6, &local_addr->sin6_addr, addrstring, 
934                        sizeof(addrstring)) == NULL) {
935                 DEBUG(0, ("Unable to convert address to string: %s\n", 
936                           strerror(errno)));
937                 talloc_free(local);
938                 return NULL;
939         }
940         
941         local->addr = talloc_strdup(mem_ctx, addrstring);
942         if (!local->addr) {
943                 talloc_free(local);
944                 return NULL;
945         }
946         local->port = ntohs(local_addr->sin6_port);
947
948         return local;
949 }
950
951 static const struct socket_ops ipv6_tcp_ops = {
952         .name                   = "ipv6",
953         .fn_init                = ipv6_init,
954         .fn_connect             = ipv6_tcp_connect,
955         .fn_connect_complete    = ip_connect_complete,
956         .fn_listen              = ipv6_listen,
957         .fn_accept              = ipv6_tcp_accept,
958         .fn_recv                = ip_recv,
959         .fn_recvfrom            = ipv6_recvfrom,
960         .fn_send                = ip_send,
961         .fn_sendto              = ipv6_sendto,
962         .fn_pending             = ip_pending,
963         .fn_close               = ip_close,
964
965         .fn_set_option          = ipv6_set_option,
966
967         .fn_get_peer_name       = ipv6_tcp_get_peer_name,
968         .fn_get_peer_addr       = ipv6_tcp_get_peer_addr,
969         .fn_get_my_addr         = ipv6_tcp_get_my_addr,
970
971         .fn_get_fd              = ip_get_fd
972 };
973
974 const struct socket_ops *socket_ipv6_ops(enum socket_type type)
975 {
976         return &ipv6_tcp_ops;
977 }
978
979 #endif