s3: Fix Coverity ID 1012, CHECKED_RETURN
[Samba.git] / source4 / lib / socket / socket_ip.c
blob9a02f0184ca1519f9dc6aaa6fabf6f5f3872c9bf
1 /*
2 Unix SMB/CIFS implementation.
4 Socket IPv4/IPv6 functions
6 Copyright (C) Stefan Metzmacher 2004
7 Copyright (C) Andrew Tridgell 2004-2005
8 Copyright (C) Jelmer Vernooij 2004
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.
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.
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/>.
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "lib/socket/socket.h"
27 #include "system/network.h"
28 #include "lib/util/util_net.h"
30 static NTSTATUS ipv4_init(struct socket_context *sock)
32 int type;
34 switch (sock->type) {
35 case SOCKET_TYPE_STREAM:
36 type = SOCK_STREAM;
37 break;
38 case SOCKET_TYPE_DGRAM:
39 type = SOCK_DGRAM;
40 break;
41 default:
42 return NT_STATUS_INVALID_PARAMETER;
45 sock->fd = socket(PF_INET, type, 0);
46 if (sock->fd == -1) {
47 return map_nt_error_from_unix(errno);
50 sock->backend_name = "ipv4";
51 sock->family = AF_INET;
53 return NT_STATUS_OK;
56 static void ip_close(struct socket_context *sock)
58 close(sock->fd);
61 static NTSTATUS ip_connect_complete(struct socket_context *sock, uint32_t flags)
63 int error=0, ret;
64 socklen_t len = sizeof(error);
66 /* check for any errors that may have occurred - this is needed
67 for non-blocking connect */
68 ret = getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len);
69 if (ret == -1) {
70 return map_nt_error_from_unix(errno);
72 if (error != 0) {
73 return map_nt_error_from_unix(error);
76 if (!(flags & SOCKET_FLAG_BLOCK)) {
77 ret = set_blocking(sock->fd, false);
78 if (ret == -1) {
79 return map_nt_error_from_unix(errno);
83 sock->state = SOCKET_STATE_CLIENT_CONNECTED;
85 return NT_STATUS_OK;
89 static NTSTATUS ipv4_connect(struct socket_context *sock,
90 const struct socket_address *my_address,
91 const struct socket_address *srv_address,
92 uint32_t flags)
94 struct sockaddr_in srv_addr;
95 struct in_addr my_ip;
96 struct in_addr srv_ip;
97 int ret;
99 if (my_address && my_address->sockaddr) {
100 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
101 if (ret == -1) {
102 return map_nt_error_from_unix(errno);
104 } else if (my_address) {
105 my_ip = interpret_addr2(my_address->addr);
107 if (my_ip.s_addr != 0 || my_address->port != 0) {
108 struct sockaddr_in my_addr;
109 ZERO_STRUCT(my_addr);
110 #ifdef HAVE_SOCK_SIN_LEN
111 my_addr.sin_len = sizeof(my_addr);
112 #endif
113 my_addr.sin_addr.s_addr = my_ip.s_addr;
114 my_addr.sin_port = htons(my_address->port);
115 my_addr.sin_family = PF_INET;
117 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
118 if (ret == -1) {
119 return map_nt_error_from_unix(errno);
124 if (srv_address->sockaddr) {
125 ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
126 if (ret == -1) {
127 return map_nt_error_from_unix(errno);
129 } else {
130 srv_ip = interpret_addr2(srv_address->addr);
131 if (!srv_ip.s_addr) {
132 return NT_STATUS_BAD_NETWORK_NAME;
135 SMB_ASSERT(srv_address->port != 0);
137 ZERO_STRUCT(srv_addr);
138 #ifdef HAVE_SOCK_SIN_LEN
139 srv_addr.sin_len = sizeof(srv_addr);
140 #endif
141 srv_addr.sin_addr.s_addr= srv_ip.s_addr;
142 srv_addr.sin_port = htons(srv_address->port);
143 srv_addr.sin_family = PF_INET;
145 ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
146 if (ret == -1) {
147 return map_nt_error_from_unix(errno);
151 return ip_connect_complete(sock, flags);
156 note that for simplicity of the API, socket_listen() is also
157 use for DGRAM sockets, but in reality only a bind() is done
159 static NTSTATUS ipv4_listen(struct socket_context *sock,
160 const struct socket_address *my_address,
161 int queue_size, uint32_t flags)
163 struct sockaddr_in my_addr;
164 struct in_addr ip_addr;
165 int ret;
167 socket_set_option(sock, "SO_REUSEADDR=1", NULL);
169 if (my_address->sockaddr) {
170 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
171 } else {
172 ip_addr = interpret_addr2(my_address->addr);
174 ZERO_STRUCT(my_addr);
175 #ifdef HAVE_SOCK_SIN_LEN
176 my_addr.sin_len = sizeof(my_addr);
177 #endif
178 my_addr.sin_addr.s_addr = ip_addr.s_addr;
179 my_addr.sin_port = htons(my_address->port);
180 my_addr.sin_family = PF_INET;
182 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
185 if (ret == -1) {
186 return map_nt_error_from_unix(errno);
189 if (sock->type == SOCKET_TYPE_STREAM) {
190 ret = listen(sock->fd, queue_size);
191 if (ret == -1) {
192 return map_nt_error_from_unix(errno);
196 if (!(flags & SOCKET_FLAG_BLOCK)) {
197 ret = set_blocking(sock->fd, false);
198 if (ret == -1) {
199 return map_nt_error_from_unix(errno);
203 sock->state= SOCKET_STATE_SERVER_LISTEN;
205 return NT_STATUS_OK;
208 static NTSTATUS ipv4_accept(struct socket_context *sock, struct socket_context **new_sock)
210 struct sockaddr_in cli_addr;
211 socklen_t cli_addr_len = sizeof(cli_addr);
212 int new_fd;
214 if (sock->type != SOCKET_TYPE_STREAM) {
215 return NT_STATUS_INVALID_PARAMETER;
218 new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
219 if (new_fd == -1) {
220 return map_nt_error_from_unix(errno);
223 if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
224 int ret = set_blocking(new_fd, false);
225 if (ret == -1) {
226 close(new_fd);
227 return map_nt_error_from_unix(errno);
231 /* TODO: we could add a 'accept_check' hook here
232 * which get the black/white lists via socket_set_accept_filter()
233 * or something like that
234 * --metze
237 (*new_sock) = talloc(NULL, struct socket_context);
238 if (!(*new_sock)) {
239 close(new_fd);
240 return NT_STATUS_NO_MEMORY;
243 /* copy the socket_context */
244 (*new_sock)->type = sock->type;
245 (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
246 (*new_sock)->flags = sock->flags;
248 (*new_sock)->fd = new_fd;
250 (*new_sock)->private_data = NULL;
251 (*new_sock)->ops = sock->ops;
252 (*new_sock)->backend_name = sock->backend_name;
254 return NT_STATUS_OK;
257 static NTSTATUS ip_recv(struct socket_context *sock, void *buf,
258 size_t wantlen, size_t *nread)
260 ssize_t gotlen;
262 *nread = 0;
264 gotlen = recv(sock->fd, buf, wantlen, 0);
265 if (gotlen == 0) {
266 return NT_STATUS_END_OF_FILE;
267 } else if (gotlen == -1) {
268 return map_nt_error_from_unix(errno);
271 *nread = gotlen;
273 return NT_STATUS_OK;
277 static NTSTATUS ipv4_recvfrom(struct socket_context *sock, void *buf,
278 size_t wantlen, size_t *nread,
279 TALLOC_CTX *addr_ctx, struct socket_address **_src)
281 ssize_t gotlen;
282 struct sockaddr_in *from_addr;
283 socklen_t from_len = sizeof(*from_addr);
284 struct socket_address *src;
285 char addrstring[INET_ADDRSTRLEN];
287 src = talloc(addr_ctx, struct socket_address);
288 if (!src) {
289 return NT_STATUS_NO_MEMORY;
292 src->family = sock->backend_name;
294 from_addr = talloc(src, struct sockaddr_in);
295 if (!from_addr) {
296 talloc_free(src);
297 return NT_STATUS_NO_MEMORY;
300 src->sockaddr = (struct sockaddr *)from_addr;
302 *nread = 0;
304 gotlen = recvfrom(sock->fd, buf, wantlen, 0,
305 src->sockaddr, &from_len);
306 if (gotlen == 0) {
307 talloc_free(src);
308 return NT_STATUS_END_OF_FILE;
309 } else if (gotlen == -1) {
310 talloc_free(src);
311 return map_nt_error_from_unix(errno);
314 src->sockaddrlen = from_len;
316 if (inet_ntop(AF_INET, &from_addr->sin_addr, addrstring,
317 sizeof(addrstring)) == NULL) {
318 talloc_free(src);
319 return NT_STATUS_INTERNAL_ERROR;
321 src->addr = talloc_strdup(src, addrstring);
322 if (src->addr == NULL) {
323 talloc_free(src);
324 return NT_STATUS_NO_MEMORY;
326 src->port = ntohs(from_addr->sin_port);
328 *nread = gotlen;
329 *_src = src;
330 return NT_STATUS_OK;
333 static NTSTATUS ip_send(struct socket_context *sock,
334 const DATA_BLOB *blob, size_t *sendlen)
336 ssize_t len;
338 *sendlen = 0;
340 len = send(sock->fd, blob->data, blob->length, 0);
341 if (len == -1) {
342 return map_nt_error_from_unix(errno);
345 *sendlen = len;
347 return NT_STATUS_OK;
350 static NTSTATUS ipv4_sendto(struct socket_context *sock,
351 const DATA_BLOB *blob, size_t *sendlen,
352 const struct socket_address *dest_addr)
354 ssize_t len;
356 if (dest_addr->sockaddr) {
357 len = sendto(sock->fd, blob->data, blob->length, 0,
358 dest_addr->sockaddr, dest_addr->sockaddrlen);
359 } else {
360 struct sockaddr_in srv_addr;
361 struct in_addr addr;
363 SMB_ASSERT(dest_addr->port != 0);
365 ZERO_STRUCT(srv_addr);
366 #ifdef HAVE_SOCK_SIN_LEN
367 srv_addr.sin_len = sizeof(srv_addr);
368 #endif
369 addr = interpret_addr2(dest_addr->addr);
370 if (addr.s_addr == 0) {
371 return NT_STATUS_HOST_UNREACHABLE;
373 srv_addr.sin_addr.s_addr = addr.s_addr;
374 srv_addr.sin_port = htons(dest_addr->port);
375 srv_addr.sin_family = PF_INET;
377 *sendlen = 0;
379 len = sendto(sock->fd, blob->data, blob->length, 0,
380 (struct sockaddr *)&srv_addr, sizeof(srv_addr));
382 if (len == -1) {
383 return map_nt_error_from_unix(errno);
386 *sendlen = len;
388 return NT_STATUS_OK;
391 static NTSTATUS ipv4_set_option(struct socket_context *sock, const char *option, const char *val)
393 set_socket_options(sock->fd, option);
394 return NT_STATUS_OK;
397 static char *ipv4_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
399 struct sockaddr_in peer_addr;
400 socklen_t len = sizeof(peer_addr);
401 struct hostent *he;
402 int ret;
404 ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
405 if (ret == -1) {
406 return NULL;
409 he = gethostbyaddr((char *)&peer_addr.sin_addr, sizeof(peer_addr.sin_addr), AF_INET);
410 if (he == NULL) {
411 return NULL;
414 return talloc_strdup(mem_ctx, he->h_name);
417 static struct socket_address *ipv4_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
419 struct sockaddr_in *peer_addr;
420 socklen_t len = sizeof(*peer_addr);
421 struct socket_address *peer;
422 char addrstring[INET_ADDRSTRLEN];
423 int ret;
425 peer = talloc(mem_ctx, struct socket_address);
426 if (!peer) {
427 return NULL;
430 peer->family = sock->backend_name;
431 peer_addr = talloc(peer, struct sockaddr_in);
432 if (!peer_addr) {
433 talloc_free(peer);
434 return NULL;
437 peer->sockaddr = (struct sockaddr *)peer_addr;
439 ret = getpeername(sock->fd, peer->sockaddr, &len);
440 if (ret == -1) {
441 talloc_free(peer);
442 return NULL;
445 peer->sockaddrlen = len;
447 if (inet_ntop(AF_INET, &peer_addr->sin_addr, addrstring,
448 sizeof(addrstring)) == NULL) {
449 talloc_free(peer);
450 return NULL;
452 peer->addr = talloc_strdup(peer, addrstring);
453 if (!peer->addr) {
454 talloc_free(peer);
455 return NULL;
457 peer->port = ntohs(peer_addr->sin_port);
459 return peer;
462 static struct socket_address *ipv4_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
464 struct sockaddr_in *local_addr;
465 socklen_t len = sizeof(*local_addr);
466 struct socket_address *local;
467 char addrstring[INET_ADDRSTRLEN];
468 int ret;
470 local = talloc(mem_ctx, struct socket_address);
471 if (!local) {
472 return NULL;
475 local->family = sock->backend_name;
476 local_addr = talloc(local, struct sockaddr_in);
477 if (!local_addr) {
478 talloc_free(local);
479 return NULL;
482 local->sockaddr = (struct sockaddr *)local_addr;
484 ret = getsockname(sock->fd, local->sockaddr, &len);
485 if (ret == -1) {
486 talloc_free(local);
487 return NULL;
490 local->sockaddrlen = len;
492 if (inet_ntop(AF_INET, &local_addr->sin_addr, addrstring,
493 sizeof(addrstring)) == NULL) {
494 talloc_free(local);
495 return NULL;
497 local->addr = talloc_strdup(local, addrstring);
498 if (!local->addr) {
499 talloc_free(local);
500 return NULL;
502 local->port = ntohs(local_addr->sin_port);
504 return local;
506 static int ip_get_fd(struct socket_context *sock)
508 return sock->fd;
511 static NTSTATUS ip_pending(struct socket_context *sock, size_t *npending)
513 int value = 0;
514 if (ioctl(sock->fd, FIONREAD, &value) == 0) {
515 *npending = value;
516 return NT_STATUS_OK;
518 return map_nt_error_from_unix(errno);
521 static const struct socket_ops ipv4_ops = {
522 .name = "ipv4",
523 .fn_init = ipv4_init,
524 .fn_connect = ipv4_connect,
525 .fn_connect_complete = ip_connect_complete,
526 .fn_listen = ipv4_listen,
527 .fn_accept = ipv4_accept,
528 .fn_recv = ip_recv,
529 .fn_recvfrom = ipv4_recvfrom,
530 .fn_send = ip_send,
531 .fn_sendto = ipv4_sendto,
532 .fn_pending = ip_pending,
533 .fn_close = ip_close,
535 .fn_set_option = ipv4_set_option,
537 .fn_get_peer_name = ipv4_get_peer_name,
538 .fn_get_peer_addr = ipv4_get_peer_addr,
539 .fn_get_my_addr = ipv4_get_my_addr,
541 .fn_get_fd = ip_get_fd
544 _PUBLIC_ const struct socket_ops *socket_ipv4_ops(enum socket_type type)
546 return &ipv4_ops;
549 #if HAVE_IPV6
551 static struct in6_addr interpret_addr6(const char *name)
553 char addr[INET6_ADDRSTRLEN];
554 struct in6_addr dest6;
555 const char *sp = name;
556 char *p;
557 int ret;
559 if (sp == NULL) return in6addr_any;
561 p = strchr_m(sp, '%');
563 if (strcasecmp(sp, "localhost") == 0) {
564 sp = "::1";
568 * Cope with link-local.
569 * This is IP:v6:addr%ifname.
572 if (p && (p > sp) && (if_nametoindex(p+1) != 0)) {
573 strlcpy(addr, sp,
574 MIN(PTR_DIFF(p,sp)+1,
575 sizeof(addr)));
576 sp = addr;
579 ret = inet_pton(AF_INET6, sp, &dest6);
580 if (ret > 0) {
581 return dest6;
584 return in6addr_any;
587 static NTSTATUS ipv6_init(struct socket_context *sock)
589 int type;
591 switch (sock->type) {
592 case SOCKET_TYPE_STREAM:
593 type = SOCK_STREAM;
594 break;
595 case SOCKET_TYPE_DGRAM:
596 type = SOCK_DGRAM;
597 break;
598 default:
599 return NT_STATUS_INVALID_PARAMETER;
602 sock->fd = socket(PF_INET6, type, 0);
603 if (sock->fd == -1) {
604 return map_nt_error_from_unix(errno);
607 sock->backend_name = "ipv6";
608 sock->family = AF_INET6;
610 return NT_STATUS_OK;
613 static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
614 const struct socket_address *my_address,
615 const struct socket_address *srv_address,
616 uint32_t flags)
618 int ret;
620 if (my_address && my_address->sockaddr) {
621 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
622 if (ret == -1) {
623 return map_nt_error_from_unix(errno);
625 } else if (my_address) {
626 struct in6_addr my_ip;
627 my_ip = interpret_addr6(my_address->addr);
629 if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_address->port != 0) {
630 struct sockaddr_in6 my_addr;
631 ZERO_STRUCT(my_addr);
632 my_addr.sin6_addr = my_ip;
633 my_addr.sin6_port = htons(my_address->port);
634 my_addr.sin6_family = PF_INET6;
636 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
637 if (ret == -1) {
638 return map_nt_error_from_unix(errno);
643 if (srv_address->sockaddr) {
644 ret = connect(sock->fd, srv_address->sockaddr, srv_address->sockaddrlen);
645 } else {
646 struct in6_addr srv_ip;
647 struct sockaddr_in6 srv_addr;
648 srv_ip = interpret_addr6(srv_address->addr);
649 if (memcmp(&srv_ip, &in6addr_any, sizeof(srv_ip)) == 0) {
650 return NT_STATUS_BAD_NETWORK_NAME;
653 ZERO_STRUCT(srv_addr);
654 srv_addr.sin6_addr = srv_ip;
655 srv_addr.sin6_port = htons(srv_address->port);
656 srv_addr.sin6_family = PF_INET6;
658 ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
660 if (ret == -1) {
661 return map_nt_error_from_unix(errno);
664 return ip_connect_complete(sock, flags);
667 static NTSTATUS ipv6_listen(struct socket_context *sock,
668 const struct socket_address *my_address,
669 int queue_size, uint32_t flags)
671 struct sockaddr_in6 my_addr;
672 struct in6_addr ip_addr;
673 int ret;
675 socket_set_option(sock, "SO_REUSEADDR=1", NULL);
677 if (my_address->sockaddr) {
678 ret = bind(sock->fd, my_address->sockaddr, my_address->sockaddrlen);
679 } else {
680 ip_addr = interpret_addr6(my_address->addr);
682 ZERO_STRUCT(my_addr);
683 my_addr.sin6_addr = ip_addr;
684 my_addr.sin6_port = htons(my_address->port);
685 my_addr.sin6_family = PF_INET6;
687 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
690 if (ret == -1) {
691 return map_nt_error_from_unix(errno);
694 if (sock->type == SOCKET_TYPE_STREAM) {
695 ret = listen(sock->fd, queue_size);
696 if (ret == -1) {
697 return map_nt_error_from_unix(errno);
701 if (!(flags & SOCKET_FLAG_BLOCK)) {
702 ret = set_blocking(sock->fd, false);
703 if (ret == -1) {
704 return map_nt_error_from_unix(errno);
708 sock->state= SOCKET_STATE_SERVER_LISTEN;
710 return NT_STATUS_OK;
713 static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
715 struct sockaddr_in6 cli_addr;
716 socklen_t cli_addr_len = sizeof(cli_addr);
717 int new_fd;
719 if (sock->type != SOCKET_TYPE_STREAM) {
720 return NT_STATUS_INVALID_PARAMETER;
723 new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
724 if (new_fd == -1) {
725 return map_nt_error_from_unix(errno);
728 if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
729 int ret = set_blocking(new_fd, false);
730 if (ret == -1) {
731 close(new_fd);
732 return map_nt_error_from_unix(errno);
736 /* TODO: we could add a 'accept_check' hook here
737 * which get the black/white lists via socket_set_accept_filter()
738 * or something like that
739 * --metze
742 (*new_sock) = talloc(NULL, struct socket_context);
743 if (!(*new_sock)) {
744 close(new_fd);
745 return NT_STATUS_NO_MEMORY;
748 /* copy the socket_context */
749 (*new_sock)->type = sock->type;
750 (*new_sock)->state = SOCKET_STATE_SERVER_CONNECTED;
751 (*new_sock)->flags = sock->flags;
753 (*new_sock)->fd = new_fd;
755 (*new_sock)->private_data = NULL;
756 (*new_sock)->ops = sock->ops;
757 (*new_sock)->backend_name = sock->backend_name;
759 return NT_STATUS_OK;
762 static NTSTATUS ipv6_recvfrom(struct socket_context *sock, void *buf,
763 size_t wantlen, size_t *nread,
764 TALLOC_CTX *addr_ctx, struct socket_address **_src)
766 ssize_t gotlen;
767 struct sockaddr_in6 *from_addr;
768 socklen_t from_len = sizeof(*from_addr);
769 struct socket_address *src;
770 char addrstring[INET6_ADDRSTRLEN];
772 src = talloc(addr_ctx, struct socket_address);
773 if (!src) {
774 return NT_STATUS_NO_MEMORY;
777 src->family = sock->backend_name;
779 from_addr = talloc(src, struct sockaddr_in6);
780 if (!from_addr) {
781 talloc_free(src);
782 return NT_STATUS_NO_MEMORY;
785 src->sockaddr = (struct sockaddr *)from_addr;
787 *nread = 0;
789 gotlen = recvfrom(sock->fd, buf, wantlen, 0,
790 src->sockaddr, &from_len);
791 if (gotlen == 0) {
792 talloc_free(src);
793 return NT_STATUS_END_OF_FILE;
794 } else if (gotlen == -1) {
795 talloc_free(src);
796 return map_nt_error_from_unix(errno);
799 src->sockaddrlen = from_len;
801 if (inet_ntop(AF_INET6, &from_addr->sin6_addr, addrstring, sizeof(addrstring)) == NULL) {
802 DEBUG(0, ("Unable to convert address to string: %s\n", strerror(errno)));
803 talloc_free(src);
804 return NT_STATUS_INTERNAL_ERROR;
807 src->addr = talloc_strdup(src, addrstring);
808 if (src->addr == NULL) {
809 talloc_free(src);
810 return NT_STATUS_NO_MEMORY;
812 src->port = ntohs(from_addr->sin6_port);
814 *nread = gotlen;
815 *_src = src;
816 return NT_STATUS_OK;
819 static NTSTATUS ipv6_sendto(struct socket_context *sock,
820 const DATA_BLOB *blob, size_t *sendlen,
821 const struct socket_address *dest_addr)
823 ssize_t len;
825 if (dest_addr->sockaddr) {
826 len = sendto(sock->fd, blob->data, blob->length, 0,
827 dest_addr->sockaddr, dest_addr->sockaddrlen);
828 } else {
829 struct sockaddr_in6 srv_addr;
830 struct in6_addr addr;
832 ZERO_STRUCT(srv_addr);
833 addr = interpret_addr6(dest_addr->addr);
834 if (addr.s6_addr == 0) {
835 return NT_STATUS_HOST_UNREACHABLE;
837 srv_addr.sin6_addr = addr;
838 srv_addr.sin6_port = htons(dest_addr->port);
839 srv_addr.sin6_family = PF_INET6;
841 *sendlen = 0;
843 len = sendto(sock->fd, blob->data, blob->length, 0,
844 (struct sockaddr *)&srv_addr, sizeof(srv_addr));
846 if (len == -1) {
847 return map_nt_error_from_unix(errno);
850 *sendlen = len;
852 return NT_STATUS_OK;
855 static NTSTATUS ipv6_set_option(struct socket_context *sock, const char *option, const char *val)
857 set_socket_options(sock->fd, option);
858 return NT_STATUS_OK;
861 static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
863 struct sockaddr_in6 peer_addr;
864 socklen_t len = sizeof(peer_addr);
865 struct hostent *he;
866 int ret;
868 ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
869 if (ret == -1) {
870 return NULL;
873 he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
874 if (he == NULL) {
875 return NULL;
878 return talloc_strdup(mem_ctx, he->h_name);
881 static struct socket_address *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
883 struct sockaddr_in6 *peer_addr;
884 socklen_t len = sizeof(*peer_addr);
885 struct socket_address *peer;
886 int ret;
887 char addr[128];
888 const char *addr_ret;
890 peer = talloc(mem_ctx, struct socket_address);
891 if (!peer) {
892 return NULL;
895 peer->family = sock->backend_name;
896 peer_addr = talloc(peer, struct sockaddr_in6);
897 if (!peer_addr) {
898 talloc_free(peer);
899 return NULL;
902 peer->sockaddr = (struct sockaddr *)peer_addr;
904 ret = getpeername(sock->fd, peer->sockaddr, &len);
905 if (ret == -1) {
906 talloc_free(peer);
907 return NULL;
910 peer->sockaddrlen = len;
912 addr_ret = inet_ntop(AF_INET6, &peer_addr->sin6_addr, addr, sizeof(addr));
913 if (addr_ret == NULL) {
914 talloc_free(peer);
915 return NULL;
918 peer->addr = talloc_strdup(peer, addr_ret);
919 if (peer->addr == NULL) {
920 talloc_free(peer);
921 return NULL;
924 peer->port = ntohs(peer_addr->sin6_port);
926 return peer;
929 static struct socket_address *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
931 struct sockaddr_in6 *local_addr;
932 socklen_t len = sizeof(*local_addr);
933 struct socket_address *local;
934 int ret;
935 char addrstring[INET6_ADDRSTRLEN];
937 local = talloc(mem_ctx, struct socket_address);
938 if (!local) {
939 return NULL;
942 local->family = sock->backend_name;
943 local_addr = talloc(local, struct sockaddr_in6);
944 if (!local_addr) {
945 talloc_free(local);
946 return NULL;
949 local->sockaddr = (struct sockaddr *)local_addr;
951 ret = getsockname(sock->fd, local->sockaddr, &len);
952 if (ret == -1) {
953 talloc_free(local);
954 return NULL;
957 local->sockaddrlen = len;
959 if (inet_ntop(AF_INET6, &local_addr->sin6_addr, addrstring,
960 sizeof(addrstring)) == NULL) {
961 DEBUG(0, ("Unable to convert address to string: %s\n",
962 strerror(errno)));
963 talloc_free(local);
964 return NULL;
967 local->addr = talloc_strdup(mem_ctx, addrstring);
968 if (!local->addr) {
969 talloc_free(local);
970 return NULL;
972 local->port = ntohs(local_addr->sin6_port);
974 return local;
977 static const struct socket_ops ipv6_tcp_ops = {
978 .name = "ipv6",
979 .fn_init = ipv6_init,
980 .fn_connect = ipv6_tcp_connect,
981 .fn_connect_complete = ip_connect_complete,
982 .fn_listen = ipv6_listen,
983 .fn_accept = ipv6_tcp_accept,
984 .fn_recv = ip_recv,
985 .fn_recvfrom = ipv6_recvfrom,
986 .fn_send = ip_send,
987 .fn_sendto = ipv6_sendto,
988 .fn_pending = ip_pending,
989 .fn_close = ip_close,
991 .fn_set_option = ipv6_set_option,
993 .fn_get_peer_name = ipv6_tcp_get_peer_name,
994 .fn_get_peer_addr = ipv6_tcp_get_peer_addr,
995 .fn_get_my_addr = ipv6_tcp_get_my_addr,
997 .fn_get_fd = ip_get_fd
1000 _PUBLIC_ const struct socket_ops *socket_ipv6_ops(enum socket_type type)
1002 return &ipv6_tcp_ops;
1005 #endif