r3450: portability fixes
[garming/samba-autobuild/.git] / source4 / lib / socket / socket_ipv6.c
1 /* 
2    Unix SMB/CIFS implementation.
3    Socket IPv6 functions
4    Copyright (C) Stefan Metzmacher 2004
5    Copyright (C) Jelmer Vernooij 2004
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23 #include "system/network.h"
24
25 static struct in6_addr interpret_addr6(const char *name)
26 {
27         struct hostent *he;
28         
29         if (name == NULL) return in6addr_any;
30         
31         he = gethostbyname2(name, PF_INET6);
32
33         if (he == NULL) return in6addr_any;
34
35         return *((struct in6_addr *)he->h_addr);
36 }
37
38 static NTSTATUS ipv6_tcp_init(struct socket_context *sock)
39 {
40         sock->fd = socket(PF_INET6, SOCK_STREAM, 0);
41         if (sock->fd == -1) {
42                 return map_nt_error_from_unix(errno);
43         }
44
45         return NT_STATUS_OK;
46 }
47
48 static void ipv6_tcp_close(struct socket_context *sock)
49 {
50         close(sock->fd);
51 }
52
53 static NTSTATUS ipv6_tcp_connect(struct socket_context *sock,
54                                  const char *my_address, int my_port,
55                                  const char *srv_address, int srv_port,
56                                  uint32_t flags)
57 {
58         struct sockaddr_in6 srv_addr;
59         struct in6_addr my_ip;
60         struct in6_addr srv_ip;
61         int ret;
62
63         my_ip = interpret_addr6(my_address);
64
65         if (memcmp(&my_ip, &in6addr_any, sizeof(my_ip)) || my_port != 0) {
66                 struct sockaddr_in6 my_addr;
67                 ZERO_STRUCT(my_addr);
68                 my_addr.sin6_addr       = my_ip;
69                 my_addr.sin6_port       = htons(my_port);
70                 my_addr.sin6_family     = PF_INET6;
71                 
72                 ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
73                 if (ret == -1) {
74                         return map_nt_error_from_unix(errno);
75                 }
76         }
77
78         srv_ip = interpret_addr6(srv_address);
79
80         ZERO_STRUCT(srv_addr);
81         srv_addr.sin6_addr      = srv_ip;
82         srv_addr.sin6_port      = htons(srv_port);
83         srv_addr.sin6_family    = PF_INET6;
84
85         ret = connect(sock->fd, (const struct sockaddr *)&srv_addr, sizeof(srv_addr));
86         if (ret == -1) {
87                 return map_nt_error_from_unix(errno);
88         }
89
90         if (!(flags & SOCKET_FLAG_BLOCK)) {
91                 ret = set_blocking(sock->fd, False);
92                 if (ret == -1) {
93                         return map_nt_error_from_unix(errno);
94                 }
95         }
96
97         sock->state = SOCKET_STATE_CLIENT_CONNECTED;
98
99         return NT_STATUS_OK;
100 }
101
102 static NTSTATUS ipv6_tcp_listen(struct socket_context *sock,
103                                         const char *my_address, int port,
104                                         int queue_size, uint32_t flags)
105 {
106         struct sockaddr_in6 my_addr;
107         struct in6_addr ip_addr;
108         int ret;
109
110         ip_addr = interpret_addr6(my_address);
111
112         ZERO_STRUCT(my_addr);
113         my_addr.sin6_addr       = ip_addr;
114         my_addr.sin6_port       = htons(port);
115         my_addr.sin6_family     = PF_INET6;
116
117         ret = bind(sock->fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
118         if (ret == -1) {
119                 return map_nt_error_from_unix(errno);
120         }
121
122         ret = listen(sock->fd, queue_size);
123         if (ret == -1) {
124                 return map_nt_error_from_unix(errno);
125         }
126
127         if (!(flags & SOCKET_FLAG_BLOCK)) {
128                 ret = set_blocking(sock->fd, False);
129                 if (ret == -1) {
130                         return map_nt_error_from_unix(errno);
131                 }
132         }
133
134         sock->state= SOCKET_STATE_SERVER_LISTEN;
135
136         return NT_STATUS_OK;
137 }
138
139 static NTSTATUS ipv6_tcp_accept(struct socket_context *sock, struct socket_context **new_sock)
140 {
141         struct sockaddr_in cli_addr;
142         socklen_t cli_addr_len = sizeof(cli_addr);
143         int new_fd;
144
145         new_fd = accept(sock->fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
146         if (new_fd == -1) {
147                 return map_nt_error_from_unix(errno);
148         }
149
150         if (!(sock->flags & SOCKET_FLAG_BLOCK)) {
151                 int ret = set_blocking(new_fd, False);
152                 if (ret == -1) {
153                         close(new_fd);
154                         return map_nt_error_from_unix(errno);
155                 }
156         }
157
158         /* TODO: we could add a 'accept_check' hook here
159          *       which get the black/white lists via socket_set_accept_filter()
160          *       or something like that
161          *       --metze
162          */
163
164         (*new_sock) = talloc_p(NULL, struct socket_context);
165         if (!(*new_sock)) {
166                 close(new_fd);
167                 return NT_STATUS_NO_MEMORY;
168         }
169
170         /* copy the socket_context */
171         (*new_sock)->type               = sock->type;
172         (*new_sock)->state              = SOCKET_STATE_SERVER_CONNECTED;
173         (*new_sock)->flags              = sock->flags;
174
175         (*new_sock)->fd                 = new_fd;
176
177         (*new_sock)->private_data       = NULL;
178         (*new_sock)->ops                = sock->ops;
179
180         return NT_STATUS_OK;
181 }
182
183 static NTSTATUS ipv6_tcp_recv(struct socket_context *sock, void *buf, 
184                               size_t wantlen, size_t *nread, uint32_t flags)
185 {
186         ssize_t gotlen;
187         int flgs = 0;
188
189         /* TODO: we need to map all flags here */
190         if (flags & SOCKET_FLAG_PEEK) {
191                 flgs |= MSG_PEEK;
192         }
193
194         if (flags & SOCKET_FLAG_BLOCK) {
195                 flgs |= MSG_WAITALL;
196         }
197
198         *nread = 0;
199
200         gotlen = recv(sock->fd, buf, wantlen, flgs);
201         if (gotlen == 0) {
202                 return NT_STATUS_END_OF_FILE;
203         } else if (gotlen == -1) {
204                 return map_nt_error_from_unix(errno);
205         }
206
207         *nread = gotlen;
208
209         return NT_STATUS_OK;
210 }
211
212 static NTSTATUS ipv6_tcp_send(struct socket_context *sock, 
213                               const DATA_BLOB *blob, size_t *sendlen, uint32_t flags)
214 {
215         ssize_t len;
216         int flgs = 0;
217
218         *sendlen = 0;
219
220         len = send(sock->fd, blob->data, blob->length, flgs);
221         if (len == -1) {
222                 return map_nt_error_from_unix(errno);
223         }       
224
225         *sendlen = len;
226
227         return NT_STATUS_OK;
228 }
229
230 static NTSTATUS ipv6_tcp_set_option(struct socket_context *sock, const char *option, const char *val)
231 {
232         set_socket_options(sock->fd, option);
233         return NT_STATUS_OK;
234 }
235
236 static char *ipv6_tcp_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
237 {
238         struct sockaddr_in6 peer_addr;
239         socklen_t len = sizeof(peer_addr);
240         struct hostent *he;
241         int ret;
242
243         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
244         if (ret == -1) {
245                 return NULL;
246         }
247
248         he = gethostbyaddr((char *)&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
249         if (he == NULL) {
250                 return NULL;
251         }
252
253         return talloc_strdup(mem_ctx, he->h_name);
254 }
255
256 static char *ipv6_tcp_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
257 {
258         struct sockaddr_in6 peer_addr;
259         socklen_t len = sizeof(peer_addr);
260         int ret;
261         struct hostent *he;
262
263         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
264         if (ret == -1) {
265                 return NULL;
266         }
267
268         he = gethostbyaddr(&peer_addr.sin6_addr, sizeof(peer_addr.sin6_addr), AF_INET6);
269
270         if (!he || !he->h_name) {
271                 return NULL;
272         }
273         
274         return talloc_strdup(mem_ctx, he->h_name);
275 }
276
277 static int ipv6_tcp_get_peer_port(struct socket_context *sock)
278 {
279         struct sockaddr_in6 peer_addr;
280         socklen_t len = sizeof(peer_addr);
281         int ret;
282
283         ret = getpeername(sock->fd, (struct sockaddr *)&peer_addr, &len);
284         if (ret == -1) {
285                 return -1;
286         }
287
288         return ntohs(peer_addr.sin6_port);
289 }
290
291 static char *ipv6_tcp_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
292 {
293         struct sockaddr_in6 my_addr;
294         socklen_t len = sizeof(my_addr);
295         int ret;
296         struct hostent *he;
297
298         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
299         if (ret == -1) {
300                 return NULL;
301         }
302
303         he = gethostbyaddr((char *)&my_addr.sin6_addr, sizeof(my_addr.sin6_addr), AF_INET6);
304         if (he == NULL) {
305                 return NULL;
306         }
307
308         return talloc_strdup(mem_ctx, he->h_name);
309 }
310
311 static int ipv6_tcp_get_my_port(struct socket_context *sock)
312 {
313         struct sockaddr_in6 my_addr;
314         socklen_t len = sizeof(my_addr);
315         int ret;
316
317         ret = getsockname(sock->fd, (struct sockaddr *)&my_addr, &len);
318         if (ret == -1) {
319                 return -1;
320         }
321
322         return ntohs(my_addr.sin6_port);
323 }
324
325 static int ipv6_tcp_get_fd(struct socket_context *sock)
326 {
327         return sock->fd;
328 }
329
330 static const struct socket_ops ipv6_tcp_ops = {
331         .name                   = "ipv6",
332         .type                   = SOCKET_TYPE_STREAM,
333
334         .fn_init                = ipv6_tcp_init,
335         .fn_connect             = ipv6_tcp_connect,
336         .fn_listen              = ipv6_tcp_listen,
337         .fn_accept              = ipv6_tcp_accept,
338         .fn_recv                = ipv6_tcp_recv,
339         .fn_send                = ipv6_tcp_send,
340         .fn_close               = ipv6_tcp_close,
341
342         .fn_set_option          = ipv6_tcp_set_option,
343
344         .fn_get_peer_name       = ipv6_tcp_get_peer_name,
345         .fn_get_peer_addr       = ipv6_tcp_get_peer_addr,
346         .fn_get_peer_port       = ipv6_tcp_get_peer_port,
347         .fn_get_my_addr         = ipv6_tcp_get_my_addr,
348         .fn_get_my_port         = ipv6_tcp_get_my_port,
349
350         .fn_get_fd              = ipv6_tcp_get_fd
351 };
352
353 const struct socket_ops *socket_ipv6_ops(void)
354 {
355         return &ipv6_tcp_ops;
356 }
357
358 NTSTATUS socket_ipv6_init(void)
359 {
360         return NT_STATUS_OK;
361 }